ATLAS Offline Software
MadGraphUtils.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2022 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 from AthenaCommon import Logging
11 mglog = Logging.logging.getLogger('MadGraphUtils')
12 
13 # Name of python executable
14 python='python'
15 # Magic name of gridpack directory
16 MADGRAPH_GRIDPACK_LOCATION='madevent'
17 # Name for the run (since we only have 1, just needs consistency)
18 MADGRAPH_RUN_NAME='run_01'
19 # For error handling
20 MADGRAPH_CATCH_ERRORS=True
21 # PDF setting (global setting)
22 MADGRAPH_PDFSETTING=None
23 MADGRAPH_COMMAND_STACK = []
24 
25 patched_shutil_loc='/cvmfs/atlas.cern.ch/repo/sw/Generators/madgraph/models/latest/shutil_patch'
26 if 'PYTHONPATH' in os.environ and patched_shutil_loc not in os.environ['PYTHONPATH']:
27  # add shutil_patch in first place so that the patched version of shutil.py is picked up by MG instead of original version
28  # the patched version does not throw the errors that has made running MG and this code impossible on some file systems
29  os.environ['PYTHONPATH'] = patched_shutil_loc+':'+os.environ['PYTHONPATH']
30  MADGRAPH_COMMAND_STACK += ['export PYTHONPATH='+patched_shutil_loc+':${PYTHONPATH}']
31 # we need to remove shutil from modules before we can use our version
32 if 'shutil' in sys.modules:
33  sys.modules.pop('shutil')
34 # make sure this python instance uses fixed shutil
35 sys.path.insert(0,patched_shutil_loc)
36 import shutil
37 
38 from MadGraphControl.MadGraphUtilsHelpers import checkSettingExists,checkSetting,checkSettingIsTrue,getDictFromCard,get_runArgs_info,get_physics_short,is_version_or_newer
39 from MadGraphControl.MadGraphParamHelpers import do_PMG_updates,check_PMG_updates
40 
41 def stack_subprocess(command,**kwargs):
42  global MADGRAPH_COMMAND_STACK
43  MADGRAPH_COMMAND_STACK += [' '.join(command)]
44  return subprocess.Popen(command,**kwargs)
45 
46 
48  # Addition for models directory
49  global MADGRAPH_COMMAND_STACK
50  if 'PYTHONPATH' in os.environ:
51  if not any( [('Generators/madgraph/models' in x and 'shutil_patch' not in x) for x in os.environ['PYTHONPATH'].split(':') ]):
52  os.environ['PYTHONPATH'] += ':/cvmfs/atlas.cern.ch/repo/sw/Generators/madgraph/models/latest'
53  MADGRAPH_COMMAND_STACK += ['export PYTHONPATH=${PYTHONPATH}:/cvmfs/atlas.cern.ch/repo/sw/Generators/madgraph/models/latest']
54  # Make sure that gfortran doesn't write to somewhere it shouldn't
55  if 'GFORTRAN_TMPDIR' in os.environ:
56  return
57  if 'TMPDIR' in os.environ:
58  os.environ['GFORTRAN_TMPDIR']=os.environ['TMPDIR']
59  MADGRAPH_COMMAND_STACK += ['export GFORTRAN_TMPDIR=${TMPDIR}']
60  return
61  if 'TMP' in os.environ:
62  os.environ['GFORTRAN_TMPDIR']=os.environ['TMP']
63  MADGRAPH_COMMAND_STACK += ['export GFORTRAN_TMPDIR=${TMP}']
64  return
65 
66 
68  try:
69  from __main__ import opts
70  if opts.config_only:
71  mglog.info('Athena running on config only mode: not executing MadGraph')
72  return True
73  except ImportError:
74  pass
75  return False
76 
77 
78 def generate_prep(process_dir):
79  global MADGRAPH_COMMAND_STACK
80  if not os.access('Cards_bkup',os.R_OK):
81  shutil.copytree(process_dir+'/Cards','Cards_bkup')
82  shutil.copyfile(process_dir+'/Source/make_opts','Cards_bkup/make_opts_bkup')
83  MADGRAPH_COMMAND_STACK += ['# In case this fails, Cards_bkup should be in your original run directory']
84  MADGRAPH_COMMAND_STACK += ['# And ${MGaMC_PROCESS_DIR} can be replaced with whatever process directory exists in your stand-alone test']
85  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup/*dat ${MGaMC_PROCESS_DIR}/Cards/']
86  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup/make_opts_bkup ${MGaMC_PROCESS_DIR}/Source/make_opts']
87  else:
88  mglog.warning('Found Cards_bkup directory existing. Suggests you are either running generation twice (a little funny) or are not using a clean directory.')
89  bkup_v = 1
90  while os.access('Cards_bkup_'+str(bkup_v),os.R_OK) and bkup_v<100:
91  bkup_v += 1
92  if bkup_v<100:
93  shutil.copytree(process_dir+'/Cards','Cards_bkup_'+str(bkup_v))
94  shutil.copyfile(process_dir+'/Source/make_opts','Cards_bkup_'+str(bkup_v)+'/make_opts_bkup')
95  MADGRAPH_COMMAND_STACK += ['# In case this fails, Cards_bkup should be in your original run directory']
96  MADGRAPH_COMMAND_STACK += ['# And ${MGaMC_PROCESS_DIR} can be replaced with whatever process directory exists in your stand-alone test']
97  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup_'+str(bkup_v)+'/*dat ${MGaMC_PROCESS_DIR}/Cards/']
98  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup_'+str(bkup_v)+'/make_opts_bkup ${MGaMC_PROCESS_DIR}/Source/make_opts']
99  else:
100  mglog.warning('Way too many Cards_bkup* directories found. Giving up -- standalone script may not work.')
101 
102 
103 def error_check(errors_a, return_code):
104  global MADGRAPH_CATCH_ERRORS
105  if not MADGRAPH_CATCH_ERRORS:
106  return
107  unmasked_error = False
108  my_debug_file = None
109  bad_variables = []
110  # Make sure we are getting a string and not a byte string (python3 ftw)
111  errors = errors_a
112  if type(errors)==bytes:
113  errors = errors.decode('utf-8')
114  if len(errors):
115  mglog.info('Some errors detected by MadGraphControl - checking for serious errors')
116  for err in errors.split('\n'):
117  if len(err.strip())==0:
118  continue
119  # Errors to do with I/O... not clear on their origin yet
120  if 'Inappropriate ioctl for device' in err:
121  mglog.info(err)
122  continue
123  if 'stty: standard input: Invalid argument' in err:
124  mglog.info(err)
125  continue
126  # Errors for PDF sets that should be fixed in MG5_aMC 2.7
127  if 'PDF already installed' in err:
128  mglog.info(err)
129  continue
130  if 'Read-only file system' in err:
131  mglog.info(err)
132  continue
133  if 'HTML' in err:
134  # https://bugs.launchpad.net/mg5amcnlo/+bug/1870217
135  mglog.info(err)
136  continue
137  if 'impossible to set default multiparticles' in err:
138  # https://answers.launchpad.net/mg5amcnlo/+question/690004
139  mglog.info(err)
140  continue
141  if 'More information is found in' in err:
142  my_debug_file = err.split("'")[1]
143  if err.startswith('tar'):
144  mglog.info(err)
145  continue
146  if 'python2 support will be removed' in err:
147  mglog.info(err)
148  continue
149  # silly ghostscript issue in 21.6.46 nightly
150  if 'required by /lib64/libfontconfig.so' in err or\
151  'required by /lib64/libgs.so' in err:
152  mglog.info(err)
153  continue
154  if 'Error: Symbol' in err and 'has no IMPLICIT type' in err:
155  bad_variables += [ err.split('Symbol ')[1].split(' at ')[0] ]
156  # error output from tqdm (progress bar)
157  if 'it/s' in err:
158  mglog.info(err)
159  continue
160  mglog.error(err)
161  unmasked_error = True
162  # This is a bit clunky, but needed because we could be several places when we get here
163  if my_debug_file is None:
164  debug_files = glob.glob('*debug.log')+glob.glob('*/*debug.log')
165  for debug_file in debug_files:
166  # This protects against somebody piping their output to my_debug.log and it being caught here
167  has_subproc = os.access(os.path.join(os.path.dirname(debug_file),'SubProcesses'),os.R_OK)
168  if has_subproc:
169  my_debug_file = debug_file
170  break
171 
172  if my_debug_file is not None:
173  if not unmasked_error:
174  mglog.warning('Found a debug file at '+my_debug_file+' but no apparent error. Will terminate.')
175  mglog.error('MadGraph5_aMC@NLO appears to have crashed. Debug file output follows.')
176  with open(my_debug_file,'r') as error_output:
177  for l in error_output:
178  mglog.error(l.replace('\n',''))
179  mglog.error('End of debug file output')
180 
181  if bad_variables:
182  mglog.warning('Appeared to detect variables in your run card that MadGraph did not understand:')
183  mglog.warning(' Check your run card / JO settings for %s',bad_variables)
184 
185  # Check the return code
186  if return_code!=0:
187  mglog.error(f'Detected a bad return code: {return_code}')
188  unmasked_error = True
189 
190  # Now raise an error if we were in either of the error states
191  if unmasked_error or my_debug_file is not None:
193  raise RuntimeError('Error detected in MadGraphControl process')
194  return
195 
196 
197 # Write a short test script for standalone debugging
199  mglog.info('Will write a stand-alone debugging script.')
200  mglog.info('This is an attempt to provide you commands that you can use')
201  mglog.info('to reproduce the error locally. If you make additional')
202  mglog.info('modifications by hand (not using MadGraphControl) in your JO,')
203  mglog.info('make sure that you check and modify the script as needed.\n\n')
204  global MADGRAPH_COMMAND_STACK
205  mglog.info('# Script start; trim off columns left of the "#"')
206  # Write offline stand-alone reproduction script
207  with open('standalone_script.sh','w') as standalone_script:
208  for command in MADGRAPH_COMMAND_STACK:
209  for line in command.split('\n'):
210  mglog.info(line)
211  standalone_script.write(line+'\n')
212  mglog.info('# Script end')
213  mglog.info('Script also written to %s/standalone_script.sh',os.getcwd())
214 
215 
216 def new_process(process='generate p p > t t~\noutput -f', plugin=None, keepJpegs=False, usePMGSettings=False):
217  """ Generate a new process in madgraph.
218  Pass a process string.
219  Optionally request JPEGs to be kept and request for PMG settings to be used in the param card
220  Return the name of the process directory.
221  """
222  if config_only_check():
223  # Give some directories to work on
224  try:
225  os.makedirs('dummy_proc/Cards')
226  except os.error:
227  pass
228  return 'dummy_proc'
229 
230  # Don't run if generating events from gridpack
232  return MADGRAPH_GRIDPACK_LOCATION
233 
234  # Actually just sent the process card contents - let's make a card
235  card_loc='proc_card_mg5.dat'
236  mglog.info('Writing process card to '+card_loc)
237  a_card = open( card_loc , 'w' )
238  for l in process.split('\n'):
239  if 'output' not in l:
240  a_card.write(l+'\n')
241  elif '-nojpeg' in l or keepJpegs:
242  a_card.write(l+'\n')
243  elif '#' in l:
244  a_card.write(l.split('#')[0]+' -nojpeg #'+l.split('#')[1]+'\n')
245  else:
246  a_card.write(l+' -nojpeg\n')
247  a_card.close()
248 
249  madpath=os.environ['MADPATH']
250  # Just in case
252 
253  # Check if we have a special output directory
254  process_dir = ''
255  for l in process.split('\n'):
256  # Look for an output line
257  if 'output' not in l.split('#')[0].split():
258  continue
259  # Check how many things before the options start
260  tmplist = l.split('#')[0].split(' -')[0]
261  # if two things, second is the directory
262  if len(tmplist.split())==2:
263  process_dir = tmplist.split()[1]
264  # if three things, third is the directory (second is the format)
265  elif len(tmplist.split())==3:
266  process_dir = tmplist.split()[2]
267  # See if we got a directory
268  if ''!=process_dir:
269  mglog.info('Saw that you asked for a special output directory: '+str(process_dir))
270  break
271 
272  mglog.info('Started process generation at '+str(time.asctime()))
273 
274  plugin_cmd = '--mode='+plugin if plugin is not None else ''
275 
276  # Note special handling here to explicitly print the process
277  global MADGRAPH_COMMAND_STACK
278  MADGRAPH_COMMAND_STACK += ['# All jobs should start in a clean directory']
279  MADGRAPH_COMMAND_STACK += ['mkdir standalone_test; cd standalone_test']
280  MADGRAPH_COMMAND_STACK += [' '.join([python,madpath+'/bin/mg5_aMC '+plugin_cmd+' << EOF\n'+process+'\nEOF\n'])]
281  global MADGRAPH_CATCH_ERRORS
282  generate = subprocess.Popen([python,madpath+'/bin/mg5_aMC',plugin_cmd,card_loc],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
283  (out,err) = generate.communicate()
284  error_check(err,generate.returncode)
285 
286  mglog.info('Finished process generation at '+str(time.asctime()))
287 
288  # at this point process_dir is for sure defined - it's equal to '' in the worst case
289  if process_dir == '': # no user-defined value, need to find the directory created by MadGraph5
290  for adir in sorted(glob.glob( os.getcwd()+'/*PROC*' ),reverse=True):
291  if os.access('%s/SubProcesses/subproc.mg'%adir,os.R_OK):
292  if process_dir=='':
293  process_dir=adir
294  else:
295  mglog.warning('Additional possible process directory, '+adir+' found. Had '+process_dir)
296  mglog.warning('Likely this is because you did not run from a clean directory, and this may cause errors later.')
297  else: # user-defined directory
298  if not os.access('%s/SubProcesses/subproc.mg'%process_dir,os.R_OK):
299  raise RuntimeError('No diagrams for this process in user-define dir='+str(process_dir))
300  if process_dir=='':
301  raise RuntimeError('No diagrams for this process from list: '+str(sorted(glob.glob(os.getcwd()+'/*PROC*'),reverse=True)))
302 
303  # Special catch related to path setting and using afs
304  needed_options = ['ninja','collier','fastjet','lhapdf','syscalc_path']
305  in_config = open(os.environ['MADPATH']+'/input/mg5_configuration.txt','r')
306  option_paths = {}
307  for l in in_config.readlines():
308  for o in needed_options:
309  if o+' =' in l.split('#')[0] and 'MCGenerators' in l.split('#')[0]:
310  old_path = l.split('#')[0].split('=')[1].strip().split('MCGenerators')[1]
311  old_path = old_path[ old_path.find('/') : ]
312  if o =='lhapdf' and 'LHAPATH' in os.environ:
313  # Patch for LHAPDF version
314  version = os.environ['LHAPATH'].split('lhapdf/')[1].split('/')[0]
315  old_version = old_path.split('lhapdf/')[1].split('/')[0]
316  old_path = old_path.replace(old_version,version)
317  if o=='ninja':
318  # Patch for stupid naming problem
319  old_path.replace('gosam_contrib','gosam-contrib')
320  option_paths[o] = os.environ['MADPATH'].split('madgraph5amc')[0]+old_path
321  # Check to see if the option has been commented out
322  if o+' =' in l and o+' =' not in l.split('#')[0]:
323  mglog.info('Option '+o+' appears commented out in the config file')
324 
325  in_config.close()
326  for o in needed_options:
327  if o not in option_paths:
328  mglog.warning('Path for option '+o+' not found in original config')
329 
330  mglog.info('Modifying config paths to avoid use of afs:')
331  mglog.info(option_paths)
332 
333  # Set the paths appropriately
334  modify_config_card(process_dir=process_dir,settings=option_paths,set_commented=False)
335  # Done modifying paths
336 
337  # If requested, apply PMG default settings
338  if usePMGSettings:
339  do_PMG_updates(process_dir)
340 
341  # After 2.9.3, enforce the standard default sde_strategy, so that this won't randomly change on the user
342  if is_version_or_newer([2,9,3]) and not is_NLO_run(process_dir=process_dir):
343  mglog.info('Setting default sde_strategy to old default (1)')
344  my_settings = {'sde_strategy':1}
345  modify_run_card(process_dir=process_dir,settings=my_settings,skipBaseFragment=True)
346 
347  # Make sure we store the resultant directory
348  MADGRAPH_COMMAND_STACK += ['export MGaMC_PROCESS_DIR='+os.path.basename(process_dir)]
349 
350  return process_dir
351 
352 
353 def get_default_runcard(process_dir=MADGRAPH_GRIDPACK_LOCATION):
354  """ Copy the default runcard from one of several locations
355  to a local file with name run_card.tmp.dat"""
356  output_name = 'run_card.tmp.dat'
357  if config_only_check():
358  mglog.info('Athena running on config only mode: grabbing run card the old way, as there will be no proc dir')
359  mglog.info('Fetching default LO run_card.dat')
360  if os.access(os.environ['MADPATH']+'/Template/LO/Cards/run_card.dat',os.R_OK):
361  shutil.copy(os.environ['MADPATH']+'/Template/LO/Cards/run_card.dat',output_name)
362  return 'run_card.dat'
363  elif os.access(os.environ['MADPATH']+'/Template/Cards/run_card.dat',os.R_OK):
364  shutil.copy(os.environ['MADPATH']+'/Template/Cards/run_card.dat',output_name)
365  return output_name
366  else:
367  raise RuntimeError('Cannot find default LO run_card.dat!')
368 
369  # Get the run card from the installation
370  run_card=process_dir+'/Cards/run_card.dat'
371  if os.access(run_card,os.R_OK):
372  mglog.info('Copying default run_card.dat from '+str(run_card))
373  shutil.copy(run_card,output_name)
374  return output_name
375  else:
376  run_card=process_dir+'/Cards/run_card_default.dat'
377  mglog.info('Fetching default run_card.dat from '+str(run_card))
378  if os.access(run_card,os.R_OK):
379  shutil.copy(run_card,output_name)
380  return output_name
381  else:
382  raise RuntimeError('Cannot find default run_card.dat or run_card_default.dat! I was looking here: %s'%run_card)
383 
384 
385 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):
386  if config_only_check():
387  return
388 
389  # Just in case
391 
392  # Set consistent mode and number of jobs
393  mode = 0
394  njobs = 1
395  if 'ATHENA_PROC_NUMBER' in os.environ and int(os.environ['ATHENA_PROC_NUMBER'])>0:
396  njobs = int(os.environ['ATHENA_PROC_NUMBER'])
397  mglog.info('Lucky you - you are running on a full node queue. Will re-configure for '+str(njobs)+' jobs.')
398  mode = 2
399 
400  cluster_type = get_cluster_type(process_dir=process_dir)
401  if cluster_type is not None:
402  mode = 1
403 
405  mglog.info('Running event generation from gridpack (using smarter mode from generate() function)')
406  generate_from_gridpack(runArgs=runArgs,extlhapath=extlhapath,gridpack_compile=gridpack_compile,requirePMGSettings=requirePMGSettings)
407  return
408  else:
409  mglog.info('Did not identify an input gridpack.')
410  if grid_pack:
411  mglog.info('The grid_pack flag is set, so I am expecting to create a gridpack in this job')
412 
413  # Now get a variety of info out of the runArgs
414  beamEnergy,random_seed = get_runArgs_info(runArgs)
415 
416  # Check if process is NLO or LO
417  isNLO=is_NLO_run(process_dir=process_dir)
418 
419  # temporary fix of makefile, needed for 3.3.1., remove in future
420  if isNLO:
421  fix_fks_makefile(process_dir=process_dir)
422 
423  # if f2py not available
424  if get_reweight_card(process_dir=process_dir) is not None:
425  from distutils.spawn import find_executable
426  if find_executable('f2py') is not None:
427  mglog.info('Found f2py, will use it for reweighting')
428  else:
429  raise RuntimeError('Could not find f2py, needed for reweighting')
430  check_reweight_card(process_dir)
431 
432  if grid_pack:
433  #Running in gridpack mode
434  mglog.info('Started generating gridpack at '+str(time.asctime()))
435  mglog.warning(' >>>>>> THIS KIND OF JOB SHOULD ONLY BE RUN LOCALLY - NOT IN GRID JOBS <<<<<<')
436 
437  # Some events required if we specify MadSpin usage!
438  my_settings = {'nevents':'1000'}
439  if isNLO:
440  my_settings['req_acc']=str(required_accuracy)
441  else:
442  my_settings = {'gridpack':'true'}
443  modify_run_card(process_dir=process_dir,settings=my_settings,skipBaseFragment=True)
444 
445  else:
446  #Running in on-the-fly mode
447  mglog.info('Started generating at '+str(time.asctime()))
448 
449  mglog.info('Run '+MADGRAPH_RUN_NAME+' will be performed in mode '+str(mode)+' with '+str(njobs)+' jobs in parallel.')
450 
451  # Ensure that things are set up normally
452  if not os.access(process_dir,os.R_OK):
453  raise RuntimeError('No process directory found at '+process_dir)
454  if not os.access(process_dir+'/bin/generate_events',os.R_OK):
455  raise RuntimeError('No generate_events module found in '+process_dir)
456 
457  mglog.info('For your information, the libraries available are (should include LHAPDF):')
458  ls_dir(process_dir+'/lib')
459 
460  setupFastjet(process_dir=process_dir)
461  if bias_module is not None:
462  setup_bias_module(bias_module,process_dir)
463 
464  mglog.info('Now I will hack the make files a bit. Apologies, but there seems to be no good way around this.')
465  shutil.copyfile(process_dir+'/Source/make_opts',process_dir+'/Source/make_opts_old')
466  old_opts = open(process_dir+'/Source/make_opts_old','r')
467  new_opts = open(process_dir+'/Source/make_opts','w')
468  for aline in old_opts:
469  if 'FC=g' in aline:
470  mglog.info('Configuring the fancy gfortran compiler instead of g77 / f77')
471  new_opts.write(' FC=gfortran\n')
472  elif 'FFLAGS+= -ffixed-line-length-132' in aline and 'i686' in os.environ['CMTCONFIG']:
473  mglog.info('Setting you up for a 32-bit compilation')
474  new_opts.write('FFLAGS+= -ffixed-line-length-132 -m32\n')
475  else:
476  new_opts.write(aline)
477  old_opts.close()
478  new_opts.close()
479  mglog.info('Make file hacking complete.')
480 
481  # Change directories
482  currdir=os.getcwd()
483  os.chdir(process_dir)
484  # Record the change
485  global MADGRAPH_COMMAND_STACK
486  MADGRAPH_COMMAND_STACK += [ 'cd ${MGaMC_PROCESS_DIR}' ]
487 
488 
489  # Check the run card
490  run_card_consistency_check(isNLO=isNLO)
491 
492  # Since the consistency check can update some settings, print the cards now
493  print_cards_from_dir(process_dir=os.getcwd())
494 
495  # Check the param card
496  code = check_PMG_updates(process_dir=os.getcwd())
497  if requirePMGSettings and code!=0:
498  raise RuntimeError('Settings are not compliant with PMG defaults! Please use do_PMG_updates function to get PMG default params.')
499 
500  # Build up the generate command
501  # Use the new-style way of passing things: just --name, everything else in config
502  command = [python,'bin/generate_events']
503  if isNLO:
504  command += ['--name='+MADGRAPH_RUN_NAME]
505  mglog.info('Removing Cards/shower_card.dat to ensure we get parton level events only')
506  os.unlink('Cards/shower_card.dat')
507  else:
508  command += [MADGRAPH_RUN_NAME]
509  # Set the number of cores to be used
510  setNCores(process_dir=os.getcwd(), Ncores=njobs)
511  # Special handling for mode 1
512  if mode==1:
513  mglog.info('Setting up cluster running')
514  modify_config_card(process_dir=os.getcwd(),settings={'run_mode':1})
515  if cluster_type=='pbs':
516  mglog.info('Modifying bin/internal/cluster.py for PBS cluster running')
517  os.system("sed -i \"s:text += prog:text += './'+prog:g\" bin/internal/cluster.py")
518  elif mode==2:
519  mglog.info('Setting up multi-core running on '+os.environ['ATHENA_PROC_NUMBER']+' cores')
520  elif mode==0:
521  mglog.info('Setting up serial generation.')
522 
523  generate_prep(process_dir=os.getcwd())
524  global MADGRAPH_CATCH_ERRORS
525  generate = stack_subprocess(command,stdin=subprocess.PIPE, stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
526  (out,err) = generate.communicate()
527  error_check(err,generate.returncode)
528 
529  # Get back to where we came from
530  os.chdir(currdir)
531  MADGRAPH_COMMAND_STACK += [ 'cd -' ]
532 
533  if grid_pack:
534  # Name dictacted by https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/PmgMcSoftware
535  energy = '%1.1f'%(beamEnergy*2./1000.)
536  energy = energy.replace('.0','').replace('.','p')
537  gridpack_name='mc_'+energy+'TeV.'+get_physics_short()+'.GRID.tar.gz'
538  mglog.info('Tidying up gridpack '+gridpack_name)
539 
540  if not isNLO:
541 
542  MADGRAPH_COMMAND_STACK += ['cp '+glob.glob(process_dir+'/'+MADGRAPH_RUN_NAME+'_*gridpack.tar.gz')[0]+' '+gridpack_name]
543  shutil.copy(glob.glob(process_dir+'/'+MADGRAPH_RUN_NAME+'_*gridpack.tar.gz')[0],gridpack_name)
544 
545  if gridpack_compile:
546  MADGRAPH_COMMAND_STACK += ['mkdir tmp%i/'%os.getpid(),'cd tmp%i/'%os.getpid()]
547  os.mkdir('tmp%i/'%os.getpid())
548  os.chdir('tmp%i/'%os.getpid())
549  mglog.info('untar gridpack')
550  untar = stack_subprocess(['tar','xvzf',('../'+gridpack_name)])
551  untar.wait()
552  mglog.info('compile and clean up')
553  MADGRAPH_COMMAND_STACK += ['cd madevent']
554  os.chdir('madevent/')
555  compilep = stack_subprocess(['./bin/compile'],stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
556  (out,err) = compilep.communicate()
557  error_check(err,compilep.returncode)
558  clean = stack_subprocess(['./bin/clean4grid'],stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
559  (out,err) = clean.communicate()
560  error_check(err,clean.returncode)
561  clean.wait()
562  MADGRAPH_COMMAND_STACK += ['cd ..','rm ../'+gridpack_name]
563  os.chdir('../')
564  mglog.info('remove old tarball')
565  os.unlink('../'+gridpack_name)
566  mglog.info('Package up new tarball')
567  tar = stack_subprocess(['tar','cvzf','../'+gridpack_name,'--exclude=SubProcesses/P*/G*/*_results.dat','--exclude=SubProcesses/P*/G*/*.log','--exclude=SubProcesses/P*/G*/*.txt','.'])
568  tar.wait()
569  MADGRAPH_COMMAND_STACK += ['cd ..','rm -r tmp%i/'%os.getpid()]
570  os.chdir('../')
571  mglog.info('Remove temporary directory')
572  shutil.rmtree('tmp%i/'%os.getpid())
573  mglog.info('Tidying up complete!')
574 
575  else:
576 
577 
578  mglog.info('Package up process_dir')
579  MADGRAPH_COMMAND_STACK += ['mv '+process_dir+' '+MADGRAPH_GRIDPACK_LOCATION]
580  os.rename(process_dir,MADGRAPH_GRIDPACK_LOCATION)
581  tar = stack_subprocess(['tar','czf',gridpack_name,MADGRAPH_GRIDPACK_LOCATION,'--exclude=Events/*/*events*gz','--exclude=SubProcesses/P*/G*/log*txt','--exclude=SubProcesses/P*/G*/events.lhe*','--exclude=*/*.o','--exclude=*/*/*.o','--exclude=*/*/*/*.o','--exclude=*/*/*/*/*.o'])
582  tar.wait()
583  MADGRAPH_COMMAND_STACK += ['mv '+MADGRAPH_GRIDPACK_LOCATION+' '+process_dir]
584  os.rename(MADGRAPH_GRIDPACK_LOCATION,process_dir)
585 
586  mglog.info('Gridpack sucessfully created, exiting the transform')
587  if hasattr(runArgs,'outputTXTFile'):
588  mglog.info('Touching output TXT (LHE) file for the transform')
589  open(runArgs.outputTXTFile, 'w').close()
590  from AthenaCommon.AppMgr import theApp
591  theApp.finalize()
592  theApp.exit()
593 
594  mglog.info('Finished at '+str(time.asctime()))
595  return 0
596 
597 
598 def generate_from_gridpack(runArgs=None, extlhapath=None, gridpack_compile=None, requirePMGSettings=False):
599 
600  # Get of info out of the runArgs
601  beamEnergy,random_seed = get_runArgs_info(runArgs)
602 
603  # Just in case
605 
606  isNLO=is_NLO_run(process_dir=MADGRAPH_GRIDPACK_LOCATION)
607 
608  setupFastjet(process_dir=MADGRAPH_GRIDPACK_LOCATION)
609 
610  # This is hard-coded as a part of MG5_aMC :'(
611  gridpack_run_name = 'GridRun_'+str(random_seed)
612 
613  # Ensure that we only do madspin at the end
614  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/Cards/madspin_card.dat',os.R_OK):
615  os.rename(MADGRAPH_GRIDPACK_LOCATION+'/Cards/madspin_card.dat',MADGRAPH_GRIDPACK_LOCATION+'/Cards/backup_madspin_card.dat')
616  do_madspin=True
617  else:
618  do_madspin=False
619 
620  if get_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION) is not None:
621  check_reweight_card(MADGRAPH_GRIDPACK_LOCATION)
622 
623  # Check the param card
624  code = check_PMG_updates(process_dir=MADGRAPH_GRIDPACK_LOCATION)
625  if requirePMGSettings and code!=0:
626  raise RuntimeError('Settings are not compliant with PMG defaults! Please use do_PMG_updates function to get PMG default params.')
627 
628  # Modify run card, then print
629  settings={'iseed':str(random_seed)}
630  if not isNLO:
631  settings['python_seed']=str(random_seed)
632  modify_run_card(process_dir=MADGRAPH_GRIDPACK_LOCATION,settings=settings,skipBaseFragment=True)
633 
634  mglog.info('Generating events from gridpack')
635 
636  # Ensure that things are set up normally
637  if not os.path.exists(MADGRAPH_GRIDPACK_LOCATION):
638  raise RuntimeError('Gridpack directory not found at '+MADGRAPH_GRIDPACK_LOCATION)
639 
640  nevents = getDictFromCard(MADGRAPH_GRIDPACK_LOCATION+'/Cards/run_card.dat')['nevents']
641  mglog.info('>>>> FOUND GRIDPACK <<<< <- This will be used for generation')
642  mglog.info('Generation of '+str(int(nevents))+' events will be performed using the supplied gridpack with random seed '+str(random_seed))
643  mglog.info('Started generating events at '+str(time.asctime()))
644 
645  #Remove addmasses if it's there
646  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/bin/internal/addmasses.py',os.R_OK):
647  os.remove(MADGRAPH_GRIDPACK_LOCATION+'/bin/internal/addmasses.py')
648 
649  currdir=os.getcwd()
650 
651  # Make sure we've set the number of processes appropriately
652  setNCores(process_dir=MADGRAPH_GRIDPACK_LOCATION)
653  global MADGRAPH_CATCH_ERRORS
654 
655  # Run the consistency check, print some useful info
656  ls_dir(currdir)
657  ls_dir(MADGRAPH_GRIDPACK_LOCATION)
658 
659  # Update the run card according to consistency checks
660  run_card_consistency_check(isNLO=isNLO,process_dir=MADGRAPH_GRIDPACK_LOCATION)
661 
662  # Now all done with updates, so print the cards with the final settings
663  print_cards_from_dir(process_dir=MADGRAPH_GRIDPACK_LOCATION)
664 
665  if isNLO:
666  #turn off systematics for gridpack generation and store settings for standalone run
667  run_card_dict=getDictFromCard(MADGRAPH_GRIDPACK_LOCATION+'/Cards/run_card.dat')
668  systematics_settings=None
669  if checkSetting('systematics_program','systematics',run_card_dict):
670  if not checkSettingIsTrue('store_rwgt_info',run_card_dict):
671  raise RuntimeError('Trying to run NLO systematics but reweight info not stored')
672  if checkSettingExists('systematics_arguments',run_card_dict):
673  systematics_settings=MadGraphControl.MadGraphSystematicsUtils.parse_systematics_arguments(run_card_dict['systematics_arguments'])
674  else:
675  systematics_settings={}
676  mglog.info('Turning off systematics for now, running standalone later')
677  modify_run_card(process_dir=MADGRAPH_GRIDPACK_LOCATION,settings={'systematics_program':'none'},skipBaseFragment=True)
678 
679  global MADGRAPH_COMMAND_STACK
680  if not isNLO:
681 
682  if not os.access(MADGRAPH_GRIDPACK_LOCATION+'/bin/gridrun',os.R_OK):
683  mglog.error('/bin/gridrun not found at '+MADGRAPH_GRIDPACK_LOCATION)
684  raise RuntimeError('Could not find gridrun executable')
685  else:
686  mglog.info('Found '+MADGRAPH_GRIDPACK_LOCATION+'/bin/gridrun, starting generation.')
687  generate_prep(MADGRAPH_GRIDPACK_LOCATION)
688  granularity=1
689  mglog.info("Now generating {} events with random seed {} and granularity {}".format(int(nevents),int(random_seed),granularity))
690  # not sure whether this is needed but it is done in the old "run.sh" script
691  new_ld_path=":".join([os.environ['LD_LIBRARY_PATH'],os.getcwd()+'/'+MADGRAPH_GRIDPACK_LOCATION+'/madevent/lib',os.getcwd()+'/'+MADGRAPH_GRIDPACK_LOCATION+'/HELAS/lib'])
692  os.environ['LD_LIBRARY_PATH']=new_ld_path
693  MADGRAPH_COMMAND_STACK+=["export LD_LIBRARY_PATH="+":".join(['${LD_LIBRARY_PATH}',new_ld_path])]
694  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)
695  (out,err) = generate.communicate()
696  error_check(err,generate.returncode)
697  gp_events=MADGRAPH_GRIDPACK_LOCATION+"/Events/GridRun_{}/unweighted_events.lhe.gz".format(int(random_seed))
698  if not os.path.exists(gp_events):
699  mglog.error('Error in gp generation, did not find events at '+gp_events)
700 
701  # add reweighting, which is not run automatically from LO GPs
702  reweight_card=get_reweight_card(MADGRAPH_GRIDPACK_LOCATION)
703  if reweight_card is not None:
704  pythonpath_backup=os.environ['PYTHONPATH']
705  # workaround as madevent crashes when path to mg in PYTHONPATH
706  os.environ['PYTHONPATH']=':'.join([p for p in pythonpath_backup.split(':') if 'madgraph5amc' not in p])
707  add_reweighting('GridRun_{}'.format(int(random_seed)))
708  os.environ['PYTHONPATH']=pythonpath_backup
709 
710  shutil.move(gp_events,'events.lhe.gz')
711 
712  else:
713 
714  if not os.access(MADGRAPH_GRIDPACK_LOCATION+'/bin/generate_events',os.R_OK):
715  raise RuntimeError('Could not find generate_events executable at '+MADGRAPH_GRIDPACK_LOCATION)
716  else:
717  mglog.info('Found '+MADGRAPH_GRIDPACK_LOCATION+'/bin/generate_events, starting generation.')
718 
719  ls_dir(MADGRAPH_GRIDPACK_LOCATION+'/Events/')
720  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name, os.F_OK):
721  mglog.info('Removing '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+' directory from gridpack generation')
722  MADGRAPH_COMMAND_STACK += ['rm -rf '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name]
723  shutil.rmtree(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name)
724 
725  # Delete events generated when setting up MadSpin during gridpack generation
726  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1', os.F_OK):
727  mglog.info('Removing '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1 directory from gridpack generation')
728  MADGRAPH_COMMAND_STACK += ['rm -rf '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1']
729  shutil.rmtree(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1')
730 
731  ls_dir(MADGRAPH_GRIDPACK_LOCATION+'/Events/')
732 
733  if not gridpack_compile:
734  mglog.info('Copying make_opts from Template')
735  shutil.copy(os.environ['MADPATH']+'/Template/LO/Source/make_opts',MADGRAPH_GRIDPACK_LOCATION+'/Source/')
736 
737  generate_prep(MADGRAPH_GRIDPACK_LOCATION)
738  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)
739  (out,err) = generate.communicate()
740  error_check(err,generate.returncode)
741  else:
742  mglog.info('Allowing recompilation of gridpack')
743  if os.path.islink(MADGRAPH_GRIDPACK_LOCATION+'/lib/libLHAPDF.a'):
744  mglog.info('Unlinking '+MADGRAPH_GRIDPACK_LOCATION+'/lib/libLHAPDF.a')
745  os.unlink(MADGRAPH_GRIDPACK_LOCATION+'/lib/libLHAPDF.a')
746 
747  generate_prep(MADGRAPH_GRIDPACK_LOCATION)
748  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)
749  (out,err) = generate.communicate()
750  error_check(err,generate.returncode)
751  if isNLO and systematics_settings is not None:
752  # run systematics
753  mglog.info('Running systematics standalone')
754  systematics_path=MADGRAPH_GRIDPACK_LOCATION+'/bin/internal/systematics.py'
755  events_location=MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/events.lhe.gz'
756  syst_cmd=[python,systematics_path]+[events_location]*2+["--"+k+"="+systematics_settings[k] for k in systematics_settings]
757  mglog.info('running: '+' '.join(syst_cmd))
758  systematics = stack_subprocess(syst_cmd)
759  systematics.wait()
760 
761 
762  # See if MG5 did the job for us already
763  if not os.access('events.lhe.gz',os.R_OK):
764  mglog.info('Copying generated events to '+currdir)
765  if not os.path.exists(MADGRAPH_GRIDPACK_LOCATION+'Events/'+gridpack_run_name):
766  shutil.copy(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/events.lhe.gz','events.lhe.gz')
767  else:
768  mglog.info('Events were already in place')
769 
770  ls_dir(currdir)
771 
772  mglog.info('Moving generated events to be in correct format for arrange_output().')
773  mglog.info('Unzipping generated events.')
774  unzip = stack_subprocess(['gunzip','-f','events.lhe.gz'])
775  unzip.wait()
776 
777  mglog.info('Moving file over to '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe')
778  mkdir = stack_subprocess(['mkdir','-p',(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name)])
779  mkdir.wait()
780  shutil.move('events.lhe',MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe')
781 
782  mglog.info('Re-zipping into dataset name '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe.gz')
783  rezip = stack_subprocess(['gzip',MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe'])
784  rezip.wait()
785 
786  os.chdir(currdir)
787 
788  # Now consider MadSpin:
789  if do_madspin:
790  # Move card back
791  os.rename(MADGRAPH_GRIDPACK_LOCATION+'/Cards/backup_madspin_card.dat',MADGRAPH_GRIDPACK_LOCATION+'/Cards/madspin_card.dat')
792  mglog.info('Decaying with MadSpin.')
793  add_madspin(process_dir=MADGRAPH_GRIDPACK_LOCATION)
794 
795  mglog.info('Finished at '+str(time.asctime()))
796 
797  return 0
798 
799 
800 def setupFastjet(process_dir=None):
801 
802  isNLO=is_NLO_run(process_dir=process_dir)
803 
804  mglog.info('Path to fastjet install dir: '+os.environ['FASTJETPATH'])
805  fastjetconfig = os.environ['FASTJETPATH']+'/bin/fastjet-config'
806 
807  mglog.info('fastjet-config --version: '+str(subprocess.Popen([fastjetconfig, '--version'],stdout = subprocess.PIPE).stdout.read().strip()))
808  mglog.info('fastjet-config --prefix: '+str(subprocess.Popen([fastjetconfig, '--prefix'],stdout = subprocess.PIPE).stdout.read().strip()))
809 
810  if not isNLO:
811  config_card=process_dir+'/Cards/me5_configuration.txt'
812  else:
813  config_card=process_dir+'/Cards/amcatnlo_configuration.txt'
814 
815  oldcard = open(config_card,'r')
816  newcard = open(config_card+'.tmp','w')
817 
818  for line in oldcard:
819  if 'fastjet = ' in line:
820  newcard.write('fastjet = '+fastjetconfig+'\n')
821  mglog.info('Setting fastjet = '+fastjetconfig+' in '+config_card)
822  else:
823  newcard.write(line)
824  oldcard.close()
825  newcard.close()
826  shutil.move(config_card+'.tmp',config_card)
827 
828  return
829 
830 
832  return get_LHAPDF_PATHS()[1]
833 
834 
836  LHADATAPATH=None
837  LHAPATH=None
838  for p in os.environ['LHAPATH'].split(':')+os.environ['LHAPDF_DATA_PATH'].split(':'):
839  if os.path.exists(p+"/../../lib/") and LHAPATH is None:
840  LHAPATH=p
841  for p in os.environ['LHAPDF_DATA_PATH'].split(':')+os.environ['LHAPATH'].split(':'):
842  if os.path.exists(p) and LHADATAPATH is None and p!=LHAPATH:
843  LHADATAPATH=p
844  if LHADATAPATH is None:
845  LHADATAPATH=LHAPATH
846  if LHAPATH is None:
847  mglog.error('Could not find path to LHAPDF installation')
848  return LHAPATH,LHADATAPATH
849 
850 
851 # function to get lhapdf id and name from either id or name
853  pdfname=''
854  pdfid=-999
855  LHADATAPATH=get_LHAPDF_DATA_PATH()
856  pdflist = open(LHADATAPATH+'/pdfsets.index','r')
857  if isinstance(pdf,int) or pdf.isdigit():
858  pdf=int(pdf)
859  pdfid=pdf
860  for line in pdflist:
861  splitline=line.split()
862  if int(splitline[0]) == pdfid:
863  pdfname=splitline[1]
864  break
865  else:
866  pdfname=pdf
867  for line in pdflist:
868  splitline=line.split()
869  if splitline[1] == pdfname:
870  pdfid=int(splitline[0])
871  break
872  pdflist.close()
873 
874  if pdfname=='':
875  err='Couldn\'t find PDF name associated to ID %i in %s.'%(pdfid,LHADATAPATH+'/pdfsets.index')
876  mglog.error(err)
877  raise RuntimeError(err)
878  if pdfid<0:
879  err='Couldn\'t find PDF ID associated to name %s in %s.'%(pdfname,LHADATAPATH+'/pdfsets.index')
880  mglog.error(err)
881  raise RuntimeError(err)
882 
883  return pdfid,pdfname
884 
885 
886 def setupLHAPDF(process_dir=None, extlhapath=None, allow_links=True):
887 
888  isNLO=is_NLO_run(process_dir=process_dir)
889 
890  origLHAPATH=os.environ['LHAPATH']
891  origLHAPDF_DATA_PATH=os.environ['LHAPDF_DATA_PATH']
892 
893  LHAPATH,LHADATAPATH=get_LHAPDF_PATHS()
894 
895  pdfname=''
896  pdfid=-999
897 
898 
899  run_card=process_dir+'/Cards/run_card.dat'
900  mydict=getDictFromCard(run_card)
901 
902  if mydict["pdlabel"].replace("'","") == 'lhapdf':
903  #Make local LHAPDF dir
904  mglog.info('creating local LHAPDF dir: MGC_LHAPDF/')
905  if os.path.islink('MGC_LHAPDF/'):
906  os.unlink('MGC_LHAPDF/')
907  elif os.path.isdir('MGC_LHAPDF/'):
908  shutil.rmtree('MGC_LHAPDF/')
909 
910  newMGCLHA='MGC_LHAPDF/'
911 
912  mkdir = subprocess.Popen(['mkdir','-p',newMGCLHA])
913  mkdir.wait()
914 
915  pdfs_used=[ int(x) for x in mydict['lhaid'].replace(' ',',').split(',') ]
916  # included systematics pdfs here
917  if 'sys_pdf' in mydict:
918  sys_pdf=mydict['sys_pdf'].replace('&&',' ').split()
919  for s in sys_pdf:
920  if s.isdigit():
921  idx=int(s)
922  if idx>1000: # the sys_pdf syntax is such that small numbers are used to specify the subpdf index
923  pdfs_used.append(idx)
924  else:
925  pdfs_used.append(s)
926  if 'systematics_arguments' in mydict:
927  systematics_arguments=MadGraphControl.MadGraphSystematicsUtils.parse_systematics_arguments(mydict['systematics_arguments'])
928  if 'pdf' in systematics_arguments:
929  sys_pdf=systematics_arguments['pdf'].replace(',',' ').replace('@',' ').split()
930  for s in sys_pdf:
931  if s.isdigit():
932  idx=int(s)
933  if idx>1000: # the sys_pdf syntax is such that small numbers are used to specify the subpdf index
934  pdfs_used.append(idx)
935  else:
936  pdfs_used.append(s)
937  for pdf in pdfs_used:
938  if isinstance(pdf,str) and (pdf.lower()=='errorset' or pdf.lower()=='central'):
939  continue
940  # new function to get both lhapdf id and name
941  pdfid,pdfname=get_lhapdf_id_and_name(pdf)
942  mglog.info("Found LHAPDF ID="+str(pdfid)+", name="+pdfname)
943 
944  if not os.path.exists(newMGCLHA+pdfname) and not os.path.lexists(newMGCLHA+pdfname):
945  if not os.path.exists(LHADATAPATH+'/'+pdfname):
946  mglog.warning('PDF not installed at '+LHADATAPATH+'/'+pdfname)
947  if allow_links:
948  mglog.info('linking '+LHADATAPATH+'/'+pdfname+' --> '+newMGCLHA+pdfname)
949  os.symlink(LHADATAPATH+'/'+pdfname,newMGCLHA+pdfname)
950  else:
951  mglog.info('copying '+LHADATAPATH+'/'+pdfname+' --> '+newMGCLHA+pdfname)
952  shutil.copytree(LHADATAPATH+'/'+pdfname,newMGCLHA+pdfname)
953 
954  if allow_links:
955  mglog.info('linking '+LHADATAPATH+'/pdfsets.index --> '+newMGCLHA+'pdfsets.index')
956  os.symlink(LHADATAPATH+'/pdfsets.index',newMGCLHA+'pdfsets.index')
957 
958  atlasLHADATAPATH=LHADATAPATH.replace('sft.cern.ch/lcg/external/lhapdfsets/current','atlas.cern.ch/repo/sw/Generators/lhapdfsets/current')
959  mglog.info('linking '+atlasLHADATAPATH+'/lhapdf.conf --> '+newMGCLHA+'lhapdf.conf')
960  os.symlink(atlasLHADATAPATH+'/lhapdf.conf',newMGCLHA+'lhapdf.conf')
961  else:
962  mglog.info('copying '+LHADATAPATH+'/pdfsets.index --> '+newMGCLHA+'pdfsets.index')
963  shutil.copy2(LHADATAPATH+'/pdfsets.index',newMGCLHA+'pdfsets.index')
964 
965  atlasLHADATAPATH=LHADATAPATH.replace('sft.cern.ch/lcg/external/lhapdfsets/current','atlas.cern.ch/repo/sw/Generators/lhapdfsets/current')
966  mglog.info('copying '+atlasLHADATAPATH+'/lhapdf.conf -->'+newMGCLHA+'lhapdf.conf')
967  shutil.copy2(atlasLHADATAPATH+'/lhapdf.conf',newMGCLHA+'lhapdf.conf')
968 
969 
970  LHADATAPATH=os.getcwd()+'/MGC_LHAPDF'
971 
972  else:
973  mglog.info('Not using LHAPDF')
974  return (LHAPATH,origLHAPATH,origLHAPDF_DATA_PATH)
975 
976 
977  if isNLO:
978  os.environ['LHAPDF_DATA_PATH']=LHADATAPATH
979 
980  mglog.info('Path to LHAPDF install dir: '+LHAPATH)
981  mglog.info('Path to LHAPDF data dir: '+LHADATAPATH)
982  if not os.path.isdir(LHADATAPATH):
983  raise RuntimeError('LHAPDF data dir not accesible: '+LHADATAPATH)
984  if not os.path.isdir(LHAPATH):
985  raise RuntimeError('LHAPDF path dir not accesible: '+LHAPATH)
986 
987  # Dealing with LHAPDF
988  if extlhapath:
989  lhapdfconfig=extlhapath
990  if not os.access(lhapdfconfig,os.X_OK):
991  raise RuntimeError('Failed to find valid external lhapdf-config at '+lhapdfconfig)
992  LHADATAPATH=subprocess.Popen([lhapdfconfig, '--datadir'],stdout = subprocess.PIPE).stdout.read().strip()
993  mglog.info('Changing LHAPDF_DATA_PATH to '+LHADATAPATH)
994  os.environ['LHAPDF_DATA_PATH']=LHADATAPATH
995  else:
996  getlhaconfig = subprocess.Popen(['get_files','-data','lhapdf-config'])
997  getlhaconfig.wait()
998  #Get custom lhapdf-config
999  if not os.access(os.getcwd()+'/lhapdf-config',os.X_OK):
1000  mglog.error('Failed to get lhapdf-config from MadGraphControl')
1001  return 1
1002  lhapdfconfig = os.getcwd()+'/lhapdf-config'
1003 
1004  mglog.info('lhapdf-config --version: '+str(subprocess.Popen([lhapdfconfig, '--version'],stdout = subprocess.PIPE).stdout.read().strip()))
1005  mglog.info('lhapdf-config --prefix: '+str(subprocess.Popen([lhapdfconfig, '--prefix'],stdout = subprocess.PIPE).stdout.read().strip()))
1006  mglog.info('lhapdf-config --libdir: '+str(subprocess.Popen([lhapdfconfig, '--libdir'],stdout = subprocess.PIPE).stdout.read().strip()))
1007  mglog.info('lhapdf-config --datadir: '+str(subprocess.Popen([lhapdfconfig, '--datadir'],stdout = subprocess.PIPE).stdout.read().strip()))
1008  mglog.info('lhapdf-config --pdfsets-path: '+str(subprocess.Popen([lhapdfconfig, '--pdfsets-path'],stdout = subprocess.PIPE).stdout.read().strip()))
1009 
1010  modify_config_card(process_dir=process_dir,settings={'lhapdf':lhapdfconfig,'lhapdf_py3':lhapdfconfig})
1011 
1012  mglog.info('Creating links for LHAPDF')
1013  if os.path.islink(process_dir+'/lib/PDFsets'):
1014  os.unlink(process_dir+'/lib/PDFsets')
1015  elif os.path.isdir(process_dir+'/lib/PDFsets'):
1016  shutil.rmtree(process_dir+'/lib/PDFsets')
1017  if allow_links:
1018  os.symlink(LHADATAPATH,process_dir+'/lib/PDFsets')
1019  else:
1020  shutil.copytree(LHADATAPATH,process_dir+'/lib/PDFsets')
1021  mglog.info('Available PDFs are:')
1022  mglog.info( sorted( [ x for x in os.listdir(process_dir+'/lib/PDFsets') if ".tar.gz" not in x ] ) )
1023 
1024  global MADGRAPH_COMMAND_STACK
1025  MADGRAPH_COMMAND_STACK += [ '# Copy the LHAPDF files locally' ]
1026  MADGRAPH_COMMAND_STACK += [ 'cp -r '+os.getcwd()+'/MGC_LHAPDF .' ]
1027  MADGRAPH_COMMAND_STACK += [ 'cp -r '+process_dir+'/lib/PDFsets ${MGaMC_PROCESS_DIR}/lib/' ]
1028 
1029  return (LHAPATH,origLHAPATH,origLHAPDF_DATA_PATH)
1030 
1031 
1032 # Function to set the number of cores and the running mode in the run card
1033 def setNCores(process_dir, Ncores=None):
1034  my_Ncores = Ncores
1035  my_runMode = 2 if 'ATHENA_PROC_NUMBER' in os.environ else 0
1036  if Ncores is None and 'ATHENA_PROC_NUMBER' in os.environ and int(os.environ['ATHENA_PROC_NUMBER'])>0:
1037  my_Ncores = int(os.environ['ATHENA_PROC_NUMBER'])
1038  my_runMode = 2
1039  if my_Ncores is None:
1040  mglog.info('Setting up for serial run')
1041  my_Ncores = 1
1042 
1043  modify_config_card(process_dir=process_dir,settings={'nb_core':my_Ncores,'run_mode':my_runMode,'automatic_html_opening':'False'})
1044 
1045 
1046 def resetLHAPDF(origLHAPATH='',origLHAPDF_DATA_PATH=''):
1047  mglog.info('Restoring original LHAPDF env variables:')
1048  os.environ['LHAPATH']=origLHAPATH
1049  os.environ['LHAPDF_DATA_PATH']=origLHAPDF_DATA_PATH
1050  mglog.info('LHAPATH='+os.environ['LHAPATH'])
1051  mglog.info('LHAPDF_DATA_PATH='+os.environ['LHAPDF_DATA_PATH'])
1052 
1053 
1055  madpath=os.environ['MADPATH']
1056  if not os.access(madpath+'/bin/mg5_aMC',os.R_OK):
1057  raise RuntimeError('mg5_aMC executable not found in '+madpath)
1058  return madpath+'/bin/mg5_aMC'
1059 
1060 
1061 def add_lifetimes(process_dir,threshold=None):
1062  """ Add lifetimes to the generated LHE file. Should be
1063  called after generate_events is called.
1064  """
1065  if config_only_check():
1066  return
1067 
1068  me_exec=get_mg5_executable()
1069 
1070  if len(glob.glob(process_dir+'/Events/*'))<1:
1071  mglog.error('Process dir '+process_dir+' does not contain events?')
1072  run = glob.glob(process_dir+'/Events/*')[0].split('/')[-1]
1073 
1074  # Note : This slightly clunky implementation is needed for the time being
1075  # See : https://answers.launchpad.net/mg5amcnlo/+question/267904
1076 
1077  tof_c = open('time_of_flight_exec_card','w')
1078  tof_c.write('launch '+process_dir+''' -i
1079 add_time_of_flight '''+run+((' --threshold='+str(threshold)) if threshold is not None else ''))
1080  tof_c.close()
1081 
1082  mglog.info('Started adding time of flight info '+str(time.asctime()))
1083 
1084  global MADGRAPH_CATCH_ERRORS
1085  generate = stack_subprocess([python,me_exec,'time_of_flight_exec_card'],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
1086  (out,err) = generate.communicate()
1087  error_check(err,generate.returncode)
1088 
1089  mglog.info('Finished adding time of flight information at '+str(time.asctime()))
1090 
1091  # Re-zip the file if needed
1092  lhe_gz = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
1093  if not os.access(lhe_gz,os.R_OK):
1094  mglog.info('LHE file needs to be zipped')
1095  lhe = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
1096  rezip = stack_subprocess(['gzip',lhe])
1097  rezip.wait()
1098  mglog.info('Zipped')
1099  else:
1100  mglog.info('LHE file zipped by MadGraph automatically. Nothing to do')
1101 
1102  return True
1103 
1104 
1105 def add_madspin(madspin_card=None,process_dir=MADGRAPH_GRIDPACK_LOCATION):
1106  """ Run madspin on the generated LHE file. Should be
1107  run when you have inputGeneratorFile set.
1108  Only requires a simplified process with the same model that you are
1109  interested in (needed to set up a process directory for MG5_aMC)
1110  """
1111  if config_only_check():
1112  return
1113 
1114  me_exec=get_mg5_executable()
1115 
1116  if madspin_card is not None:
1117  shutil.copyfile(madspin_card,process_dir+'/Cards/madspin_card.dat')
1118 
1119  if len(glob.glob(process_dir+'/Events/*'))<1:
1120  mglog.error('Process dir '+process_dir+' does not contain events?')
1121  proc_dir_list = glob.glob(process_dir+'/Events/*')
1122  run=None
1123  for adir in proc_dir_list:
1124  if 'GridRun_' in adir:
1125  run=adir.split('/')[-1]
1126  break
1127  else:
1128  run=proc_dir_list[0].split('/')[-1]
1129 
1130  # Note : This slightly clunky implementation is needed for the time being
1131  # See : https://answers.launchpad.net/mg5amcnlo/+question/267904
1132 
1133  ms_c = open('madspin_exec_card','w')
1134  ms_c.write('launch '+process_dir+''' -i
1135 decay_events '''+run)
1136  ms_c.close()
1137 
1138  mglog.info('Started running madspin at '+str(time.asctime()))
1139 
1140  global MADGRAPH_CATCH_ERRORS
1141  generate = stack_subprocess([python,me_exec,'madspin_exec_card'],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
1142  (out,err) = generate.communicate()
1143  error_check(err,generate.returncode)
1144  if len(glob.glob(process_dir+'/Events/'+run+'_decayed_*/')) == 0:
1145  mglog.error('No '+process_dir+'/Events/'+run+'_decayed_*/ can be found')
1146  raise RuntimeError('Problem while running MadSpin')
1147 
1148  mglog.info('Finished running madspin at '+str(time.asctime()))
1149 
1150  # Re-zip the file if needed
1151  lhe_gz = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
1152  if not os.access(lhe_gz,os.R_OK):
1153  mglog.info('LHE file needs to be zipped')
1154  lhe = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
1155  rezip = stack_subprocess(['gzip',lhe])
1156  rezip.wait()
1157  mglog.info('Zipped')
1158  else:
1159  mglog.info('LHE file zipped by MadGraph automatically. Nothing to do')
1160 
1161 
1162 def madspin_on_lhe(input_LHE,madspin_card,runArgs=None,keep_original=False):
1163  """ Run MadSpin on an input LHE file. Takes the process
1164  from the LHE file, so you don't need to have a process directory
1165  set up in advance. Runs MadSpin and packs the LHE file up appropriately
1166  Needs runArgs for the file handling"""
1167  if not os.access(input_LHE,os.R_OK):
1168  raise RuntimeError('Could not find LHE file '+input_LHE)
1169  if not os.access(madspin_card,os.R_OK):
1170  raise RuntimeError('Could not find input MadSpin card '+madspin_card)
1171  if keep_original:
1172  shutil.copy(input_LHE,input_LHE+'.original')
1173  mglog.info('Put backup copy of LHE file at '+input_LHE+'.original')
1174  # Start writing the card for execution
1175  madspin_exec_card = open('madspin_exec_card','w')
1176  madspin_exec_card.write('import '+input_LHE+'\n')
1177  # Based on the original card
1178  input_madspin_card = open(madspin_card,'r')
1179  has_launch = False
1180  for l in input_madspin_card.readlines():
1181  commands = l.split('#')[0].split()
1182  # Skip import of a file name that isn't our file
1183  if len(commands)>1 and 'import'==commands[0] and not 'model'==commands[1]:
1184  continue
1185  # Check for a launch command
1186  if len(commands)>0 and 'launch' == commands[0]:
1187  has_launch = True
1188  madspin_exec_card.write(l.strip()+'\n')
1189  if not has_launch:
1190  madspin_exec_card.write('launch\n')
1191  madspin_exec_card.close()
1192  input_madspin_card.close()
1193  # Now get the madspin executable
1194  madpath=os.environ['MADPATH']
1195  if not os.access(madpath+'/MadSpin/madspin',os.R_OK):
1196  raise RuntimeError('madspin executable not found in '+madpath)
1197  mglog.info('Starting madspin at '+str(time.asctime()))
1198  global MADGRAPH_CATCH_ERRORS
1199  generate = stack_subprocess([python,madpath+'/MadSpin/madspin','madspin_exec_card'],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
1200  (out,err) = generate.communicate()
1201  error_check(err,generate.returncode)
1202  mglog.info('Done with madspin at '+str(time.asctime()))
1203  # Should now have a re-zipped LHE file
1204  # We now have to do a shortened version of arrange_output below
1205  # Clean up in case a link or file was already there
1206  if os.path.exists(os.getcwd()+'/events.lhe'):
1207  os.remove(os.getcwd()+'/events.lhe')
1208 
1209  mglog.info('Unzipping generated events.')
1210  unzip = stack_subprocess(['gunzip','-f',input_LHE+'.gz'])
1211  unzip.wait()
1212 
1213  mglog.info('Putting a copy in place for the transform.')
1214  mod_output = open(os.getcwd()+'/events.lhe','w')
1215 
1216  #Removing empty lines in LHE
1217  nEmpty=0
1218  with open(input_LHE,'r') as fileobject:
1219  for line in fileobject:
1220  if line.strip():
1221  mod_output.write(line)
1222  else:
1223  nEmpty=nEmpty+1
1224  mod_output.close()
1225 
1226  mglog.info('Removed '+str(nEmpty)+' empty lines from LHEF')
1227 
1228  # Actually move over the dataset - this first part is horrible...
1229  if runArgs is None:
1230  raise RuntimeError('Must provide runArgs to madspin_on_lhe')
1231 
1232  outputDS = runArgs.outputTXTFile if hasattr(runArgs,'outputTXTFile') else 'tmp_LHE_events'
1233 
1234  mglog.info('Moving file over to '+outputDS.split('.tar.gz')[0]+'.events')
1235  shutil.move(os.getcwd()+'/events.lhe',outputDS.split('.tar.gz')[0]+'.events')
1236 
1237  mglog.info('Re-zipping into dataset name '+outputDS)
1238  rezip = stack_subprocess(['tar','cvzf',outputDS,outputDS.split('.tar.gz')[0]+'.events'])
1239  rezip.wait()
1240 
1241  # shortening the outputDS in the case of an output TXT file
1242  if hasattr(runArgs,'outputTXTFile') and runArgs.outputTXTFile is not None:
1243  outputDS = outputDS.split('.TXT')[0]
1244  # Do some fixing up for them
1245  if runArgs is not None:
1246  mglog.debug('Setting inputGenerator file to '+outputDS)
1247  runArgs.inputGeneratorFile=outputDS
1248 
1249 
1250 def arrange_output(process_dir=MADGRAPH_GRIDPACK_LOCATION,lhe_version=None,saveProcDir=False,runArgs=None,fixEventWeightsForBridgeMode=False):
1251  if config_only_check():
1252  return
1253 
1254  # NLO is not *really* the question here, we need to know if we should look for weighted or
1255  # unweighted events in the output directory. MadSpin (above) only seems to give weighted
1256  # results for now?
1257  if len(glob.glob(os.path.join(process_dir, 'Events','*')))<1:
1258  mglog.error('Process dir '+process_dir+' does not contain events?')
1259  proc_dir_list = glob.glob(os.path.join(process_dir, 'Events', '*'))
1260  this_run_name=None
1261  # looping over possible directories to find the right one
1262  for adir in proc_dir_list:
1263  if 'decayed' in adir:# skipping '*decayed*' directories produced by MadSpin, will be picked later if they exist
1264  continue
1265  else:
1266  if 'GridRun_' in adir:
1267  this_run_name=adir
1268  break # GridRun_* directories have priority
1269  elif os.path.join(process_dir, 'Events',MADGRAPH_RUN_NAME) in adir:
1270  this_run_name=adir
1271  if not os.access(this_run_name,os.R_OK):
1272  raise RuntimeError('Unable to locate run directory')
1273 
1274  hasUnweighted = os.access(this_run_name+'/unweighted_events.lhe.gz',os.R_OK)
1275 
1276  hasRunMadSpin=False
1277  madspinDirs=sorted(glob.glob(this_run_name+'_decayed_*/'))
1278  if len(madspinDirs):
1279  hasRunMadSpin=True
1280  if hasRunMadSpin and not hasUnweighted:
1281  # check again:
1282  hasUnweighted = os.access(madspinDirs[-1]+'/unweighted_events.lhe.gz',os.R_OK)
1283 
1284  global MADGRAPH_COMMAND_STACK
1285  if hasRunMadSpin:
1286  if len(madspinDirs):
1287  if hasUnweighted:
1288  # so this is a bit of a mess now...
1289  # if madspin is run from an NLO grid pack the correct lhe events are at both
1290  # madevent/Events/run_01/unweighted_events.lhe.gz
1291  # and madevent/Events/run_01_decayed_1/events.lhe.gz
1292  # so there are unweighted events but not in the madspinDir...
1293  if os.path.exists(madspinDirs[-1]+'/unweighted_events.lhe.gz'):
1294  MADGRAPH_COMMAND_STACK += ['mv '+madspinDirs[-1]+'/unweighted_events.lhe.gz'+' '+this_run_name+'/unweighted_events.lhe.gz']
1295  shutil.move(madspinDirs[-1]+'/unweighted_events.lhe.gz',this_run_name+'/unweighted_events.lhe.gz')
1296  mglog.info('Moving MadSpin events from '+madspinDirs[-1]+'/unweighted_events.lhe.gz to '+this_run_name+'/unweighted_events.lhe.gz')
1297  elif os.path.exists(madspinDirs[-1]+'/events.lhe.gz'):
1298  MADGRAPH_COMMAND_STACK += ['mv '+madspinDirs[-1]+'/events.lhe.gz'+' '+this_run_name+'/unweighted_events.lhe.gz']
1299  shutil.move(madspinDirs[-1]+'/events.lhe.gz',this_run_name+'/unweighted_events.lhe.gz')
1300  mglog.info('Moving MadSpin events from '+madspinDirs[-1]+'/events.lhe.gz to '+this_run_name+'/unweighted_events.lhe.gz')
1301  else:
1302  raise RuntimeError('MadSpin was run but can\'t find files :(')
1303 
1304  else:
1305  MADGRAPH_COMMAND_STACK += ['mv '+madspinDirs[-1]+'/events.lhe.gz '+this_run_name+'/events.lhe.gz']
1306  shutil.move(madspinDirs[-1]+'/events.lhe.gz',this_run_name+'/events.lhe.gz')
1307  mglog.info('Moving MadSpin events from '+madspinDirs[-1]+'/events.lhe.gz to '+this_run_name+'/events.lhe.gz')
1308 
1309  else:
1310  mglog.error('MadSpin was run but can\'t find output folder '+(this_run_name+'_decayed_1/'))
1311  raise RuntimeError('MadSpin was run but can\'t find output folder '+(this_run_name+'_decayed_1/'))
1312 
1313  if fixEventWeightsForBridgeMode:
1314  mglog.info("Fixing event weights after MadSpin... initial checks.")
1315 
1316  # get the cross section from the undecayed LHE file
1317  spinmodenone=False
1318  MGnumevents=-1
1319  MGintweight=-1
1320 
1321  if hasUnweighted:
1322  eventsfilename="unweighted_events"
1323  else:
1324  eventsfilename="events"
1325  unzip = stack_subprocess(['gunzip','-f',this_run_name+'/%s.lhe.gz' % eventsfilename])
1326  unzip.wait()
1327 
1328  for line in open(process_dir+'/Events/'+MADGRAPH_RUN_NAME+'/%s.lhe'%eventsfilename):
1329  if "Number of Events" in line:
1330  sline=line.split()
1331  MGnumevents=int(sline[-1])
1332  elif "Integrated weight (pb)" in line:
1333  sline=line.split()
1334  MGintweight=float(sline[-1])
1335  elif "set spinmode none" in line:
1336  spinmodenone=True
1337  elif "</header>" in line:
1338  break
1339 
1340  if spinmodenone and MGnumevents>0 and MGintweight>0:
1341  mglog.info("Fixing event weights after MadSpin... modifying LHE file.")
1342  newlhe=open(this_run_name+'/%s_fixXS.lhe'%eventsfilename,'w')
1343  initlinecount=0
1344  eventlinecount=0
1345  inInit=False
1346  inEvent=False
1347 
1348  # new default for MG 2.6.1+ (https://its.cern.ch/jira/browse/AGENE-1725)
1349  # but verified from LHE below.
1350  event_norm_setting="average"
1351 
1352  for line in open(this_run_name+'/%s.lhe'%eventsfilename):
1353 
1354  newline=line
1355  if "<init>" in line:
1356  inInit=True
1357  initlinecount=0
1358  elif "</init>" in line:
1359  inInit=False
1360  elif inInit and initlinecount==0:
1361  initlinecount=1
1362  # check event_norm setting in LHE file, deteremines how Pythia interprets event weights
1363  sline=line.split()
1364  if abs(int(sline[-2])) == 3:
1365  event_norm_setting="sum"
1366  elif abs(int(sline[-2])) == 4:
1367  event_norm_setting="average"
1368  elif inInit and initlinecount==1:
1369  sline=line.split()
1370  # update the global XS info
1371  relunc=float(sline[1])/float(sline[0])
1372  sline[0]=str(MGintweight)
1373  sline[1]=str(float(sline[0])*relunc)
1374  if event_norm_setting=="sum":
1375  sline[2]=str(MGintweight/MGnumevents)
1376  elif event_norm_setting=="average":
1377  sline[2]=str(MGintweight)
1378  newline=' '.join(sline)
1379  newline+="\n"
1380  initlinecount+=1
1381  elif inInit and initlinecount>1:
1382  initlinecount+=1
1383  elif "<event>" in line:
1384  inEvent=True
1385  eventlinecount=0
1386  elif "</event>" in line:
1387  inEvent=False
1388  elif inEvent and eventlinecount==0:
1389  sline=line.split()
1390  # next change the per-event weights
1391  if event_norm_setting=="sum":
1392  sline[2]=str(MGintweight/MGnumevents)
1393  elif event_norm_setting=="average":
1394  sline[2]=str(MGintweight)
1395  newline=' '.join(sline)
1396  newline+="\n"
1397  eventlinecount+=1
1398  newlhe.write(newline)
1399  newlhe.close()
1400 
1401  mglog.info("Fixing event weights after MadSpin... cleaning up.")
1402  shutil.copyfile(this_run_name+'/%s.lhe' % eventsfilename,
1403  this_run_name+'/%s_badXS.lhe' % eventsfilename)
1404 
1405  shutil.move(this_run_name+'/%s_fixXS.lhe' % eventsfilename,
1406  this_run_name+'/%s.lhe' % eventsfilename)
1407 
1408  rezip = stack_subprocess(['gzip',this_run_name+'/%s.lhe' % eventsfilename])
1409  rezip.wait()
1410 
1411  rezip = stack_subprocess(['gzip',this_run_name+'/%s_badXS.lhe' % eventsfilename])
1412  rezip.wait()
1413 
1414  # Clean up in case a link or file was already there
1415  if os.path.exists(os.getcwd()+'/events.lhe'):
1416  os.remove(os.getcwd()+'/events.lhe')
1417 
1418  mglog.info('Unzipping generated events.')
1419  if hasUnweighted:
1420  unzip = stack_subprocess(['gunzip','-f',this_run_name+'/unweighted_events.lhe.gz'])
1421  unzip.wait()
1422  else:
1423  unzip = stack_subprocess(['gunzip','-f',this_run_name+'/events.lhe.gz'])
1424  unzip.wait()
1425 
1426  mglog.info('Putting a copy in place for the transform.')
1427  if hasUnweighted:
1428  orig_input = this_run_name+'/unweighted_events.lhe'
1429  mod_output = open(os.getcwd()+'/events.lhe','w')
1430  else:
1431  orig_input = this_run_name+'/events.lhe'
1432  mod_output = open(os.getcwd()+'/events.lhe','w')
1433 
1434  #Removing empty lines and bad comments in LHE
1435  #and check for existence of weights
1436  initrwgt=None
1437  nEmpty=0
1438  lhe_weights=[]
1439  with open(orig_input,'r') as fileobject:
1440  for line in fileobject:
1441  if line.strip():
1442  # search for bad characters (neccessary until at least MG5 2.8.1)
1443  newline=line
1444  if '#' not in newline:
1445  newline=newline
1446  elif '>' not in newline[ newline.find('#'): ]:
1447  newline=newline
1448  else:
1449  mglog.warning('Found bad LHE line with an XML mark in a comment: "'+newline.strip()+'"')
1450  newline=newline[:newline.find('#')]+'#'+ (newline[newline.find('#'):].replace('>','-'))
1451  # check for weightnames that should exist, simplify nominal weight names
1452  if initrwgt is False:
1453  pass
1454  elif "</initrwgt>" in newline:
1455  initrwgt=False
1456  elif "<initrwgt>" in newline:
1457  initrwgt=True
1458  elif initrwgt is not None:
1459  newline=newline.replace('_DYNSCALE-1','')
1460  if '</weight>' in newline:
1461  iend=newline.find('</weight>')
1462  istart=newline[:iend].rfind('>')
1463  lhe_weights+=[newline[istart+1:iend].strip()]
1464  mod_output.write(newline)
1465  else:
1466  nEmpty=nEmpty+1
1467  mod_output.close()
1468  mglog.info('Removed '+str(nEmpty)+' empty lines from LHEF')
1469 
1470  mglog.info("The following "+str(len(lhe_weights))+" weights have been written to the LHE file: "+",".join(lhe_weights))
1471  expected_weights=get_expected_reweight_names(get_reweight_card(process_dir))
1472  expected_weights+=get_expected_systematic_names(MADGRAPH_PDFSETTING)
1473  mglog.info("Checking whether the following expected weights are in LHE file: "+",".join(expected_weights))
1474  for w in expected_weights:
1475  if w not in lhe_weights:
1476  raise RuntimeError("Did not find expected weight "+w+" in lhe file. Did the reweight or systematics module crash?")
1477  mglog.info("Found all required weights!")
1478 
1479  if lhe_version:
1480  mod_output2 = open(os.getcwd()+'/events.lhe','r')
1481  test=mod_output2.readline()
1482  if 'version="' in test:
1483  mglog.info('Applying LHE version hack')
1484  final_file = open(os.getcwd()+'/events.lhe.copy','w')
1485  final_file.write('<LesHouchesEvents version="%i.0">\n'%lhe_version)
1486  shutil.copyfileobj(mod_output2, final_file)
1487  final_file.close()
1488  shutil.copy(os.getcwd()+'/events.lhe.copy',os.getcwd()+'/events.lhe')
1489  # Clean up after ourselves
1490  os.remove(os.getcwd()+'/events.lhe.copy')
1491  mod_output2.close()
1492 
1493  # Actually move over the dataset
1494  if runArgs is None:
1495  raise RuntimeError('Must provide runArgs to arrange_output')
1496 
1497  if hasattr(runArgs,'outputTXTFile'):
1498  outputDS = runArgs.outputTXTFile
1499  else:
1500  outputDS = 'tmp_LHE_events'
1501 
1502  mglog.info('Moving file over to '+outputDS.split('.tar.gz')[0]+'.events')
1503 
1504  shutil.move(os.getcwd()+'/events.lhe',outputDS.split('.tar.gz')[0]+'.events')
1505 
1506  mglog.info('Re-zipping into dataset name '+outputDS)
1507  rezip = stack_subprocess(['tar','cvzf',outputDS,outputDS.split('.tar.gz')[0]+'.events'])
1508  rezip.wait()
1509 
1510  if not saveProcDir:
1511  mglog.info('Removing the process directory')
1512  shutil.rmtree(process_dir,ignore_errors=True)
1513 
1514  if os.path.isdir('MGC_LHAPDF/'):
1515  shutil.rmtree('MGC_LHAPDF/',ignore_errors=True)
1516 
1517  # shortening the outputDS in the case of an output TXT file
1518  if hasattr(runArgs,'outputTXTFile') and runArgs.outputTXTFile is not None:
1519  outputDS = outputDS.split('.TXT')[0]
1520  # Do some fixing up for them
1521  if runArgs is not None:
1522  mglog.debug('Setting inputGenerator file to '+outputDS)
1523  runArgs.inputGeneratorFile=outputDS
1524 
1525  mglog.info('All done with output arranging!')
1526  return outputDS
1527 
1528 def get_expected_reweight_names(reweight_card_loc):
1529  if reweight_card_loc is None:
1530  return []
1531  names=[]
1532  f_rw=open(reweight_card_loc)
1533  for line in f_rw:
1534  if 'launch' not in line:
1535  continue
1536  match=re.match(r'launch.*--rwgt_info\s*=\s*(\S+).*',line.strip())
1537  if len(match.groups())!=1:
1538  raise RuntimeError('Unexpected format of reweight card in line'+line)
1539  else:
1540  names+=[match.group(1)]
1541  f_rw.close()
1542  return names
1543 
1545  names=[]
1546  if syst_setting is None or 'central_pdf' not in syst_setting:
1547  mglog.warning("Systematics have not been defined via base fragment or 'MADGRAPH_PDFSETTING', cannot check for expected weights")
1548  return []
1549  if 'pdf_variations' in syst_setting and isinstance(syst_setting['pdf_variations'],list):
1550  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':1.0,'muf':1.0,'pdf':syst_setting['central_pdf']}]
1551  for pdf in syst_setting['pdf_variations']:
1552  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':1.0,'muf':1.0,'pdf':pdf+1}]
1553  if 'alternative_pdfs' in syst_setting and isinstance(syst_setting['alternative_pdfs'],list):
1554  for pdf in syst_setting['alternative_pdfs']:
1555  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':1.0,'muf':1.0,'pdf':pdf}]
1556  if 'scale_variations' in syst_setting and isinstance(syst_setting['scale_variations'],list):
1557  for mur in syst_setting['scale_variations']:
1558  for muf in syst_setting['scale_variations']:
1559  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':mur,'muf':muf,'pdf':syst_setting['central_pdf']}]
1560  return names
1561 
1562 def setup_bias_module(bias_module,process_dir):
1563  run_card = process_dir+'/Cards/run_card.dat'
1564  if isinstance(bias_module,tuple):
1565  mglog.info('Using bias module '+bias_module[0])
1566  the_run_card = open(run_card,'r')
1567  for line in the_run_card:
1568  if 'bias_module' in line and not bias_module[0] in line:
1569  raise RuntimeError('You need to add the bias module '+bias_module[0]+' to the run card to actually run it')
1570  the_run_card.close()
1571  if len(bias_module)!=3:
1572  raise RuntimeError('Please give a 3-tuple of strings containing bias module name, bias module, and makefile. Alternatively, give path to bias module tarball.')
1573  bias_module_newpath=process_dir+'/Source/BIAS/'+bias_module[0]
1574  os.makedirs(bias_module_newpath)
1575  bias_module_file=open(bias_module_newpath+'/'+bias_module[0]+'.f','w')
1576  bias_module_file.write(bias_module[1])
1577  bias_module_file.close()
1578  bias_module_make_file=open(bias_module_newpath+'/Makefile','w')
1579  bias_module_make_file.write(bias_module[2])
1580  bias_module_make_file.close()
1581  else:
1582  mglog.info('Using bias module '+bias_module)
1583  bias_module_name=bias_module.split('/')[-1].replace('.gz','')
1584  bias_module_name=bias_module_name.replace('.tar','')
1585  the_run_card = open(run_card,'r')
1586  for line in the_run_card:
1587  if 'bias_module' in line and bias_module_name not in line:
1588  raise RuntimeError('You need to add the bias module '+bias_module_name+' to the run card to actually run it')
1589  the_run_card.close()
1590 
1591  if os.path.exists(bias_module+'.tar.gz'):
1592  bias_module_path=bias_module+'.tar.gz'
1593  elif os.path.exists(bias_module+'.gz'):
1594  bias_module_path=bias_module+'.gz'
1595  elif os.path.exists(bias_module):
1596  bias_module_path=bias_module
1597  else:
1598  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')
1599  return 1
1600  bias_module_newpath=process_dir+'/Source/BIAS/'+bias_module_path.split('/')[-1]
1601  mglog.info('Copying bias module into place: '+bias_module_newpath)
1602  shutil.copy(bias_module_path,bias_module_newpath)
1603  mglog.info('Unpacking bias module')
1604  if bias_module_newpath.endswith('.tar.gz'):
1605  untar = stack_subprocess(['tar','xvzf',bias_module_newpath,'--directory='+process_dir+'/Source/BIAS/'])
1606  untar.wait()
1607  elif bias_module_path.endswith('.gz'):
1608  gunzip = stack_subprocess(['gunzip',bias_module_newpath])
1609  gunzip.wait()
1610 
1611 
1612 def get_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION):
1613  if os.access(process_dir+'/Cards/reweight_card.dat',os.R_OK):
1614  return process_dir+'/Cards/reweight_card.dat'
1615  return None
1616 
1617 
1618 def check_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION):
1619  reweight_card=get_reweight_card(process_dir=process_dir)
1620  shutil.move(reweight_card,reweight_card+'.old')
1621  oldcard = open(reweight_card+'.old','r')
1622  newcard = open(reweight_card,'w')
1623  changed = False
1624  info_expression=r'launch.*--rwgt_info\s*=\s*(\S+).*'
1625  name_expression=info_expression.replace('info','name')
1626  goodname_expression=r'^[A-Za-z0-9_\-.]+$'
1627  for line in oldcard:
1628  # we are only interested in the 'launch' line
1629  if not line.strip().startswith('launch') :
1630  newcard.write(line)
1631  else:
1632  rwgt_name_match=re.match(name_expression,line.strip())
1633  rwgt_info_match=re.match(info_expression,line.strip())
1634  if rwgt_name_match is None and rwgt_info_match is None:
1635  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)
1636  for match in [rwgt_info_match,rwgt_name_match]:
1637  if match is None:
1638  continue
1639  if len(match.groups())!=1:
1640  raise RuntimeError('Unexpected format of reweight card in line: '+line)
1641  if not re.match(goodname_expression,match.group(1)):
1642  raise RuntimeError('No special character in reweighting info/name, only allowing '+goodname_expression)
1643  if rwgt_info_match is not None:
1644  newcard.write(line)
1645  elif rwgt_name_match is not None:
1646  newcard.write(line.strip()+' --rwgt_info={0}\n'.format(rwgt_name_match.group(1)))
1647  changed=True
1648  if changed:
1649  mglog.info('Updated reweight_card')
1650  newcard.close()
1651  oldcard.close()
1652 
1653 
1655  return """
1656 # Define multiparticle labels
1657 define p = g u c d s u~ c~ d~ s~
1658 define j = g u c d s u~ c~ d~ s~
1659 define pb = g u c d s b u~ c~ d~ s~ b~
1660 define jb = g u c d s b u~ c~ d~ s~ b~
1661 define l+ = e+ mu+
1662 define l- = e- mu-
1663 define vl = ve vm vt
1664 define vl~ = ve~ vm~ vt~
1665 define fu = u c e+ mu+ ta+
1666 define fu~ = u~ c~ e- mu- ta-
1667 define fd = d s ve~ vm~ vt~
1668 define fd~ = d~ s~ ve vm vt
1669 define susystrong = go ul ur dl dr cl cr sl sr t1 t2 b1 b2 ul~ ur~ dl~ dr~ cl~ cr~ sl~ sr~ t1~ t2~ b1~ b2~
1670 define susyweak = el- el+ er- er+ mul- mul+ mur- mur+ ta1- ta1+ ta2- ta2+ n1 n2 n3 n4 x1- x1+ x2- x2+ sve sve~ svm svm~ svt svt~
1671 define susylq = ul ur dl dr cl cr sl sr
1672 define susylq~ = ul~ ur~ dl~ dr~ cl~ cr~ sl~ sr~
1673 define susysq = ul ur dl dr cl cr sl sr t1 t2 b1 b2
1674 define susysq~ = ul~ ur~ dl~ dr~ cl~ cr~ sl~ sr~ t1~ t2~ b1~ b2~
1675 define susysl = el- el+ er- er+ mul- mul+ mur- mur+ ta1- ta1+ ta2- ta2+
1676 define susyv = sve svm svt
1677 define susyv~ = sve~ svm~ svt~
1678 """
1679 
1680 
1681 def get_SUSY_variations( process , masses , syst_mod , ktdurham = None ):
1682  # Don't override an explicit setting from the run card!
1683  if ktdurham is None:
1684  prod_particles = []
1685  if process is not None:
1686  id_map = {'go':'1000021','dl':'1000001','ul':'1000002','sl':'1000003','cl':'1000004','b1':'1000005','t1':'1000006',
1687  'dr':'2000001','ur':'2000002','sr':'2000003','cr':'2000004','b2':'2000005','t2':'2000006',
1688  'n1':'1000022','n2':'1000023','x1':'1000024','x2':'1000037','n3':'1000025','n4':'1000035',
1689  'el':'1000011','mul':'1000013','ta1':'1000015','sve':'1000012','svm':'1000014','svt':'1000016',
1690  'er':'2000011','mur':'2000013','ta2':'2000015'}
1691  for l in process:
1692  if 'generate' in l or 'add process' in l:
1693  clean_proc = l.replace('generate','').replace('+','').replace('-','').replace('~','').replace('add process','').split('>')[1].split(',')[0]
1694  for particle in clean_proc.split():
1695  if particle not in id_map:
1696  mglog.info(f'Particle {particle} not found in PDG ID map - skipping')
1697  else:
1698  prod_particles += id_map[particle]
1699  # If we don't specify a process, then all we can do is guess based on available masses
1700  # Same if we failed to identify the right particles
1701  my_mass = 10000.
1702  if len(prod_particles)>0:
1703  for x in prod_particles:
1704  if x in masses:
1705  my_mass = min(my_mass,abs(float(masses[x])))
1706  else:
1707  mglog.info(f'Seem to ask for production of PDG ID {x}, but {x} not in mass dictionary?')
1708  if my_mass>9999.:
1709  strong_ids = ['1000001','1000002','1000003','1000004','1000005','1000006','2000001','2000002','2000003','2000004','2000005','2000006','1000021']
1710  weak_ids = ['1000023','1000024','1000025','1000011','1000013','1000015','2000011','2000013','2000015','1000012','1000014','1000016']
1711  # First check the lightest of the heavy sparticles - all squarks and gluino
1712  my_mass = min([abs(float(masses[x])) for x in strong_ids if x in masses])
1713  # Now check if strong production was not the key mode
1714  if my_mass>10000.:
1715  # This is a little tricky, but: we want the heaviest non-decoupled mass
1716  my_mass = max([abs(float(masses[x])) for x in weak_ids if x in masses and float(masses[x])<10000.])
1717  # Final check for N1N1 with everything else decoupled
1718  if my_mass>10000. and '1000022' in masses:
1719  my_mass = masses['1000022']
1720  if my_mass>10000.:
1721  raise RuntimeError('Could not understand which mass to use for matching cut in '+str(masses))
1722 
1723  # Now set the matching scale accordingly
1724  ktdurham = min(my_mass*0.25,500)
1725  # Should not be weirdly low - can't imagine a situation where you'd really want the scale below 15 GeV
1726  ktdurham = max(ktdurham,15)
1727  if syst_mod is not None and 'qup' in syst_mod.lower():
1728  ktdurham = ktdurham*2.
1729  elif syst_mod is not None and 'qdown' in syst_mod.lower():
1730  ktdurham = ktdurham*0.5
1731 
1732  mglog.info('For matching, will use ktdurham of '+str(ktdurham))
1733 
1734  alpsfact = 1.0
1735  scalefact = 1.0
1736  if syst_mod is not None and 'alpsfactup' in syst_mod.lower():
1737  alpsfact = 2.0
1738  elif syst_mod is not None and 'alpsfactdown' in syst_mod.lower():
1739  alpsfact = 0.5
1740 
1741  if syst_mod is not None and 'scalefactup' in syst_mod.lower():
1742  scalefact = 2.0
1743  elif syst_mod is not None and 'scalefactdown' in syst_mod.lower():
1744  scalefact = 0.5
1745 
1746  return abs(ktdurham) , alpsfact , scalefact
1747 
1748 
1749 def SUSY_process(process=''):
1750  # Generate the new process!
1751  if 'import model' in process:
1752  mglog.info('Assuming that you have specified the model in your process string already')
1753  full_proc = ''
1754  for l in process.split('\n'):
1755  if 'import model' in l:
1756  full_proc += l+'\n'
1757  break
1758  # Only magically add helpful definitions if we are in the right model
1759  if 'MSSM_SLHA2' in full_proc:
1760  full_proc+=helpful_SUSY_definitions()
1761  for l in process.split('\n'):
1762  if 'import model' not in l:
1763  full_proc += l+'\n'
1764  full_proc+="""
1765 # Output processes to MadEvent directory
1766 output -f
1767 """
1768  else:
1769  full_proc = "import model MSSM_SLHA2\n"+helpful_SUSY_definitions()+"""
1770 # Specify process(es) to run
1771 
1772 """+process+"""
1773 # Output processes to MadEvent directory
1774 output -f
1775 """
1776  return full_proc
1777 
1778 
1779 def SUSY_Generation(runArgs = None, process=None, plugin=None,\
1780  syst_mod=None, keepOutput=False, param_card=None, writeGridpack=False,\
1781  madspin_card=None, run_settings={}, params={}, fixEventWeightsForBridgeMode=False, add_lifetimes_lhe=False, usePMGSettings=True):
1782 
1783  """
1784  Keyword Arguments:
1785  usePMGSettings (bool): See :py:func:`new_process`. Will set SM parameters to the appropriate values. Default: True.
1786  """
1787  ktdurham = run_settings['ktdurham'] if 'ktdurham' in run_settings else None
1788  ktdurham , alpsfact , scalefact = get_SUSY_variations( process, params['MASS'] , syst_mod , ktdurham=ktdurham )
1789 
1790  process_dir = MADGRAPH_GRIDPACK_LOCATION
1791  if not is_gen_from_gridpack():
1792  full_proc = SUSY_process(process)
1793  process_dir = new_process(full_proc, plugin=plugin, usePMGSettings=usePMGSettings)
1794  mglog.info('Using process directory '+str(process_dir))
1795 
1796  # Grab the param card and move the new masses into place
1797  modify_param_card(param_card_input=param_card,process_dir=process_dir,params=params)
1798 
1799  # Set up the extras dictionary
1800  settings = {'ktdurham':ktdurham,'scalefact':scalefact,'alpsfact':alpsfact}
1801  settings.update(run_settings) # This allows explicit settings in the input to override these settings
1802 
1803  # Set up the run card
1804  modify_run_card(process_dir=process_dir,runArgs=runArgs,settings=settings)
1805 
1806  # Set up madspin if needed
1807  if madspin_card is not None:
1808  if not os.access(madspin_card,os.R_OK):
1809  raise RuntimeError('Could not locate madspin card at '+str(madspin_card))
1810  shutil.copy(madspin_card,process_dir+'/Cards/madspin_card.dat')
1811 
1812  # Generate events!
1813  if is_gen_from_gridpack():
1814  generate_from_gridpack(runArgs=runArgs)
1815  else:
1816  # Grab the run card and move it into place
1817  generate(runArgs=runArgs,process_dir=process_dir,grid_pack=writeGridpack)
1818 
1819  # Add lifetimes to LHE before arranging output if requested
1820  if add_lifetimes_lhe :
1821  mglog.info('Requested addition of lifetimes to LHE files: doing so now.')
1822  if is_gen_from_gridpack():
1823  add_lifetimes()
1824  else:
1825  add_lifetimes(process_dir=process_dir)
1826 
1827  # Move output files into the appropriate place, with the appropriate name
1828  arrange_output(process_dir=process_dir,saveProcDir=keepOutput,runArgs=runArgs,fixEventWeightsForBridgeMode=fixEventWeightsForBridgeMode)
1829 
1830  mglog.info('All done generating events!!')
1831  return settings['ktdurham']
1832 
1833 
1834 def update_lhe_file(lhe_file_old,param_card_old=None,lhe_file_new=None,masses={},delete_old_lhe=True):
1835  """Build a new LHE file from an old one and an updated param card.
1836  The masses of some particles can be changed via the masses dictionary. No particles that appear in the events
1837  may have their masses changed.
1838  If the param card is provided, the decay block in the LHE file will be replaced with the one in the param card.
1839  By default, the old LHE file is removed.
1840  If None is provided as a new LHE file name, the new file will replace the old one."""
1841  # If we want to just use a temp file, then put in a little temp holder
1842  lhe_file_new_tmp = lhe_file_new if lhe_file_new is not None else lhe_file_old+'.tmp'
1843  # Make sure the LHE file is there
1844  if not os.access(lhe_file_old,os.R_OK):
1845  raise RuntimeError('Could not access old LHE file at '+str(lhe_file_old)+'. Please check the file location.')
1846  # Grab the old param card
1847  if param_card_old is not None:
1848  paramcard = subprocess.Popen(['get_files','-data',param_card_old])
1849  paramcard.wait()
1850  if not os.access(param_card_old,os.R_OK):
1851  raise RuntimeError('Could not get param card '+param_card_old)
1852  # Don't overwrite old param cards
1853  if os.access(lhe_file_new_tmp,os.R_OK):
1854  raise RuntimeError('Old file at'+str(lhe_file_new_tmp)+' in the current directory. Dont want to clobber it. Please move it first.')
1855 
1856  newlhe = open(lhe_file_new_tmp,'w')
1857  blockName = None
1858  decayEdit = False
1859  eventRead = False
1860  particles_in_events = []
1861  # Decay block ends with </slha>
1862 
1863  with open(lhe_file_old,'r') as fileobject:
1864  for line in fileobject:
1865  if decayEdit and '</slha>' not in line:
1866  continue
1867  if decayEdit and '</slha>' in line:
1868  decayEdit = False
1869  if line.strip().upper().startswith('BLOCK') or line.strip().upper().startswith('DECAY')\
1870  and len(line.strip().split()) > 1:
1871  pos = 0 if line.strip().startswith('DECAY') else 1
1872  blockName = line.strip().upper().split()[pos]
1873 
1874  akey = None
1875  if blockName != 'DECAY' and len(line.strip().split()) > 0:
1876  akey = line.strip().split()[0]
1877  elif blockName == 'DECAY' and len(line.strip().split()) > 1:
1878  akey = line.strip().split()[1]
1879 
1880  # Replace the masses with those in the dictionary
1881  if akey is not None and blockName == 'MASS' and akey in masses:
1882  newlhe.write(' '+akey+' '+str(masses[akey])+' # \n')
1883  mglog.info(' '+akey+' '+str(masses[akey])+' #')
1884  decayEdit = False
1885  continue
1886 
1887  # Replace the entire decay section of the LHE file with the one from the param card
1888  if blockName == 'DECAY' and param_card_old is not None:
1889  # We are now reading the decay blocks! Take them from the param card
1890  oldparam = open(param_card_old,'r')
1891  newDecays = False
1892  for old_line in oldparam.readlines():
1893  newBlockName = None
1894  if old_line.strip().upper().startswith('DECAY') and len(old_line.strip().split()) > 1:
1895  newBlockName = line.strip().upper().split()[pos]
1896  if newDecays:
1897  newlhe.write(old_line)
1898  elif newBlockName == 'DECAY':
1899  newDecays = True
1900  newlhe.write(old_line)
1901  oldparam.close()
1902  # Done adding the decays
1903  decayEdit = True
1904  blockName = None
1905  continue
1906 
1907  # Keep a record of the particles that are in the events
1908  if not eventRead and '<event>' in line:
1909  eventRead = True
1910  if eventRead:
1911  if len(line.split())==11:
1912  aparticle = line.split()[0]
1913  if aparticle not in particles_in_events:
1914  particles_in_events += [aparticle]
1915 
1916  # Otherwise write the line again
1917  newlhe.write(line)
1918 
1919  # Check that none of the particles that we were setting the masses of appear in the LHE events
1920  for akey in masses:
1921  if akey in particles_in_events:
1922  mglog.error('Attempted to change mass of a particle that was in an LHE event! This is not allowed!')
1923  return -1
1924 
1925  # Close up and return
1926  newlhe.close()
1927 
1928  # Move the new file to the old file location
1929  if lhe_file_new is None:
1930  os.remove(lhe_file_old)
1931  shutil.move(lhe_file_new_tmp,lhe_file_old)
1932  lhe_file_new_tmp = lhe_file_old
1933  # Delete the old file if requested
1934  elif delete_old_lhe:
1935  os.remove(lhe_file_old)
1936 
1937  return lhe_file_new_tmp
1938 
1939 
1940 def remap_lhe_pdgids(lhe_file_old,lhe_file_new=None,pdgid_map={},delete_old_lhe=True):
1941  """Update the PDG IDs used in an LHE file. This is a bit finicky, as we have to
1942  both touch the LHE file metadata _and_ modify the events themselves. But since this
1943  is "just" a remapping, it should be safe assuming Pythia8 is told the correct thing
1944  afterwards and can get the showering right."""
1945  # If we want to just use a temp file, then put in a little temp holder
1946  lhe_file_new_tmp = lhe_file_new if lhe_file_new is not None else lhe_file_old+'.tmp'
1947  # Make sure the LHE file is there
1948  if not os.access(lhe_file_old,os.R_OK):
1949  raise RuntimeError('Could not access old LHE file at '+str(lhe_file_old)+'. Please check the file location.')
1950 
1951  # Convert the map into a str:str map, no matter what we started with
1952  pdgid_map_str = { str(x) : str(pdgid_map[x]) for x in pdgid_map }
1953  # Add anti-particles if they aren't already there
1954  pdgid_map_str.update( { '-'+str(x) : '-'+str(pdgid_map[x]) for x in pdgid_map if '-'+str(x) not in pdgid_map } )
1955 
1956  newlhe = open(lhe_file_new_tmp,'w')
1957  blockName = None
1958  eventRead = False
1959  with open(lhe_file_old,'r') as fileobject:
1960  for line in fileobject:
1961  # In case we're reading the param section and we have a block, read the block name
1962  if line.strip().upper().startswith('BLOCK') or line.strip().upper().startswith('DECAY')\
1963  and len(line.strip().split()) > 1:
1964  pos = 0 if line.strip().startswith('DECAY') else 1
1965  blockName = line.strip().upper().split()[pos]
1966  elif '</slha>' in line:
1967  blockName = None
1968  # Check for comments - just write those and move on
1969  if len(line.split('#')[0].strip())==0:
1970  line_mod = line
1971  for pdgid in pdgid_map_str:
1972  if pdgid in line_mod.split():
1973  line_mod = line_mod.replace( pdgid , pdgid_map_str[pdgid] )
1974  newlhe.write(line_mod)
1975  continue
1976  # Replace the PDG ID in the mass block
1977  if blockName=='MASS' and line.split()[0] in pdgid_map_str:
1978  newlhe.write( line.replace( line.split()[0] , pdgid_map_str[ line.split()[0] ] , 1 ) )
1979  continue
1980  if blockName=='DECAY' and line.split()[1] in pdgid_map_str:
1981  newlhe.write( line.replace( line.split()[1] , pdgid_map_str[ line.split()[1] ] , 1 ) )
1982  continue
1983  if blockName=='QNUMBERS' and line.split()[2] in pdgid_map_str:
1984  newlhe.write( line.replace( line.split()[2] , pdgid_map_str[ line.split()[2] ] , 1 ) )
1985  continue
1986  if '<event>' in line:
1987  eventRead = True
1988  if eventRead and len(line.split())==13 and line.split()[0] in pdgid_map_str:
1989  newlhe.write( line.replace( line.split()[0] , pdgid_map_str[ line.split()[0] ] , 1 ) )
1990  continue
1991 
1992  # Otherwise write the line again
1993  newlhe.write(line)
1994 
1995  # Move the new file to the old file location
1996  if lhe_file_new is None:
1997  os.remove(lhe_file_old)
1998  shutil.move(lhe_file_new_tmp,lhe_file_old)
1999  lhe_file_new_tmp = lhe_file_old
2000  # Delete the old file if requested
2001  elif delete_old_lhe:
2002  os.remove(lhe_file_old)
2003 
2004  return lhe_file_new_tmp
2005 
2006 
2007 def find_key_and_update(akey,dictionary):
2008  """ Helper function when looking at param cards
2009  In some cases it's tricky to match keys - they may differ
2010  only in white space. This tries to sort out when we have
2011  a match, and then uses the one in blockParams afterwards.
2012  In the case of no match, it returns the original key.
2013  """
2014  test_key = ' '.join(akey.strip().replace('\t',' ').split())
2015  for key in dictionary:
2016  mod_key = ' '.join(key.strip().replace('\t',' ').split())
2017  if mod_key==test_key:
2018  return key
2019  return akey
2020 
2021 
2022 def modify_param_card(param_card_input=None,param_card_backup=None,process_dir=MADGRAPH_GRIDPACK_LOCATION,params={},output_location=None):
2023  """Build a new param_card.dat from an existing one.
2024  Params should be a dictionary of dictionaries. The first key is the block name, and the second in the param name.
2025  Keys can include MASS (for masses) and DECAY X (for decays of particle X)"""
2026  # Grab the old param card and move it into place
2027 
2028  # Check for the default run card location
2029  if param_card_input is None:
2030  param_card_input=process_dir+'/Cards/param_card.dat'
2031  elif param_card_input is not None and not os.access(param_card_input,os.R_OK):
2032  paramcard = subprocess.Popen(['get_files','-data',param_card_input])
2033  paramcard.wait()
2034  if not os.access(param_card_input,os.R_OK):
2035  raise RuntimeError('Could not get param card '+param_card_input)
2036  mglog.info('Using input param card at '+param_card_input)
2037 
2038  #ensure all blocknames and paramnames are upper case
2039  paramsUpper = {}
2040  for blockName in list(params.keys()):
2041  paramsUpper[blockName.upper()] = {}
2042  for paramName in list(params[blockName].keys()):
2043  paramsUpper[blockName.upper()][paramName.upper()] = params[blockName][paramName]
2044 
2045  if param_card_backup is not None:
2046  mglog.info('Keeping backup of original param card at '+param_card_backup)
2047  param_card_old = param_card_backup
2048  else:
2049  param_card_old = param_card_input+'.old_to_be_deleted'
2050  if os.path.isfile(param_card_old):
2051  os.unlink(param_card_old) # delete old backup
2052  os.rename(param_card_input, param_card_old) # change name of original card
2053 
2054  oldcard = open(param_card_old,'r')
2055  param_card_location= process_dir+'/Cards/param_card.dat' if output_location is None else output_location
2056  newcard = open(param_card_location,'w')
2057  decayEdit = False #only becomes true in a DECAY block when specifying the BR
2058  blockName = ""
2059  doneParams = {} #tracks which params have been done
2060  for linewithcomment in oldcard:
2061  line=linewithcomment.split('#')[0]
2062  if line.strip().upper().startswith('BLOCK') or line.strip().upper().startswith('DECAY')\
2063  and len(line.strip().split()) > 1:
2064  if decayEdit and blockName == 'DECAY':
2065  decayEdit = False # Start a new DECAY block
2066  pos = 0 if line.strip().startswith('DECAY') else 1
2067  if blockName=='MASS' and 'MASS' in paramsUpper:
2068  # Any residual masses to set?
2069  if "MASS" in doneParams:
2070  leftOvers = [ x for x in paramsUpper['MASS'] if x not in doneParams['MASS'] ]
2071  else:
2072  leftOvers = [ x for x in paramsUpper['MASS'] ]
2073 
2074  for pdg_id in leftOvers:
2075  mglog.warning('Adding mass line for '+str(pdg_id)+' = '+str(paramsUpper['MASS'][pdg_id])+' which was not in original param card')
2076  newcard.write(' '+str(pdg_id)+' '+str(paramsUpper['MASS'][pdg_id])+'\n')
2077  doneParams['MASS'][pdg_id]=True
2078  if blockName=='DECAY' and 'DECAY' not in line.strip().upper() and 'DECAY' in paramsUpper:
2079  # Any residual decays to include?
2080  leftOvers = [ x for x in paramsUpper['DECAY'] if x not in doneParams['DECAY'] ]
2081  for pdg_id in leftOvers:
2082  mglog.warning('Adding decay for pdg id '+str(pdg_id)+' which was not in the original param card')
2083  newcard.write( paramsUpper['DECAY'][pdg_id].strip()+'\n' )
2084  doneParams['DECAY'][pdg_id]=True
2085  blockName = line.strip().upper().split()[pos]
2086  if decayEdit:
2087  continue #skipping these lines because we are in an edit of the DECAY BR
2088 
2089  akey = None
2090  if blockName != 'DECAY' and len(line.strip().split()) > 0:
2091  # The line is already without the comment.
2092  # In the case of mixing matrices this is a bit tricky
2093  if len(line.split())==2:
2094  akey = line.upper().strip().split()[0]
2095  else:
2096  # Take everything but the last word
2097  akey = line.upper().strip()[:line.strip().rfind(' ')].strip()
2098  elif blockName == 'DECAY' and len(line.strip().split()) > 1:
2099  akey = line.strip().split()[1]
2100  if akey is None:
2101  newcard.write(linewithcomment)
2102  continue
2103 
2104  #check if we have params for this block
2105  if blockName not in paramsUpper:
2106  newcard.write(linewithcomment)
2107  continue
2108  blockParams = paramsUpper[blockName]
2109  # Check the spacing in the key
2110  akey = find_key_and_update(akey,blockParams)
2111 
2112  # look for a string key, which would follow a #
2113  stringkey = None
2114  if '#' in linewithcomment: #ignores comment lines
2115  stringkey = linewithcomment[linewithcomment.find('#')+1:].strip()
2116  if len(stringkey.split()) > 0:
2117  stringkey = stringkey.split()[0].upper()
2118 
2119  if akey not in blockParams and not (stringkey is not None and stringkey in blockParams):
2120  newcard.write(linewithcomment)
2121  continue
2122 
2123  if akey in blockParams and (stringkey is not None and stringkey in blockParams):
2124  raise RuntimeError('Conflicting use of numeric and string keys '+akey+' and '+stringkey)
2125 
2126  theParam = blockParams.get(akey,blockParams[stringkey] if stringkey in blockParams else None)
2127  if blockName not in doneParams:
2128  doneParams[blockName] = {}
2129  if akey in blockParams:
2130  doneParams[blockName][akey]=True
2131  elif stringkey is not None and stringkey in blockParams:
2132  doneParams[blockName][stringkey]=True
2133 
2134  #do special case of DECAY block
2135  if blockName=="DECAY":
2136  if theParam.splitlines()[0].split()[0].upper()=="DECAY":
2137  #specifying the full decay block
2138  for newline in theParam.splitlines():
2139  newcard.write(newline+'\n')
2140  mglog.info(newline)
2141  decayEdit = True
2142  else: #just updating the total width
2143  newcard.write('DECAY '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
2144  mglog.info('DECAY '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
2145  # second special case of QNUMBERS
2146  elif blockName=='QNUMBERS':
2147  #specifying the full QNUMBERS block
2148  for newline in theParam.splitlines():
2149  newcard.write(newline+'\n')
2150  mglog.info(newline)
2151  decayEdit = True
2152  else: #just updating the parameter
2153  newcard.write(' '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
2154  mglog.info(' '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
2155  # Done editing the line!
2156 
2157  #check that all specified parameters have been updated (helps to catch typos)
2158  for blockName in paramsUpper:
2159  if blockName not in doneParams and len(paramsUpper[blockName].keys())>0:
2160  raise RuntimeError('Did not find any of the parameters for block '+blockName+' in param_card')
2161  for paramName in paramsUpper[blockName]:
2162  if paramName not in doneParams[blockName]:
2163  raise RuntimeError('Was not able to replace parameter '+paramName+' in param_card')
2164 
2165  # Close up and return
2166  oldcard.close()
2167  newcard.close()
2168 
2169 
2170 def modify_run_card(run_card_input=None,run_card_backup=None,process_dir=MADGRAPH_GRIDPACK_LOCATION,runArgs=None,settings={},skipBaseFragment=False):
2171  """Build a new run_card.dat from an existing one.
2172  This function can get a fresh runcard from DATAPATH or start from the process directory.
2173  Settings is a dictionary of keys (no spaces needed) and values to replace.
2174  """
2175  if config_only_check():
2176  mglog.info('Running config-only. No proc card, so not operating on the run card.')
2177  return
2178 
2179  # Operate on lower case settings, and choose the capitalization MG5 has as the default (or all lower case)
2180  settings_lower = {}
2181  for s in list(settings.keys()):
2182  settings_lower[s.lower()] = settings[s]
2183 
2184  # Check for the default run card location
2185  if run_card_input is None:
2186  run_card_input=get_default_runcard(process_dir)
2187  elif run_card_input is not None and not os.access(run_card_input,os.R_OK):
2188  runcard = subprocess.Popen(['get_files','-data',run_card_input])
2189  runcard.wait()
2190  if not os.access(run_card_input,os.R_OK):
2191  raise RuntimeError('Could not get run card '+run_card_input)
2192 
2193  # guess NLO
2194  isNLO=is_NLO_run(process_dir=process_dir)
2195  # add gobal PDF and scale uncertainty config to extras, except PDF or weights for syscal config are explictly set
2196  if not skipBaseFragment:
2197  MadGraphControl.MadGraphSystematicsUtils.setup_pdf_and_systematic_weights(MADGRAPH_PDFSETTING,settings_lower,isNLO)
2198 
2199  # Get some info out of the runArgs
2200  if runArgs is not None:
2201  beamEnergy,rand_seed = get_runArgs_info(runArgs)
2202  if 'iseed' not in settings_lower:
2203  settings_lower['iseed']=rand_seed
2204  if not isNLO and 'python_seed' not in settings_lower:
2205  settings_lower['python_seed']=rand_seed
2206  if 'beamenergy' in settings_lower:
2207  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']))
2208  beamEnergy=settings_lower['beamenergy']
2209  settings_lower.pop('beamenergy')
2210  if 'ebeam1' not in settings_lower:
2211  settings_lower['ebeam1']=beamEnergy
2212  if 'ebeam2' not in settings_lower:
2213  settings_lower['ebeam2']=beamEnergy
2214  # Make sure nevents is an integer
2215  if 'nevents' in settings_lower:
2216  settings_lower['nevents'] = int(settings_lower['nevents'])
2217 
2218  mglog.info('Modifying run card located at '+run_card_input)
2219  if run_card_backup is not None:
2220  mglog.info('Keeping backup of original run card at '+run_card_backup)
2221  run_card_old = run_card_backup
2222  else:
2223  run_card_old = run_card_input+'.old_to_be_deleted'
2224  mglog.debug('Modifying runcard settings: '+str(settings_lower))
2225  if os.path.isfile(run_card_old):
2226  os.unlink(run_card_old) # delete old backup
2227  os.rename(run_card_input, run_card_old) # change name of original card
2228 
2229  oldCard = open(run_card_old, 'r')
2230  newCard = open(process_dir+'/Cards/run_card.dat', 'w')
2231  used_settings = []
2232  for line in iter(oldCard):
2233  if not line.strip().startswith('#'): # line commented out
2234  command = line.split('!', 1)[0]
2235  comment = line.split('!', 1)[1] if '!' in line else ''
2236  if '=' in command:
2237  setting = command.split('=')[-1] #.strip()
2238  stripped_setting = setting.strip()
2239  oldValue = '='.join(command.split('=')[:-1])
2240  if stripped_setting.lower() in settings_lower:
2241  # if setting set to 'None' it will be removed from run_card
2242  if settings_lower[stripped_setting.lower()] is None:
2243  line=''
2244  mglog.info('Removing '+stripped_setting+'.')
2245  used_settings += [ stripped_setting.lower() ]
2246  else:
2247  line = oldValue.replace(oldValue.strip(), str(settings_lower[stripped_setting.lower()]))+'='+setting
2248  if comment != '':
2249  line += ' !' + comment
2250  mglog.info('Setting '+stripped_setting+' = '+str(settings_lower[stripped_setting.lower()]))
2251  used_settings += [ stripped_setting.lower() ]
2252  newCard.write(line.strip()+'\n')
2253 
2254  # Check whether mcatnlo_delta is applied to setup pythia8 path
2255  if 'mcatnlo_delta' in settings_lower:
2256  if settings_lower['mcatnlo_delta'] == 'True':
2257  modify_config_card(process_dir=process_dir,settings={'pythia8_path':os.getenv("PY8PATH")})
2258 
2259  # Clean up unused options
2260  for asetting in settings_lower:
2261  if asetting in used_settings:
2262  continue
2263  if settings_lower[asetting] is None:
2264  continue
2265  mglog.warning('Option '+asetting+' was not in the default run_card. Adding by hand a setting to '+str(settings_lower[asetting]) )
2266  newCard.write( ' '+str(settings_lower[asetting])+' = '+str(asetting)+'\n')
2267  # close files
2268  oldCard.close()
2269  newCard.close()
2270  mglog.info('Finished modification of run card.')
2271  if run_card_backup is None:
2272  os.unlink(run_card_old)
2273 
2274 
2275 def modify_config_card(config_card_backup=None,process_dir=MADGRAPH_GRIDPACK_LOCATION,settings={},set_commented=True):
2276  """Build a new configuration from an existing one.
2277  This function can get a fresh runcard from DATAPATH or start from the process directory.
2278  Settings is a dictionary of keys (no spaces needed) and values to replace.
2279  """
2280  # Check for the default config card location
2281  config_card=get_default_config_card(process_dir=process_dir)
2282 
2283  # The format is similar to the run card, but backwards
2284  mglog.info('Modifying config card located at '+config_card)
2285  if config_card_backup is not None:
2286  mglog.info('Keeping backup of original config card at '+config_card_backup)
2287  config_card_old = config_card_backup
2288  else:
2289  config_card_old = config_card+'.old_to_be_deleted'
2290  mglog.debug('Modifying config card settings: '+str(settings))
2291  if os.path.isfile(config_card_old):
2292  os.unlink(config_card_old) # delete old backup
2293  os.rename(config_card, config_card_old) # change name of original card
2294 
2295  oldCard = open(config_card_old, 'r')
2296  newCard = open(config_card, 'w')
2297  used_settings = []
2298  for line in iter(oldCard):
2299  lmod = line if set_commented else line.split('#')[0]
2300  if '=' in lmod:
2301  modified = False
2302  for setting in settings:
2303  if setting not in lmod:
2304  continue
2305  # Assume we hit
2306  mglog.info('Setting '+setting.strip()+' to '+str(settings[setting]))
2307  newCard.write(' '+str(setting.strip())+' = '+str(settings[setting])+'\n')
2308  used_settings += [ setting.strip() ]
2309  modified = True
2310  break
2311  if modified:
2312  continue
2313  newCard.write(line)
2314 
2315  # Clean up unused options
2316  for asetting in settings:
2317  if asetting in used_settings:
2318  continue
2319  if settings[asetting] is None:
2320  continue
2321  mglog.warning('Option '+asetting+' was not in the default config card. Adding by hand a setting to '+str(settings[asetting]) )
2322  newCard.write(' '+str(asetting)+' = '+str(settings[asetting])+'\n')
2323  # close files
2324  oldCard.close()
2325  newCard.close()
2326  mglog.info('Finished modification of config card.')
2327  if config_card_backup is None:
2328  os.unlink(config_card_old)
2329 
2330 
2331 def print_cards_from_dir(process_dir=MADGRAPH_GRIDPACK_LOCATION):
2332  card_dir=process_dir+'/Cards/'
2333  print_cards(proc_card=card_dir+'proc_card_mg5.dat',run_card=card_dir+'run_card.dat',param_card=card_dir+'param_card.dat',\
2334  madspin_card=card_dir+'madspin_card.dat',reweight_card=card_dir+'reweight_card.dat',warn_on_missing=False)
2335 
2336 
2337 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):
2338  if os.access(proc_card,os.R_OK):
2339  mglog.info("proc_card:")
2340  procCard = subprocess.Popen(['cat',proc_card])
2341  procCard.wait()
2342  elif warn_on_missing:
2343  mglog.warning('No proc_card: '+proc_card+' found')
2344 
2345  if run_card is not None and os.access(run_card,os.R_OK):
2346  mglog.info("run_card:")
2347  runCard = subprocess.Popen(['cat',run_card])
2348  runCard.wait()
2349  elif run_card is not None and warn_on_missing:
2350  mglog.warning('No run_card: '+run_card+' found')
2351  else:
2352  mglog.info('Default run card in use')
2353 
2354  if param_card is not None and os.access(param_card,os.R_OK):
2355  mglog.info("param_card:")
2356  paramCard = subprocess.Popen(['cat',param_card])
2357  paramCard.wait()
2358  elif param_card is not None and warn_on_missing:
2359  mglog.warning('No param_card: '+param_card+' found')
2360  else:
2361  mglog.info('Default param card in use')
2362 
2363  if madspin_card is not None and os.access(madspin_card,os.R_OK):
2364  mglog.info("madspin_card:")
2365  madspinCard = subprocess.Popen(['cat',madspin_card])
2366  madspinCard.wait()
2367  elif madspin_card is not None and warn_on_missing:
2368  mglog.warning('No madspin_card: '+madspin_card+' found')
2369  else:
2370  mglog.info('No madspin card in use')
2371 
2372  if reweight_card is not None and os.access(reweight_card,os.R_OK):
2373  mglog.info("reweight_card:")
2374  madspinCard = subprocess.Popen(['cat',reweight_card])
2375  madspinCard.wait()
2376  elif reweight_card is not None and warn_on_missing:
2377  mglog.warning('No reweight_card: '+reweight_card+' found')
2378  else:
2379  mglog.info('No reweight card in use')
2380 
2381 
2383  """ Simple function for checking if there is a grid pack.
2384  Relies on the specific location of the unpacked gridpack (madevent)
2385  which is here set as a global variable. The gridpack is untarred by
2386  the transform (Gen_tf.py) and no sign is sent to the job itself
2387  that there is a gridpack in use except the file's existence"""
2388  if os.access(MADGRAPH_GRIDPACK_LOCATION,os.R_OK):
2389  mglog.info('Located input grid pack area')
2390  return True
2391  return False
2392 
2393 
2394 def get_default_config_card(process_dir=MADGRAPH_GRIDPACK_LOCATION):
2395  if config_only_check():
2396  mglog.info('Athena running on config only mode: grabbing config card the old way, as there will be no proc dir')
2397  if os.access(os.environ['MADPATH']+'/input/mg5_configuration.txt',os.R_OK):
2398  shutil.copy(os.environ['MADPATH']+'/input/mg5_configuration.txt','local_mg5_configuration.txt')
2399  return 'local_mg5_configuration.txt'
2400 
2401  lo_config_card=process_dir+'/Cards/me5_configuration.txt'
2402  nlo_config_card=process_dir+'/Cards/amcatnlo_configuration.txt'
2403 
2404  if os.access(lo_config_card,os.R_OK) and not os.access(nlo_config_card,os.R_OK):
2405  return lo_config_card
2406  elif os.access(nlo_config_card,os.R_OK) and not os.access(lo_config_card,os.R_OK):
2407  return nlo_config_card
2408  elif os.access(nlo_config_card,os.R_OK) and os.access(lo_config_card,os.R_OK):
2409  mglog.error('Found both types of config card in '+process_dir)
2410  else:
2411  mglog.error('No config card in '+process_dir)
2412  raise RuntimeError('Unable to locate configuration card')
2413 
2414 
2415 def get_cluster_type(process_dir=MADGRAPH_GRIDPACK_LOCATION):
2416  card_in = open(get_default_config_card(process_dir=process_dir),'r')
2417  for l in card_in.readlines():
2418  if 'cluster_type' not in l.split('#')[0]:
2419  continue
2420  cluster_type = l.split('#')[0].split('=')[1]
2421  mglog.info('Returning cluster type: '+cluster_type)
2422  return cluster_type
2423  return None
2424 
2425 
2426 def is_NLO_run(process_dir=MADGRAPH_GRIDPACK_LOCATION):
2427  # Very simple check based on the above config card grabbing
2428  if config_only_check():
2429  return False
2430  return get_default_config_card(process_dir=process_dir)==process_dir+'/Cards/amcatnlo_configuration.txt'
2431 
2432 
2433 def run_card_consistency_check(isNLO=False,process_dir='.'):
2434  cardpath=process_dir+'/Cards/run_card.dat'
2435  mydict=getDictFromCard(cardpath)
2436 
2437  # We should always use event_norm = average [AGENE-1725] otherwise Pythia cross sections are wrong
2438  # Modification: average or bias is ok; sum is incorrect. Change the test to set sum to average
2439  if checkSetting('event_norm','sum',mydict):
2440  modify_run_card(process_dir=process_dir,settings={'event_norm':'average'},skipBaseFragment=True)
2441  mglog.warning("setting event_norm to average, there is basically no use case where event_norm=sum is a good idea")
2442 
2443  if not isNLO:
2444  #Check CKKW-L setting
2445  if 'ktdurham' in mydict and float(mydict['ktdurham']) > 0 and int(mydict['ickkw']) != 0:
2446  log='Bad combination of settings for CKKW-L merging! ktdurham=%s and ickkw=%s.'%(mydict['ktdurham'],mydict['ickkw'])
2447  mglog.error(log)
2448  raise RuntimeError(log)
2449 
2450  # Check if user is trying to use deprecated syscalc arguments with the other systematics script
2451  if 'systematics_program' not in mydict or mydict['systematics_program']=='systematics':
2452  syscalc_settings=['sys_pdf', 'sys_scalefact', 'sys_alpsfact', 'sys_matchscale']
2453  found_syscalc_setting=False
2454  for s in syscalc_settings:
2455  if s in mydict:
2456  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)')
2457  found_syscalc_setting=True
2458  if found_syscalc_setting:
2459  syst_arguments=MadGraphControl.MadGraphSystematicsUtils.convertSysCalcArguments(mydict)
2460  mglog.info('Converted syscalc arguments to systematics arguments: '+syst_arguments)
2461  syst_settings_update={'systematics_arguments':syst_arguments}
2462  for s in syscalc_settings:
2463  syst_settings_update[s]=None
2464  modify_run_card(process_dir=process_dir,settings=syst_settings_update,skipBaseFragment=True)
2465 
2466 
2467  # usually the pdf and systematics should be set during modify_run_card
2468  # but check again in case the user did not call the function or provides a different card here
2469  mglog.info('Checking PDF and systematics settings')
2470  if not MadGraphControl.MadGraphSystematicsUtils.base_fragment_setup_check(MADGRAPH_PDFSETTING,mydict,isNLO):
2471  # still need to set pdf and systematics
2472  syst_settings=MadGraphControl.MadGraphSystematicsUtils.get_pdf_and_systematic_settings(MADGRAPH_PDFSETTING,isNLO)
2473  modify_run_card(process_dir=process_dir,settings=syst_settings,skipBaseFragment=True)
2474 
2475  mydict_new=getDictFromCard(cardpath)
2476  if 'systematics_arguments' in mydict_new:
2477  systematics_arguments=MadGraphControl.MadGraphSystematicsUtils.parse_systematics_arguments(mydict_new['systematics_arguments'])
2478  if 'weight_info' not in systematics_arguments:
2479  mglog.info('Enforcing systematic weight name convention')
2480  dyn = None
2481  if '--dyn' in systematics_arguments or ' dyn' in systematics_arguments:
2482  if '--dyn' in systematics_arguments:
2483  dyn = systematics_arguments.split('--dyn')[1]
2484  if ' dyn' in systematics_arguments:
2485  dyn = systematics_arguments.split(' dyn')[1]
2486  dyn = dyn.replace('\'',' ').replace('=',' ').split()[0]
2487  if dyn is not None and len(dyn.split(','))>1:
2488  systematics_arguments['weight_info']=MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO_ALTDYNSCALES
2489  else:
2490  systematics_arguments['weight_info']=MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO
2491  modify_run_card(process_dir=process_dir,settings={'systematics_arguments':MadGraphControl.MadGraphSystematicsUtils.write_systematics_arguments(systematics_arguments)},skipBaseFragment=True)
2492 
2493  if not isNLO:
2494  if 'python_seed' not in mydict:
2495  mglog.warning('No python seed set in run_card -- adding one with same value as iseed')
2496  modify_run_card(process_dir=process_dir,settings={'python_seed':mydict['iseed']},skipBaseFragment=True)
2497 
2498  mglog.info('Finished checking run card - All OK!')
2499 
2500 def add_reweighting(run_name,reweight_card=None,process_dir=MADGRAPH_GRIDPACK_LOCATION):
2501  mglog.info('Running reweighting module on existing events')
2502  if reweight_card is not None:
2503  mglog.info('Copying new reweight card from '+reweight_card)
2504  shutil.move(reweight_card,process_dir+'/Cards/reweight_card.dat')
2505  reweight_cmd='{}/bin/madevent reweight {} -f'.format(process_dir,run_name)
2506  global MADGRAPH_CATCH_ERRORS
2507  reweight = stack_subprocess([python]+reweight_cmd.split(),stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
2508  (out,err) = reweight.communicate()
2509  error_check(err,reweight.returncode)
2510  mglog.info('Finished reweighting')
2511 
2513  if 'ATHENA_PROC_NUMBER' in os.environ and int(os.environ['ATHENA_PROC_NUMBER'])>0:
2514  mglog.info('Noticed that you have run with an athena MP-like whole-node setup. Will re-configure now to make sure that the remainder of the job runs serially.')
2515  # Try to modify the opts underfoot
2516  if not hasattr(opts,'nprocs'):
2517  mglog.warning('Did not see option!')
2518  else:
2519  opts.nprocs = 0
2520  mglog.debug(str(opts))
2521 
2522 
2523 def ls_dir(directory):
2524  mglog.info('For your information, ls of '+directory+':')
2525  mglog.info( sorted( os.listdir( directory ) ) )
2526 
2527 # Final import of some code used in these functions
2528 import MadGraphControl.MadGraphSystematicsUtils
2529 
2530 # To be removed once we moved past MG5 3.3.1
2531 def fix_fks_makefile(process_dir):
2532  makefile_fks=process_dir+'/SubProcesses/makefile_fks_dir'
2533  mglog.info('Fixing '+makefile_fks)
2534  shutil.move(makefile_fks,makefile_fks+'_orig')
2535  fin=open(makefile_fks+'_orig')
2536  fout=open(makefile_fks,'w')
2537  edit=False
2538  for line in fin:
2539  if 'FKSParams.mod' in line:
2540  fout.write(line.replace('FKSParams.mod','FKSParams.o'))
2541  edit=True
2542  elif edit and 'driver_mintFO' in line:
2543  fout.write('driver_mintFO.o: weight_lines.o mint_module.o FKSParams.o\n')
2544  elif edit and 'genps_fks.o' in line:
2545  fout.write('genps_fks.o: mint_module.o FKSParams.o\n')
2546  elif edit and 'test_soft_col_limits' in line:
2547  fout.write(line)
2548  fout.write('madfks_plot.o: mint_module.o\n')
2549  fout.write('cluster.o: weight_lines.o\n')
2550  else:
2551  fout.write(line)
2552  fin.close()
2553  fout.close()
python.MadGraphUtils.check_reset_proc_number
def check_reset_proc_number(opts)
Definition: MadGraphUtils.py:2512
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
python.MadGraphUtils.is_NLO_run
def is_NLO_run(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:2426
max
#define max(a, b)
Definition: cfImp.cxx:41
vtune_athena.format
format
Definition: vtune_athena.py:14
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.MadGraphUtilsHelpers.checkSettingIsTrue
def checkSettingIsTrue(key_, mydict_)
Definition: MadGraphUtilsHelpers.py:43
python.MadGraphParamHelpers.do_PMG_updates
def do_PMG_updates(process_dir)
Definition: MadGraphParamHelpers.py:144
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:2022
python.MadGraphUtils.arrange_output
def arrange_output(process_dir=MADGRAPH_GRIDPACK_LOCATION, lhe_version=None, saveProcDir=False, runArgs=None, fixEventWeightsForBridgeMode=False)
Definition: MadGraphUtils.py:1250
python.MadGraphUtilsHelpers.get_runArgs_info
def get_runArgs_info(runArgs)
Definition: MadGraphUtilsHelpers.py:96
python.MadGraphUtils.setupLHAPDF
def setupLHAPDF(process_dir=None, extlhapath=None, allow_links=True)
Definition: MadGraphUtils.py:886
python.MadGraphUtils.get_LHAPDF_PATHS
def get_LHAPDF_PATHS()
Definition: MadGraphUtils.py:835
python.MadGraphUtils.ls_dir
def ls_dir(directory)
Definition: MadGraphUtils.py:2523
python.MadGraphUtils.remap_lhe_pdgids
def remap_lhe_pdgids(lhe_file_old, lhe_file_new=None, pdgid_map={}, delete_old_lhe=True)
Definition: MadGraphUtils.py:1940
python.MadGraphUtilsHelpers.checkSetting
def checkSetting(key_, value_, mydict_)
Definition: MadGraphUtilsHelpers.py:35
python.MadGraphUtils.resetLHAPDF
def resetLHAPDF(origLHAPATH='', origLHAPDF_DATA_PATH='')
Definition: MadGraphUtils.py:1046
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:1834
python.MadGraphParamHelpers.check_PMG_updates
def check_PMG_updates(process_dir)
Definition: MadGraphParamHelpers.py:154
python.MadGraphUtils.get_default_config_card
def get_default_config_card(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:2394
python.MadGraphUtils.new_process
def new_process(process='generate p p > t t~\noutput -f', plugin=None, keepJpegs=False, usePMGSettings=False)
Definition: MadGraphUtils.py:216
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:2275
python.MadGraphUtils.setup_path_protection
def setup_path_protection()
Definition: MadGraphUtils.py:47
python.MadGraphUtils.SUSY_Generation
def SUSY_Generation(runArgs=None, process=None, plugin=None, syst_mod=None, keepOutput=False, param_card=None, writeGridpack=False, madspin_card=None, run_settings={}, params={}, fixEventWeightsForBridgeMode=False, add_lifetimes_lhe=False, usePMGSettings=True)
Definition: MadGraphUtils.py:1779
python.MadGraphUtils.check_reweight_card
def check_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1618
python.MadGraphUtils.config_only_check
def config_only_check()
Definition: MadGraphUtils.py:67
python.MadGraphUtils.get_default_runcard
def get_default_runcard(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:353
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.MadGraphUtils.write_test_script
def write_test_script()
Definition: MadGraphUtils.py:198
python.MadGraphUtils.error_check
def error_check(errors_a, return_code)
Definition: MadGraphUtils.py:103
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:385
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:2337
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename T::value_type > sorted(T begin, T end)
Helper function to create a sorted vector from an unsorted one.
python.MadGraphUtils.is_gen_from_gridpack
def is_gen_from_gridpack()
Definition: MadGraphUtils.py:2382
python.MadGraphUtils.get_lhapdf_id_and_name
def get_lhapdf_id_and_name(pdf)
Definition: MadGraphUtils.py:852
min
#define min(a, b)
Definition: cfImp.cxx:40
python.MadGraphUtils.get_expected_systematic_names
def get_expected_systematic_names(syst_setting)
Definition: MadGraphUtils.py:1544
python.MadGraphUtils.add_madspin
def add_madspin(madspin_card=None, process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1105
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:78
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:2170
python.MadGraphUtils.setup_bias_module
def setup_bias_module(bias_module, process_dir)
Definition: MadGraphUtils.py:1562
python.MadGraphUtils.run_card_consistency_check
def run_card_consistency_check(isNLO=False, process_dir='.')
Definition: MadGraphUtils.py:2433
python.MadGraphUtils.setupFastjet
def setupFastjet(process_dir=None)
Definition: MadGraphUtils.py:800
generate
Definition: generate.cxx:28
python.MadGraphUtilsHelpers.checkSettingExists
def checkSettingExists(key_, mydict_)
Definition: MadGraphUtilsHelpers.py:50
Trk::open
@ open
Definition: BinningType.h:40
python.MadGraphUtils.fix_fks_makefile
def fix_fks_makefile(process_dir)
Definition: MadGraphUtils.py:2531
python.MadGraphUtils.setNCores
def setNCores(process_dir, Ncores=None)
Definition: MadGraphUtils.py:1033
python.MadGraphUtils.generate_from_gridpack
def generate_from_gridpack(runArgs=None, extlhapath=None, gridpack_compile=None, requirePMGSettings=False)
Definition: MadGraphUtils.py:598
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
python.MadGraphUtils.get_expected_reweight_names
def get_expected_reweight_names(reweight_card_loc)
Definition: MadGraphUtils.py:1528
python.MadGraphUtils.get_mg5_executable
def get_mg5_executable()
Definition: MadGraphUtils.py:1054
python.MadGraphUtils.get_cluster_type
def get_cluster_type(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:2415
str
Definition: BTagTrackIpAccessor.cxx:11
python.MadGraphUtils.get_reweight_card
def get_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1612
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:790
python.MadGraphUtils.madspin_on_lhe
def madspin_on_lhe(input_LHE, madspin_card, runArgs=None, keep_original=False)
Definition: MadGraphUtils.py:1162
python.MadGraphUtils.get_SUSY_variations
def get_SUSY_variations(process, masses, syst_mod, ktdurham=None)
Definition: MadGraphUtils.py:1681
python.MadGraphUtils.print_cards_from_dir
def print_cards_from_dir(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:2331
python.MadGraphUtils.add_reweighting
def add_reweighting(run_name, reweight_card=None, process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:2500
python.MadGraphUtils.stack_subprocess
def stack_subprocess(command, **kwargs)
Definition: MadGraphUtils.py:41
python.MadGraphUtils.add_lifetimes
def add_lifetimes(process_dir, threshold=None)
Definition: MadGraphUtils.py:1061
python.MadGraphUtils.find_key_and_update
def find_key_and_update(akey, dictionary)
Definition: MadGraphUtils.py:2007
python.MadGraphUtils.SUSY_process
def SUSY_process(process='')
Definition: MadGraphUtils.py:1749
readCCLHist.float
float
Definition: readCCLHist.py:83
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
python.MadGraphUtilsHelpers.get_physics_short
def get_physics_short()
Definition: MadGraphUtilsHelpers.py:109
python.MadGraphUtilsHelpers.getDictFromCard
def getDictFromCard(card_loc, lowercase=False)
Definition: MadGraphUtilsHelpers.py:5
python.MadGraphUtilsHelpers.is_version_or_newer
def is_version_or_newer(args)
Definition: MadGraphUtilsHelpers.py:57
python.MadGraphUtils.get_LHAPDF_DATA_PATH
def get_LHAPDF_DATA_PATH()
Definition: MadGraphUtils.py:831
python.MadGraphUtils.helpful_SUSY_definitions
def helpful_SUSY_definitions()
Definition: MadGraphUtils.py:1654