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