11import os,time,subprocess,glob,re,sys
12from AthenaCommon
import Logging
13from MadGraphControl.MadGraphUtilsHelpers
import error_check,modify_param_card
14from MadGraphControl.MadGraphParamHelpers
import do_PMG_updates
15from MadGraphControl.MadGraphSystematicsUtils
import convertSysCalcArguments,get_pdf_and_systematic_settings,parse_systematics_arguments,SYSTEMATICS_WEIGHT_INFO_ALTDYNSCALES,SYSTEMATICS_WEIGHT_INFO,write_systematics_arguments
17mglog = Logging.logging.getLogger(
'MadGraphUtils')
22MADGRAPH_GRIDPACK_LOCATION =
'madevent'
24MADGRAPH_RUN_NAME =
'run_01'
26MADGRAPH_CATCH_ERRORS =
True
28MADGRAPH_PDFSETTING =
None
34MADGRAPH_DEVICES =
None
37 def __init__(self, process='generate p p > t t~\noutput -f', plugin=None, keepJpegs=False, usePMGSettings=False):
38 """ Generate a new process in madgraph.
39 Pass a process string.
40 Optionally request JPEGs to be kept and request for PMG settings to be used in the param card
41 Return the name of the process directory.
43 self.
mglog = Logging.logging.getLogger(
'MadGraphUtils')
57 card_loc =
'proc_card_mg5.dat'
58 mglog.info(
'Writing process card to '+card_loc)
59 a_card = open( card_loc ,
'w' )
60 for l
in process.split(
'\n'):
66 if '-nojpeg' not in l
and not keepJpegs:
69 outline = outline.split(
'#')[0]+
' -nojpeg #'+outline.split(
'#')[1]
71 outline = outline +
' -nojpeg'
73 if MADGRAPH_DEVICES
is not None:
74 if MADGRAPH_DEVICES.lower()
in [
'madevent_simd',
'madevent_gpu']:
75 outline =
'output '+MADGRAPH_DEVICES.lower()+
' '+outline.split(
'output')[1]
76 elif MADGRAPH_DEVICES.lower() ==
'max':
77 self.
mglog.warning(
'Not fully implemented yet; setting avx')
78 outline =
'output madevent_simd '+outline.split(
'output')[1]
79 a_card.write(outline+
'\n')
82 madpath = os.environ[
'MADPATH']
89 for l
in process.split(
'\n'):
91 if 'output' not in l.split(
'#')[0].
split():
94 tmplist = l.split(
'#')[0].
split(
' -')[0]
96 if len(tmplist.split())==2:
97 process_dir = tmplist.split()[1]
99 elif len(tmplist.split())==3:
100 process_dir = tmplist.split()[2]
103 mglog.info(
'Saw that you asked for a special output directory: '+str(process_dir))
106 mglog.info(
'Started process generation at '+str(time.asctime()))
108 plugin_cmd =
'--mode='+plugin
if plugin
is not None else ''
113 self.
MADGRAPH_COMMAND_STACK += [
' '.join([python,madpath+
'/bin/mg5_aMC '+plugin_cmd+
' << EOF\n'+process+
'\nEOF\n'])]
114 generate = subprocess.Popen([python,madpath+
'/bin/mg5_aMC',plugin_cmd,card_loc],stdin=subprocess.PIPE,stderr=subprocess.PIPE
if MADGRAPH_CATCH_ERRORS
else None)
115 (out,err) = generate.communicate()
116 error_check(err,generate.returncode)
118 mglog.info(
'Finished process generation at '+str(time.asctime()))
121 if process_dir ==
'':
122 for adir
in sorted(glob.glob( os.getcwd()+
'/*PROC*' ),reverse=
True):
123 if os.access(
'%s/SubProcesses/subproc.mg'%adir,os.R_OK):
127 mglog.warning(
'Additional possible process directory, '+adir+
' found. Had '+process_dir)
128 mglog.warning(
'Likely this is because you did not run from a clean directory, and this may cause errors later.')
130 if not os.access(
'%s/SubProcesses/subproc.mg'%process_dir,os.R_OK):
131 raise RuntimeError(
'No diagrams for this process in user-define dir='+str(process_dir))
133 raise RuntimeError(
'No diagrams for this process from list: '+str(sorted(glob.glob(os.getcwd()+
'/*PROC*'),reverse=
True)))
147 mglog.info(
'Setting default sde_strategy to old default (1)')
154 if MADGRAPH_DEVICES
is not None:
155 if MADGRAPH_DEVICES.lower()==
'madevent_simd':
157 elif MADGRAPH_DEVICES.lower()==
'madevent_gpu':
161 os.environ[
'ALLOW_UNSUPPORTED_COMPILER_IN_CUDA'] =
'Y'
162 elif MADGRAPH_DEVICES.lower() ==
'max':
163 self.
mglog.warning(
'Not fully implemented yet; setting avx')
174 if 'PYTHONPATH' in os.environ:
175 if not any( [(
'Generators/madgraph/models' in x)
for x
in os.environ[
'PYTHONPATH'].
split(
':') ]):
176 os.environ[
'PYTHONPATH'] +=
':/cvmfs/atlas.cern.ch/repo/sw/Generators/madgraph/models/latest'
177 self.
MADGRAPH_COMMAND_STACK += [
'export PYTHONPATH=${PYTHONPATH}:/cvmfs/atlas.cern.ch/repo/sw/Generators/madgraph/models/latest']
179 if 'GFORTRAN_TMPDIR' in os.environ:
181 if 'TMPDIR' in os.environ:
182 os.environ[
'GFORTRAN_TMPDIR']=os.environ[
'TMPDIR']
185 if 'TMP' in os.environ:
186 os.environ[
'GFORTRAN_TMPDIR']=os.environ[
'TMP']
190 """Builds a dictionary from the run card.
191 This function takes in the card location and saves the contents as a dictionary object in the MGControl class.
193 run_card = self.
process_dir +
'/Cards/run_card.dat'
195 if os.access(run_card,os.R_OK):
196 mglog.info(
'Copying default run_card.dat from '+str(run_card))
198 run_card = self.
process_dir+
'/Cards/run_card_default.dat'
199 if os.access(run_card,os.R_OK):
200 mglog.info(
'Copying default run_card.dat from '+str(run_card))
202 raise RuntimeError(
'Cannot find default run_card.dat or run_card_default.dat! I was looking here: %s'%run_card)
204 card = open(run_card)
206 for line
in iter(card):
207 if not line.strip().startswith(
'#'):
208 command = line.split(
'!', 1)[0]
210 setting = command.split(
'=')[-1].
strip()
211 value =
'='.join(command.split(
'=')[:-1]).
strip()
213 value = value.lower()
214 setting = setting.lower()
220 """Gets the config card location and determines if the process is LO or NLO
221 This function takes in the process diectory as an input and uses it to find the configuration.
222 Using the path to the config path, we can determine if the process will require a LO or NLO configuration.
226 lo_config_card = self.
process_dir+
'/Cards/me5_configuration.txt'
227 nlo_config_card = self.
process_dir+
'/Cards/amcatnlo_configuration.txt'
229 if os.access(lo_config_card,os.R_OK)
and not os.access(nlo_config_card,os.R_OK):
232 elif os.access(nlo_config_card,os.R_OK)
and not os.access(lo_config_card,os.R_OK):
235 elif os.access(nlo_config_card,os.R_OK)
and os.access(lo_config_card,os.R_OK):
236 mglog.error(
'Found both types of config card in '+str(self.
process_dir))
237 raise RuntimeError(
'Unable to locate configuration card')
239 mglog.error(
'No config card in '+str(self.
process_dir))
240 raise RuntimeError(
'Unable to locate configuration card')
244 """Builds a dictionary from the config card.
245 This function creates a dictionary object configCardDict from the config card.
246 Using the config card location, we copy over th settings to the dictionary.
247 Note: This function is works in the same way as self.getRunCardDict() however with small changes based on how the card is written.
249 card = open(card_loc)
252 for line
in iter(card):
253 if not line.strip().startswith(
'#'):
254 command = line.split(
'!', 1)[0]
257 value = command.split(
'=')[-1].
strip()
258 setting =
'='.join(command.split(
'=')[:-1]).
strip()
261 value = value.lower()
262 setting = setting.lower()
268 """This function gets the beam energy and random seed from the runArguments
272 raise RuntimeError(
'runArgs must be provided!')
274 if hasattr(runArgs,
'ecmEnergy'):
277 raise RuntimeError(
"No center of mass energy found in runArgs.")
279 if hasattr(runArgs,
'randomSeed'):
282 raise RuntimeError(
"No random seed found in runArgs.")
286 """This function adds run arguments to the self.runCardDict.
287 If the runArgs argument is left blank, the function will get the runArgs information before adding to the dictionary
289 if runArgs
is not None:
298 raise RuntimeError(
'Do not set beamenergy in the run card. Use runArgs instead.')
307 """Build a new run_card.dat from a run card dictionary.
308 This function can get a fresh run card from the runCardDict object.
309 Before writing the dictionary to the run card, we require to check a few things first
323 if runArgs
is not None and hasattr(runArgs,
'jobConfig'):
324 cfgdir = runArgs.jobConfig[0]
if isinstance(runArgs.jobConfig, (list, tuple))
else runArgs.jobConfig
326 full_path = os.path.join(cfgdir, raw_name)
327 self.
runCardDict[
'custom_fcts'] = os.path.abspath(full_path)
328 mglog.info(f
"Using custom function(s), specified in custom_fcts with path: {self.runCardDict['custom_fcts']}")
331 self.
runCardDict[
'custom_fcts'] = os.path.abspath(raw_name)
334 runCard_old = self.
process_dir+
'/Cards/run_card.dat.old_to_be_deleted'
335 os.rename(self.
process_dir+
'/Cards/run_card.dat', runCard_old)
341 with open(runCard_old)
as oldCard, open(self.
process_dir+
'/Cards/run_card.dat',
'w')
as newCard:
342 for line
in iter(oldCard):
344 if line.strip().startswith(
'#'):
347 command= line.split(
'!',1)[0]
348 if len(line.split(
'!',1)) > 1:
349 comment= line.split(
'!',1)[1]
353 setting = command.split(
'=')[-1].
strip()
356 newCard.write(
' '+str(self.
runCardDict[setting])+
' = '+str(setting)+
' ! '+ comment)
357 listSettings.append(str(setting))
359 raise RuntimeError(
'Could not find '+str(setting)+
' in the Run Card Dictionary!')
363 newCard.write(
"""#***********************************************************************
364# Any Additional settings can be added here *
365#***********************************************************************
370 if setting
not in listSettings:
371 newCard.write(
' '+str(self.
runCardDict[setting])+
' = '+str(setting)+
'\n')
380 mglog.info(
'Finished writing to run card.')
381 os.unlink(runCard_old)
384 """Build a new configuration from a config card dictionary.
385 This function can get a fresh runcard from the configCardDict object.
386 This function behaves similaraly to self.write_runCard()
388 mglog.info(
'Writing config card in '+self.
process_dir)
391 config_pathOLD = self.
config_path+
'.old_to_be_deleted'
399 mglog.info(
'Writing option '+setting+
' to the config card. Adding a setting to '+str(self.
configCardDict[setting]))
400 newCard.write(
' '+str(setting)+
' = '+str(self.
configCardDict[setting])+
'\n')
405 mglog.info(
'Finished writing to config card.')
407 os.unlink(config_pathOLD)
412 """This function checks that the casing in the run card dictionary is the same as the default run card.
413 It checks if the default setting appears, with the correct casing, in the updated card
414 If it isn't in the run card, if then checks if the default setting (in lower case) appears in the lowered (updated) card
415 Assuming that any inconsistencies have just lowered the casing of the setting, the function then attempts to resolve the inconsistency
420 lower_card = [key.lower()
for key
in self.
runCardDict]
428 if default_setting
in temp_run_card:
430 elif default_setting.lower()
in lower_card:
431 mglog.warning(f
"The casing in the run card seems to be wrong for {default_setting}. We will try fix this now.")
434 temp_run_card[default_setting] = temp_run_card[default_setting.lower()]
435 temp_run_card.pop(default_setting.lower())
438 raise RuntimeError(
"Run Card Dictionary casing is inconsistent")
443 mglog.info(
'Run card casing looks good!')
446 """Checks the consistency of runCardDict.
447 This function should be called before writing runCardDict to disk to ensure that the run card is consistent and has appropriate settings.
455 mglog.warning(
"setting event_norm to average, there is basically no use case where event_norm=sum is a good idea")
460 log=
'Bad combination of settings for CKKW-L merging! ktdurham=%s and ickkw=%s.'%(self.
runCardDict[
'ktdurham'],self.
runCardDict[
'ickkw'])
462 raise RuntimeError(log)
465 if 'systematics_program' not in self.
runCardDict or self.
runCardDict[
'systematics_program']==
'systematics':
466 syscalc_settings = [
'sys_pdf',
'sys_scalefact',
'sys_alpsfact',
'sys_matchscale']
467 found_syscalc_setting =
False
468 for s
in syscalc_settings:
470 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)')
471 found_syscalc_setting =
True
472 if found_syscalc_setting:
473 syst_arguments = convertSysCalcArguments(self.
runCardDict)
474 mglog.info(
'Converted syscalc arguments to systematics arguments: '+syst_arguments)
475 syst_settings_update = {
'systematics_arguments':syst_arguments}
476 for s
in syscalc_settings:
477 syst_settings_update[s] =
None
481 mglog.info(
'Checking PDF and systematics settings')
484 syst_settings = get_pdf_and_systematic_settings(MADGRAPH_PDFSETTING,self.
isNLO)
488 systematics_arguments = parse_systematics_arguments(self.
runCardDict[
'systematics_arguments'])
489 if 'weight_info' not in systematics_arguments:
490 mglog.info(
'Enforcing systematic weight name convention')
492 if '--dyn' in systematics_arguments
or ' dyn' in systematics_arguments:
493 if '--dyn' in systematics_arguments:
494 dyn = systematics_arguments.split(
'--dyn')[1]
495 if ' dyn' in systematics_arguments:
496 dyn = systematics_arguments.split(
' dyn')[1]
498 if dyn
is not None and len(dyn.split(
','))>1:
499 systematics_arguments[
'weight_info'] = SYSTEMATICS_WEIGHT_INFO_ALTDYNSCALES
501 systematics_arguments[
'weight_info'] = SYSTEMATICS_WEIGHT_INFO
502 self.
runCardDict[
'systematics_arguments'] = write_systematics_arguments(systematics_arguments)
506 mglog.warning(
'No python seed set in run_card -- adding one with same value as iseed')
514 with open(self.
process_dir+
'/Cards/proc_card_mg5.dat',
'r')
as file:
515 content = file.readlines()
517 for rawline
in content:
518 line = rawline.split(
'#')[0]
519 if line.startswith(
"define p"):
520 if (
'b' in line.split()
and 'b~' in line.split())
or (
'5' in line.split()
and '-5' in line.split()):
523 if 'j' in line.split()
and jet_5flav:
525 if line.startswith(
"define j"):
526 if (
'b' in line.split()
and 'b~' in line.split())
or (
'5' in line.split()
and '-5' in line.split()):
529 if 'p' in line.split()
and proton_5flav:
531 if proton_5flav
or jet_5flav:
532 FS_updates[
'asrwgtflavor'] = 5
535 mglog.warning(
'Found 5-flavour jets but 4-flavour proton. This is inconsistent - please pick one.')
536 mglog.warning(
'Will proceed assuming 5-flavour scheme.')
538 mglog.warning(
'Found 5-flavour protons but 4-flavour jets. This is inconsistent - please pick one.')
539 mglog.warning(
'Will proceed assuming 5-flavour scheme.')
541 FS_updates[
'asrwgtflavor'] = 4
543 if len(FS_updates)==0:
544 mglog.warning(f
'Could not identify 4- or 5-flavor scheme from process card {self.process_dir}/Cards/proc_card_mg5.dat')
548 if FS_updates[
'asrwgtflavor'] == 5:
552 mglog.warning(
'b and b~ included in p and j for 5-flavor scheme but run card settings are inconsistent; adjusting run card')
553 run_card_updates = {
'asrwgtflavor': 5,
'maxjetflavor': 5,
'pdgs_for_merging_cut':
'1, 2, 3, 4, 5, 21'}
556 modify_param_card(process_dir=self.
process_dir, params={
'MASS': {
'5':
'0.000000e+00'}})
558 mglog.debug(
'Consistent 5-flavor scheme setup detected.')
560 if FS_updates[
'asrwgtflavor'] == 4:
564 mglog.warning(
'b and b~ not included in p and j (4-flavor scheme) but run card settings are inconsistent; adjusting run card')
565 run_card_updates = {
'asrwgtflavor': 4,
'maxjetflavor': 4,
'pdgs_for_merging_cut':
'1, 2, 3, 4, 21'}
568 modify_param_card(process_dir=self.
process_dir, params={
'MASS': {
'5':
'4.700000e+00'}})
570 mglog.debug(
'Consistent 4-flavor scheme setup detected.')
573 if FS_updates[
'asrwgtflavor'] == 4:
575 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.')
576 run_card_updates = {
'asrwgtflavor': 4,
'maxjetflavor': 4,
'pdgs_for_merging_cut':
'1, 2, 3, 4, 21'}
578 modify_param_card(process_dir=self.
process_dir, params={
'MASS': {
'5':
'4.700000e+00'}})
579 elif FS_updates[
'asrwgtflavor'] == 5:
580 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.')
581 run_card_updates = {
'asrwgtflavor': 5,
'maxjetflavor': 5,
'pdgs_for_merging_cut':
'1, 2, 3, 4, 5, 21'}
583 modify_param_card(process_dir=self.
process_dir, params={
'MASS': {
'5':
'0.000000e+00'}})
585 mglog.info(
'Finished checking run card - All OK!')
596 if the_base_fragment
is None:
597 mglog.warning(
'!!! No pdf base fragment was included in your job options. PDFs should be set with an include file. You might be unable to follow the PDF4LHC uncertainty prescription. Let\'s hope you know what you doing !!!')
598 if not extras.get(
'pdlabel',
None) ==
'lhapdf' or 'lhaid' not in extras:
599 mglog.warning(
'!!! No pdf base fragment was included in your job options and you did not specify a LHAPDF yourself -- in the future, this will cause an error !!!')
605 correct_settings=get_pdf_and_systematic_settings(the_base_fragment,isNLO)
608 for s
in correct_settings:
609 if s
is None and s
in extras:
612 if s
not in extras
or extras[s]!=correct_settings[s]:
compare_runCardCasing(self)
write_runCard(self, runArgs=None)
get_runArgs_info(self, runArgs)
setup_path_protection(self)
base_fragment_setup_check(self, the_base_fragment, extras, isNLO)
__init__(self, process='generate p p > t t~\noutput -f', plugin=None, keepJpegs=False, usePMGSettings=False)
list MADGRAPH_COMMAND_STACK
add_runArgs(self, runArgs=None)
run_card_consistency_check(self)
getConfigFromPath(self, card_loc, lowercase=False)
getRunCardDict(self, lowercase=False)
std::string replace(std::string s, const std::string &s2, const std::string &s3)
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
std::vector< std::string > split(const std::string &s, const std::string &t=":")