diff --git a/bakeryHelpers.py b/bakeryHelpers.py index b78ac27..cf00c1d 100755 --- a/bakeryHelpers.py +++ b/bakeryHelpers.py @@ -7,130 +7,198 @@ .. moduleauthor:: Wolfgang Waltenberger """ -import numpy, sys, os, time, subprocess, glob +import numpy +import sys +import os +import time +import subprocess +import glob +import logging from os import PathLike -from typing import List, Union -sys.path.insert(0,"../../smodels" ) +from typing import List +import functools + +LOG = logging.getLogger(__name__) + +# TODO: investigate this... +sys.path.insert(0, "../../smodels") ## some global variables (currently only one) ## minimumNrOfEvents: set a minimum number of events requirement. # constants = { "minimumNrOfEvents": 29000 } # constants = { "minimumNrOfEvents": 5001 } -constants = { "minimumNrOfEvents": 1 } +constants = {"minimumNrOfEvents": 1} + def nCPUs(): - """ obtain the number of CPU cores on the machine, for several - platforms and python versions. """ + """obtain the number of CPU cores on the machine, for several + platforms and python versions.""" try: from smodels.tools.runtime import nCPUs as smodelsNCPUs + return smodelsNCPUs() except ImportError: pass try: import multiprocessing + return multiprocessing.cpu_count() except ImportError: pass try: import psutil + return psutil.NUM_CPUS except ImportError: pass try: import os - res = int(os.sysconf('SC_NPROCESSORS_ONLN')) - if res>0: return res + + res = int(os.sysconf("SC_NPROCESSORS_ONLN")) + if res > 0: + return res except ImportError: pass return None -def yesno ( boolean : bool ) -> str: + +def yesno(boolean: bool) -> str: if boolean: return "yes" return "no" -def mkdir ( dirname : PathLike ): - if not os.path.exists ( dirname ): - os.mkdir ( dirname ) -def getAge ( f : PathLike ) -> float: - """ get the age of file in hours. age goes by last modification """ - if not os.path.exists ( f ): - return 0. +def mkdir(dirname: PathLike): + if not os.path.exists(dirname): + os.mkdir(dirname) + + +def getAge(f: PathLike) -> float: + """get the age of file in hours. age goes by last modification""" + if not os.path.exists(f): + return 0.0 t0 = time.time() - mt = os.stat ( f ).st_mtime - dt = ( t0 - mt ) / 60. / 60. ## hours + mt = os.stat(f).st_mtime + dt = (t0 - mt) / 60.0 / 60.0 ## hours return dt -def safFile ( dirname : PathLike, topo : str, masses, sqrts ) -> str: - """ return saf file name """ - smass = "_".join ( map ( str, masses ) ) - ret = "%s/%s_%s.%d.saf" % ( dirname, topo, smass, sqrts ) - ret = ret.replace("//","/") + +def safFile(dirname: PathLike, topo: str, masses, sqrts) -> str: + """return saf file name""" + smass = "_".join(map(str, masses)) + ret = "%s/%s_%s.%d.saf" % (dirname, topo, smass, sqrts) + ret = ret.replace("//", "/") return ret -def datFile ( dirname, topo, masses, sqrts ): - """ return dat file name """ - smass = "_".join ( map ( str, masses ) ) - ret = "%s/%s_%s.%d.dat" % ( dirname, topo, smass, sqrts ) - ret = ret.replace("//","/") + +def datFile(dirname, topo, masses, sqrts): + """return dat file name""" + smass = "_".join(map(str, masses)) + ret = "%s/%s_%s.%d.dat" % (dirname, topo, smass, sqrts) + ret = ret.replace("//", "/") return ret -def isAssociateProduction ( topo ): - """ return true if topo is associate squark gluino production + +def isAssociateProduction(topo): + """return true if topo is associate squark gluino production :param topo: str, e.g. TGQ """ - if topo in [ "TGQ", "T3GQ", "T5GQ" ]: + if topo in ["TGQ", "T3GQ", "T5GQ"]: return True - if topo in [ "TChiWZ", "TChiWZoff", "THigWZ", "THigWZoff", "TChiWH", "TChiWHoff", \ - "THigWH", "THigWHoff" ]: + if topo in [ + "TChiWZ", + "TChiWZoff", + "THigWZ", + "THigWZoff", + "TChiWH", + "TChiWHoff", + "THigWH", + "THigWHoff", + ]: return True return False -def baseDir(): - """ our basedir """ +def contains_bake_py(path: str) -> bool: + bake_py_file = os.path.join(path, "bake.py") + if os.path.isfile(bake_py_file): + return True + return False + +@functools.cache +def baseDir() -> str: + """Returns path to em-creator repository.""" + result = None + # First try baking.conf conffile = "baking.conf" - if os.path.exists ( conffile ): - with open ( conffile, "rt" ) as f: - ret = f.read() - ret = ret.strip() - return ret - # ret = "/scratch-cbe/users/wolfgan.waltenberger/git/em-creator/" - subdir = "em-creator" - ret = "~/%s/" % subdir - ret = os.path.expanduser ( ret ) - if ret.count ( subdir ) == 2: - ret = ret.replace(subdir,"",1) - while ret.find("//")>0: - ret = ret.replace("//","/") - return ret + if os.path.exists(conffile): + LOG.debug(f"Using {conffile} to get base directory.") + with open(conffile, "rt") as f: + ret = f.read().strip() + if os.path.isdir(ret): + result = ret + else: + LOG.error(f"Got invalid base directory from {conffile}: {ret}") + + # FIXME: Maybe change priority with baking.conf? + # then look for env var + if not result: + result = os.environ.get("EM_CREATOR_BASE_DIR") + + # then try current working directory + cwd = os.getcwd() + if not result and contains_bake_py(cwd): + result = cwd + + # then try some default path + if not result: + subdir = "git/em-creator" + ret = os.path.join("~", subdir) + ret = os.path.expanduser(ret) + if ret.count(subdir) == 2: + ret = ret.replace(subdir, "", 1) + # if we fail, panic... + if not contains_bake_py(ret): + LOG.error("Could not get a valid base dir - please specify it with EM_CREATOR_BASE_DIR environment variable.") + sys.exit(-1) + + LOG.info("Using base dir: {result}") + return result def tempDir(): - """ our temp dir """ + """our temp dir""" ret = "./temp/" - #ret = baseDir()+"/temp/" - while ret.find("//")>0: - ret = ret.replace("//","/") - if not os.path.exists ( ret ): + # ret = baseDir()+"/temp/" + while ret.find("//") > 0: + ret = ret.replace("//", "/") + if not os.path.exists(ret): try: - os.mkdir ( ret ) - except FileExistsError as e: + os.mkdir(ret) + except FileExistsError: pass return ret -def dirName ( process, masses, basedir=None ): - """ the name of the directory of one process + masses + +def dirName(process, masses, basedir=None): + """the name of the directory of one process + masses :param process: e.g. T2_1jet :param masses: tuple or list of masses, e.g. (1000, 800) """ - filename = process + "." + "_".join(map(str,masses)) + filename = process + "." + "_".join(map(str, masses)) if basedir == None: return filename return basedir + "/" + filename -def parseMasses ( massstring, mingap1=None, maxgap1=None, - mingap2=None, maxgap2=None, mingap13=None, maxgap13=None ): - """ parse the mass string, e.g. (500,510,10),(100,110,10). keywords like "half" or "same" are accepted. + +def parseMasses( + massstring, + mingap1=None, + maxgap1=None, + mingap2=None, + maxgap2=None, + mingap13=None, + maxgap13=None, +): + """parse the mass string, e.g. (500,510,10),(100,110,10). keywords like "half" or "same" are accepted. :param mingap1: min mass gap between first and second particle, ignore if None. this is meant to force onshellness or a mass hierarchy :param maxgap1: max mass gap between second and third particle, ignore if None. @@ -146,296 +214,341 @@ def parseMasses ( massstring, mingap1=None, maxgap1=None, :returns: a list of all model points. E.g. [ (500,100),(510,100),(500,110),(510,110)]. """ try: - masses = eval ( massstring ) - except NameError as e: + masses = eval(massstring) + except NameError: masses = "" - if type(masses) not in [ list, tuple ] or len(masses)<2: + if type(masses) not in [list, tuple] or len(masses) < 2: mdefault = "(500,510,10),(100,110,10)" - print ( "[bakeryHelpers.py] Error: masses need to be given as e.g. %s (you will need to put it under parentheses)" % mdefault ) + print( + "[bakeryHelpers.py] Error: masses need to be given as e.g. %s (you will need to put it under parentheses)" + % mdefault + ) sys.exit() - lists=[] - for ctr,mtuple in enumerate(masses): ## tuple by tuple - tmp=[] - if type(mtuple) in [ str ]: ## descriptive strings + lists = [] + for ctr, mtuple in enumerate(masses): ## tuple by tuple + tmp = [] + if type(mtuple) in [str]: ## descriptive strings if mtuple == "half" and ctr == 1: - tmp.append ( mtuple ) - lists.append ( tuple(tmp) ) + tmp.append(mtuple) + lists.append(tuple(tmp)) continue elif "half" in mtuple and ctr == 1: - tmp.append ( mtuple ) - lists.append ( tuple(tmp) ) + tmp.append(mtuple) + lists.append(tuple(tmp)) continue elif mtuple == "same" and ctr == 1: - tmp.append ( mtuple ) - lists.append ( tuple(tmp) ) + tmp.append(mtuple) + lists.append(tuple(tmp)) continue # elif any(f"M0+{i}" in mtuple for i in range(5, 50, 1)) and ctr == 2: - elif mtuple.startswith("M0+") and ctr in [ 1, 2 ]: - tmp.append ( mtuple ) - lists.append ( tuple(tmp) ) + elif mtuple.startswith("M0+") and ctr in [1, 2]: + tmp.append(mtuple) + lists.append(tuple(tmp)) continue else: - print ( "[bakeryHelpers.py] error: i know only 'half' or 'same' for a string, and only in middle position. the only exception is 'M0+nb' in the third position when we have 4 mass tuples with nb in range(5,50,1) and M0 is the mass of the LSP, this can't be used with 'half' though" ) + print( + "[bakeryHelpers.py] error: i know only 'half' or 'same' for a string, and only in middle position. the only exception is 'M0+nb' in the third position when we have 4 mass tuples with nb in range(5,50,1) and M0 is the mass of the LSP, this can't be used with 'half' though" + ) sys.exit() - if type(mtuple) in [ int, float ]: - tmp.append ( mtuple ) - lists.append ( tuple(tmp) ) + if type(mtuple) in [int, float]: + tmp.append(mtuple) + lists.append(tuple(tmp)) continue if len(mtuple) == 1: - tmp.append ( mtuple[0] ) + tmp.append(mtuple[0]) continue if len(mtuple) == 2: - mtuple = ( mtuple[0], mtuple[1], 10 ) - for i in numpy.arange(mtuple[0],mtuple[1],mtuple[2] ): - tmp.append ( i ) - lists.append ( tuple(tmp) ) + mtuple = (mtuple[0], mtuple[1], 10) + for i in numpy.arange(mtuple[0], mtuple[1], mtuple[2]): + tmp.append(i) + lists.append(tuple(tmp)) # mesh = numpy.meshgrid ( *lists ) ret = [] if len(lists[1]) == 0: - print ( "[bakeryHelpers] no daughter masses found. you sure you specified a meaningful mass array?" ) + print( + "[bakeryHelpers] no daughter masses found. you sure you specified a meaningful mass array?" + ) sys.exit(-1) - if lists[1][0]=="half": - for x in lists[0]: - if len(lists)==2: - y=int(.5*x) - ret.append ( (int(x),y) ) + if lists[1][0] == "half": + for x in lists[0]: + if len(lists) == 2: + y = int(0.5 * x) + ret.append((int(x), y)) else: for z in lists[2]: - y=int(.5*x+.5*z) - ret.append ( (int(x),y,int(z)) ) - elif type(lists[1][0]) in [ str ] and "half" in lists[1][0]: - for x in lists[0]: - expr = lists[1][0].replace("half",f"{.5*x}" ) - if len(lists)==2: - y=int(eval(expr)) - ret.append ( (int(x),y) ) + y = int(0.5 * x + 0.5 * z) + ret.append((int(x), y, int(z))) + elif type(lists[1][0]) in [str] and "half" in lists[1][0]: + for x in lists[0]: + expr = lists[1][0].replace("half", f"{0.5 * x}") + if len(lists) == 2: + y = int(eval(expr)) + ret.append((int(x), y)) else: for z in lists[2]: - y=int(.5*x+.5*z) - ret.append ( (int(x),y,int(z)) ) - elif lists[1][0]=="same" and len(lists)<4: - for x in lists[0]: + y = int(0.5 * x + 0.5 * z) + ret.append((int(x), y, int(z))) + elif lists[1][0] == "same" and len(lists) < 4: + for x in lists[0]: for z in lists[2]: - ret.append ( (int(x),int(x),int(z)) ) - elif lists[1][0]=="same" and not isinstance(lists[2][0], str) and len(lists)==4: - for x in lists[0]: + ret.append((int(x), int(x), int(z))) + elif lists[1][0] == "same" and not isinstance(lists[2][0], str) and len(lists) == 4: + for x in lists[0]: for z in lists[2]: for k in lists[3]: - ret.append ( (int(x),int(x),int(z),int(k)) ) - elif lists[1][0]=="same" and any(f"M0+{i}" in lists[2][0] for i in range(5, 101, 1)) and len(lists)==4: + ret.append((int(x), int(x), int(z), int(k))) + elif ( + lists[1][0] == "same" + and any(f"M0+{i}" in lists[2][0] for i in range(5, 101, 1)) + and len(lists) == 4 + ): substrings = lists[2][0].split("+") - for x in lists[0]: - for k in lists[3]: - ret.append ( (int(x),int(x),int(k)+int(substrings[1]),int(k)) ) - elif len(lists)==2: - for x in range ( len(lists[0] ) ): - for y in range ( len(lists[1]) ): - ret.append ( (int(lists[0][x]),int(lists[1][y])) ) - elif type (lists[1][0])==str and any(f"M0+{i}" in lists[1][0] for i in range(5, 101, 1)) and len(lists)==3: + for x in lists[0]: + for k in lists[3]: + ret.append((int(x), int(x), int(k) + int(substrings[1]), int(k))) + elif len(lists) == 2: + for x in range(len(lists[0])): + for y in range(len(lists[1])): + ret.append((int(lists[0][x]), int(lists[1][y]))) + elif ( + type(lists[1][0]) == str + and any(f"M0+{i}" in lists[1][0] for i in range(5, 101, 1)) + and len(lists) == 3 + ): substrings = lists[1][0].split("+") for x in lists[0]: for k in lists[2]: - tmp = (int(x),int(k)+int(substrings[1]),int(k)) - ret.append ( tmp ) - elif len(lists)==3: - for x in range ( len(lists[0] ) ): - for y in range ( len(lists[1]) ): - for z in range ( len(lists[2]) ): - ret.append ( (int(lists[0][x]),int(lists[1][y]),int(lists[2][z])) ) - elif len(lists)==4 and not isinstance(lists[2][0], str): - for x in range ( len(lists[0] ) ): - for y in range ( len(lists[1]) ): - for z in range ( len(lists[2]) ): - for k in range ( len(lists[3]) ): - ret.append ( (int(lists[0][x]),int(lists[1][y]),int(lists[2][z]),int(lists[3][k])) ) - elif len(lists)==4 and any(f"M0+{i}" in lists[2][0] for i in range(5, 101, 1)): + tmp = (int(x), int(k) + int(substrings[1]), int(k)) + ret.append(tmp) + elif len(lists) == 3: + for x in range(len(lists[0])): + for y in range(len(lists[1])): + for z in range(len(lists[2])): + ret.append((int(lists[0][x]), int(lists[1][y]), int(lists[2][z]))) + elif len(lists) == 4 and not isinstance(lists[2][0], str): + for x in range(len(lists[0])): + for y in range(len(lists[1])): + for z in range(len(lists[2])): + for k in range(len(lists[3])): + ret.append( + ( + int(lists[0][x]), + int(lists[1][y]), + int(lists[2][z]), + int(lists[3][k]), + ) + ) + elif len(lists) == 4 and any(f"M0+{i}" in lists[2][0] for i in range(5, 101, 1)): substrings = lists[2][0].split("+") - for x in range ( len(lists[0] ) ): - for y in range ( len(lists[1]) ): - for k in range ( len(lists[3]) ): - ret.append ( (int(lists[0][x]),int(lists[1][y]),int(lists[3][k])+int(substrings[1]),int(lists[3][k])) ) - ret = filterForGap ( ret, mingap1, True, [0,1] ) - ret = filterForGap ( ret, mingap2, True, [1,2] ) - ret = filterForGap ( ret, mingap13, True, [0,2] ) - ret = filterForGap ( ret, maxgap1, False, [0,1] ) - ret = filterForGap ( ret, maxgap2, False, [1,2] ) - ret = filterForGap ( ret, maxgap13, False, [0,2] ) + for x in range(len(lists[0])): + for y in range(len(lists[1])): + for k in range(len(lists[3])): + ret.append( + ( + int(lists[0][x]), + int(lists[1][y]), + int(lists[3][k]) + int(substrings[1]), + int(lists[3][k]), + ) + ) + ret = filterForGap(ret, mingap1, True, [0, 1]) + ret = filterForGap(ret, mingap2, True, [1, 2]) + ret = filterForGap(ret, mingap13, True, [0, 2]) + ret = filterForGap(ret, maxgap1, False, [0, 1]) + ret = filterForGap(ret, maxgap2, False, [1, 2]) + ret = filterForGap(ret, maxgap13, False, [0, 2]) return ret -def filterForGap ( masses, gap, isMin=True, indices=[0,1] ): - """ filter out tuples for which gap is not met + +def filterForGap(masses, gap, isMin=True, indices=[0, 1]): + """filter out tuples for which gap is not met between particles :param isMin: if True, filter out too low gaps, if False, filter out too high gaps """ if gap == None: return masses - if len(masses)==0: - print ( f"[bakeryHelpers] empty mass list, check your constraints on the masses!" ) + if len(masses) == 0: + print("[bakeryHelpers] empty mass list, check your constraints on the masses!") sys.exit(-1) - if len(masses[0])<=max(indices): ## not enough masses + if len(masses[0]) <= max(indices): ## not enough masses return masses ret = [] for t in masses: - if isMin and t[ indices[0] ] > t[ indices[1] ]+ gap: - ret.append ( t ) - if not isMin and t[ indices[0] ] < t[ indices[1] ]+ gap: - ret.append ( t ) + if isMin and t[indices[0]] > t[indices[1]] + gap: + ret.append(t) + if not isMin and t[indices[0]] < t[indices[1]] + gap: + ret.append(t) return ret -def ma5AnaNameToSModelSName ( name ): - """ translate an analysis name from MA5 naming to - SModelS naming (atlas -> ATLAS, etc) """ - name = name.replace("atlas","ATLAS") - name = name.replace("cms","CMS") - name = name.replace("susy","SUSY") - name = name.replace("sus","SUS") - name = name.replace("exo","EXO") - name = name.replace("exot","EXOT") - name = name.replace("_","-") + +def ma5AnaNameToSModelSName(name): + """translate an analysis name from MA5 naming to + SModelS naming (atlas -> ATLAS, etc)""" + name = name.replace("atlas", "ATLAS") + name = name.replace("cms", "CMS") + name = name.replace("susy", "SUSY") + name = name.replace("sus", "SUS") + name = name.replace("exo", "EXO") + name = name.replace("exot", "EXOT") + name = name.replace("_", "-") return name -def cm2AnaNameToSModelSName ( name : str ) -> str: - """ translate an analysis name from checkmate2 naming to - SModelS naming (looking up arxiv ids) """ + +def cm2AnaNameToSModelSName(name: str) -> str: + """translate an analysis name from checkmate2 naming to + SModelS naming (looking up arxiv ids)""" if "," in name: names = name.split(",") - allnames = [ self.cm2AnaNameToSModelSName(n) for n in names ] + allnames = [self.cm2AnaNameToSModelSName(n) for n in names] return ",".join(allnames) transD = loadCM2DictionaryFile() if name in transD.keys(): return transD[name] f = name if "sus" in f or "exo" in f or "smp" in f or "conf" in f or "higg" in f: - f = f.upper().replace("_","-") + f = f.upper().replace("_", "-") return f -def sModelsName2cm2AnaName ( name : str ) -> str: - """ translate an analysis name from SModelS naming to - checkmate2 naming (looking up arxiv ids) """ + +def sModelsName2cm2AnaName(name: str) -> str: + """translate an analysis name from SModelS naming to + checkmate2 naming (looking up arxiv ids)""" if "," in name: names = name.split(",") - allnames = [ self.sModelsName2cm2AnaName(n) for n in names ] + allnames = [self.sModelsName2cm2AnaName(n) for n in names] return ",".join(allnames) transD = loadCM2DictionaryFile() - inverted = { v:k for k,v in transD.items() } + inverted = {v: k for k, v in transD.items()} """ additional entries, hand-written """ - inverted["ATLAS-SUSY-2018-22"] = 'atlas_2010_14293' + inverted["ATLAS-SUSY-2018-22"] = "atlas_2010_14293" if name in inverted.keys(): return inverted[name] - mname = name.upper().replace("_","-") + mname = name.upper().replace("_", "-") if mname in inverted.keys(): return inverted[mname] f = name if "SUS" in f or "EXO" in f or "SMP" in f or "CONF" in f or "HIGG" in f: - f = f.lower().replace("-","_") + f = f.lower().replace("-", "_") return f -def listAnalysesCutLang( ): - """ list the analyses that are available in cutlang """ + +def listAnalysesCutLang(): + """list the analyses that are available in cutlang""" dirname = "CutLang/ADLLHCanalyses/" - files = glob.glob ( f"{dirname}*" ) - print ( "List of ADL analyses:" ) - print ( "=====================" ) + files = glob.glob(f"{dirname}*") + print("List of ADL analyses:") + print("=====================") for f in files: - f = f.replace(dirname,"") + f = f.replace(dirname, "") if "README" in f: continue - print ( f ) + print(f) -def listAnalysesColliderbit( ): - """ list the analyses that are available in colliderbit """ + +def listAnalysesColliderbit(): + """list the analyses that are available in colliderbit""" import gambitWrapper - print ( "List of colliderbit analyses:" ) - print ( "=============================" ) + + print("List of colliderbit analyses:") + print("=============================") wrapper = gambitWrapper.GambitWrapper() wrapper.listAnalyses() -def listAnalyses ( cutlang : bool, checkmate : bool, - colliderbit : bool ): - """ list the analyses that are available in MA5 or cutlang """ + +def listAnalyses(cutlang: bool, checkmate: bool, colliderbit: bool): + """list the analyses that are available in MA5 or cutlang""" if cutlang: - listAnalysesCutLang( ) + listAnalysesCutLang() elif checkmate: - listAnalysesCheckMATE( ) + listAnalysesCheckMATE() elif colliderbit: listAnalysesColliderbit() else: - listAnalysesMA5( ) + listAnalysesMA5() + -def listAnalysesMA5( ): - """ list the analyses that are available in MA5 """ +def listAnalysesMA5(): + """list the analyses that are available in MA5""" import glob + # dname = "ma5/tools/PAD/Build/" - dn = [ "ma5/tools/PAD/Build/SampleAnalyzer/User/Analyzer/", - "ma5/tools/PADForMA5tune/Build/SampleAnalyzer/User/Analyzer/" ] + dn = [ + "ma5/tools/PAD/Build/SampleAnalyzer/User/Analyzer/", + "ma5/tools/PADForMA5tune/Build/SampleAnalyzer/User/Analyzer/", + ] # print ( "[bakeryHelpers] searching for analyses in %s" % dname ) files = [] for d in dn: - files += glob.glob ( "%s/*.cpp" % d ) - files = list ( set ( files ) ) + files += glob.glob("%s/*.cpp" % d) + files = list(set(files)) files.sort() - print ( "List of MA5 analyses:" ) - print ( "=====================" ) + print("List of MA5 analyses:") + print("=====================") for f in files: - f = f.replace(".saf","").replace(".cpp","") + f = f.replace(".saf", "").replace(".cpp", "") for d in dn: - f = f.replace(d,"") - print ( " %s" % f ) + f = f.replace(d, "") + print(" %s" % f) + def loadCM2DictionaryFile(): - """ load the checkmate2 <-> SModelS analyses names dictionary """ - if not os.path.exists ( "cm2names.dict" ): + """load the checkmate2 <-> SModelS analyses names dictionary""" + if not os.path.exists("cm2names.dict"): return {} - f=open("cm2names.dict") - D=eval(f.read()) + f = open("cm2names.dict") + D = eval(f.read()) f.close() return D -def listAnalysesCheckMATE( ): - """ list the analyses that are available in CheckMATE """ + +def listAnalysesCheckMATE(): + """list the analyses that are available in CheckMATE""" import glob + path = "cm2/checkmate2/tools/analysis/include/analyses/" - files = glob.glob ( f"{path}/*/*.h" ) - files = list ( set ( files ) ) + files = glob.glob(f"{path}/*/*.h") + files = list(set(files)) files.sort() transD = loadCM2DictionaryFile() - print ( "List of cm2 analyses:" ) - print ( "=====================" ) + print("List of cm2 analyses:") + print("=====================") cleaned = {} cm2Names = {} for f in files: - f = f.replace(".h","") - f = f.replace( path, "" ) + f = f.replace(".h", "") + f = f.replace(path, "") p = f.find("/") - f = f[p+1:] - nr = f.lower().replace("atlas","").replace("cms","") - nr = nr.replace("phys","").replace("conf","").replace("exo","") - nr = nr.replace( "higg", "" ).replace ("pas","").replace("pub_","") - nr = nr.replace("_","").replace("smp","").replace("susy","") - nr = nr.replace("atl","").replace("sus","").replace("-","") - newf = cm2AnaNameToSModelSName ( f ) + f = f[p + 1 :] + nr = f.lower().replace("atlas", "").replace("cms", "") + nr = nr.replace("phys", "").replace("conf", "").replace("exo", "") + nr = nr.replace("higg", "").replace("pas", "").replace("pub_", "") + nr = nr.replace("_", "").replace("smp", "").replace("susy", "") + nr = nr.replace("atl", "").replace("sus", "").replace("-", "") + newf = cm2AnaNameToSModelSName(f) cm2Names[nr] = f - cleaned[nr]=newf - cleanedkeys = list ( cleaned.keys() ) + cleaned[nr] = newf + cleanedkeys = list(cleaned.keys()) cleanedkeys.sort() import colorama + for k in cleanedkeys: cName = cleaned[k] oldName = "" if cm2Names[k] != cName: - oldName = " ("+cm2Names[k]+")" - col,res = "", "" + oldName = " (" + cm2Names[k] + ")" + col, res = "", "" if "SUS" in cName or "EXO" in cName: - col,res = colorama.Fore.GREEN, colorama.Fore.RESET + col, res = colorama.Fore.GREEN, colorama.Fore.RESET if "atlas" in cName: - col,res = colorama.Fore.RED, colorama.Fore.RESET - print ( f"{col} * {cName}{res}{oldName}" ) + col, res = colorama.Fore.RED, colorama.Fore.RESET + print(f"{col} * {cName}{res}{oldName}") + -def nJobs ( nproc, npoints ): - """ determine the number of jobs we should run, given nproc is - the user's input for number of processes, and npoints is the number - of points to be processed. """ +def nJobs(nproc, npoints): + """determine the number of jobs we should run, given nproc is + the user's input for number of processes, and npoints is the number + of points to be processed.""" ret = nproc if ret < 1: ret = nCPUs() + ret @@ -443,276 +556,299 @@ def nJobs ( nproc, npoints ): ret = npoints return ret -def getListOfCutlangMasses( topo, sqrts=13, ana=None ): - """ get a list of the masses of an cutlang scan. + +def getListOfCutlangMasses(topo, sqrts=13, ana=None): + """get a list of the masses of an cutlang scan. :param topo: e.g. T1 :param sqrts: sqrt(s) in tev """ - ret=[] + ret = [] sana = "*" if ana != None: - sana = ana.replace("_","-").upper() + sana = ana.replace("_", "-").upper() sana += "*" pattern = f"cutlang_results/{sana}/ANA_{topo}_*/output/*embaked" - files = glob.glob( pattern ) + files = glob.glob(pattern) for f in files: - fname = f.replace(".embaked","") + fname = f.replace(".embaked", "") p = f.rfind("mass_") - fname = fname[p+5:] + fname = fname[p + 5 :] tokens = fname.split("_") - tokens = tuple ( map ( int, tokens ) ) - if not tokens in ret: - ret.append ( tokens ) + tokens = tuple(map(int, tokens)) + if tokens not in ret: + ret.append(tokens) return ret -def getListOfMasses(topo, postMA5=False, sqrts=13, recaster=[], ana=None ): - """ get a list of the masses of an mg5 scan. to be used for e.g. ma5. + +def getListOfMasses(topo, postMA5=False, sqrts=13, recaster=[], ana=None): + """get a list of the masses of an mg5 scan. to be used for e.g. ma5. :param postMA5: query the ma5 output, not mg5 output. :param recaster: which recaster do we consider :param ana: analysis, if None, then all analyses """ ret = [] if "adl" in recaster: - ret += getListOfCutlangMasses ( topo, sqrts, ana ) + ret += getListOfCutlangMasses(topo, sqrts, ana) if "MA5" in recaster: - ret += getListOfMA5Masses ( topo, sqrts, ana ) + ret += getListOfMA5Masses(topo, sqrts, ana) if "cm2" in recaster: - ret += getListOfCm2Masses ( topo, sqrts, ana ) + ret += getListOfCm2Masses(topo, sqrts, ana) if "colliderbit" in recaster: - ret += getListOfColliderBitMasses ( topo, sqrts, ana ) + ret += getListOfColliderBitMasses(topo, sqrts, ana) return ret + def createSlurmLink(): - """ simple convenience method to create a symlink to slurm.py """ - for f in [ "slurm.py", "cancel_jobs.py" ]: - if not os.path.exists ( f ): - if os.path.exists ( f"{os.environ['HOME']}/{f}" ): + """simple convenience method to create a symlink to slurm.py""" + for f in ["slurm.py", "cancel_jobs.py"]: + if not os.path.exists(f): + if os.path.exists(f"{os.environ['HOME']}/{f}"): cmd = f"ln -s {os.environ['HOME']}/{f} ." - subprocess.getoutput ( cmd ) + subprocess.getoutput(cmd) -def getEmbakedName(analysisId : str, topo : str, recaster : str ) -> str: - """ Given analysis name, topology name, and the mass string, + +def getEmbakedName(analysisId: str, topo: str, recaster: str) -> str: + """Given analysis name, topology name, and the mass string, construct the name of the embaked file :param analysisId: the analysis name, e.g. ATLAS-SUSY-2018-22 :param topo: the topology name, e.g. T1 :param recaster: the recaster, either of MA5, adl, cm2 :returns: e.g. ATLAS-SUSY-2018-22.T5WW.cm2.embaked """ - retval = ".".join([analysisId.upper().replace("_", "-"), topo ]) + retval = ".".join([analysisId.upper().replace("_", "-"), topo]) retval = ".".join([retval, recaster, "embaked"]) retval = f"embaked/{retval}" return retval -def writeEmbaked ( effs : dict, effi_file : PathLike, masses, recaster : str ): - """ write our new efficiencies to the embaked file + +def writeEmbaked(effs: dict, effi_file: PathLike, masses, recaster: str): + """write our new efficiencies to the embaked file :param effs: the efficiencies, e.g. {"SR1":.5,"SR2":.25} :param effi_file: the embaked file, e.g. ATLAS-SUSY-2018-22.T5WW.cm2.embaked :param masses: the mass tuple, e.g. (500,200) :param recaster: the name of the recaster, MA5, adl, or cm2 """ - def lock ( lockfile ): - """ lock me """ - ctr=0 - while os.path.exists ( lockfile ): - ctr+=1 - if ctr>100: - os.unlink ( lockfile ) - time.sleep ( .2 ) - f = open ( lockfile, "wt" ) - f.write ( f"# locked {time.asctime()}\n" ) + + def lock(lockfile): + """lock me""" + ctr = 0 + while os.path.exists(lockfile): + ctr += 1 + if ctr > 100: + os.unlink(lockfile) + time.sleep(0.2) + f = open(lockfile, "wt") + f.write(f"# locked {time.asctime()}\n") f.close() - lockfile = effi_file+".lock" - if recaster not in [ "adl", "cm2", "MA5" ]: - print ( "[bakeryHelpers] error in {__line__} recaster {recaster} unknown." ) - print ( "[bakeryHelpers] we only know: adl, cm2, MA5" ) - if os.path.exists ( lockfile ): - os.unlink ( lockfile ) + lockfile = effi_file + ".lock" + if recaster not in ["adl", "cm2", "MA5"]: + print("[bakeryHelpers] error in {__line__} recaster {recaster} unknown.") + print("[bakeryHelpers] we only know: adl, cm2, MA5") + if os.path.exists(lockfile): + os.unlink(lockfile) sys.exit() - - if not os.path.exists ( "embaked" ): - os.mkdir ( "embaked" ) + + if not os.path.exists("embaked"): + os.mkdir("embaked") try: - lock ( lockfile ) - print ( f"[bakeryHelpers] adding point {masses} to {effi_file}" ) + lock(lockfile) + print(f"[bakeryHelpers] adding point {masses} to {effi_file}") previousEffs = {} - if os.path.exists ( effi_file ): - g = open ( effi_file, "rt" ) + if os.path.exists(effi_file): + g = open(effi_file, "rt") previousEffs = eval(g.read()) g.close() - previousEffs[masses]=effs + previousEffs[masses] = effs nregions = len(effs) npoints = len(previousEffs) - f = open ( effi_file, "wt" ) - f.write ( f"# EM-Baked {time.asctime()}. {npoints} points, {nregions} signal regions, checkmate2(direct)\n" ) - f.write( "{" ) - masses = list ( previousEffs.keys() ) + f = open(effi_file, "wt") + f.write( + f"# EM-Baked {time.asctime()}. {npoints} points, {nregions} signal regions, checkmate2(direct)\n" + ) + f.write("{") + masses = list(previousEffs.keys()) masses.sort() for m in masses: v = previousEffs[m] - f.write(str(m)+":"+str(v)+",\n") - f.write ( "}\n" ) + f.write(str(m) + ":" + str(v) + ",\n") + f.write("}\n") f.close() except Exception as e: - print ( f"[bakeryHelpers] Exception {e}" ) - if os.path.exists ( lockfile ): - os.unlink ( lockfile ) + print(f"[bakeryHelpers] Exception {e}") + if os.path.exists(lockfile): + os.unlink(lockfile) + -def getListOfMA5Masses ( topo, sqrts, ana ): +def getListOfMA5Masses(topo, sqrts, ana): dirname = "ma5results/" extension = "dat" - fname=f"{dirname}/{topo}_*.{extension}" - files = glob.glob( fname ) - ana = ana.lower().replace("-","_") - ret=[] + fname = f"{dirname}/{topo}_*.{extension}" + files = glob.glob(fname) + ana = ana.lower().replace("-", "_") + ret = [] for f in files: - with open ( f ) as handle: - txt= handle.read() - if not ana in txt: + with open(f) as handle: + txt = handle.read() + if ana not in txt: continue - f = f.replace( dirname, "" ) - f = f.replace( topo+"_", "" ) - f = f.replace( "."+extension, "" ) + f = f.replace(dirname, "") + f = f.replace(topo + "_", "") + f = f.replace("." + extension, "") p1 = f.find(".") f = f[:p1] - masses = tuple(map(int,map(float,f.split("_")))) - ret.append ( masses ) + masses = tuple(map(int, map(float, f.split("_")))) + ret.append(masses) return ret -def getListOfColliderBitMasses ( topo, sqrts, ana ): - """ get all collider bit masses """ - files = glob.glob ( f"gambit_results/{ana}.{topo}.*.eff" ) - ret=[] + +def getListOfColliderBitMasses(topo, sqrts, ana): + """get all collider bit masses""" + files = glob.glob(f"gambit_results/{ana}.{topo}.*.eff") + ret = [] for f in files: - tokens = f.split ( "." ) - masses = map ( int , tokens[2].split("_") ) - ret.append ( tuple(masses) ) + tokens = f.split(".") + masses = map(int, tokens[2].split("_")) + ret.append(tuple(masses)) return ret -def getListOfCm2Masses ( topo, sqrts, ana ): - """ FIXME need to get list of cm2 masses, from cm2results. - for now lets just list the ones in the embaked files """ + +def getListOfCm2Masses(topo, sqrts, ana): + """FIXME need to get list of cm2 masses, from cm2results. + for now lets just list the ones in the embaked files""" ret = [] return ret + def nRequiredMasses(topo): - """ find out how many masses a topology requires """ - M=set() - with open( f"{baseDir()}/templates/slha/{topo}_template.slha", "r" ) as f: + """find out how many masses a topology requires""" + M = set() + with open(f"{baseDir()}/templates/slha/{topo}_template.slha", "r") as f: for line in f.readlines(): p1 = line.find("#") if p1 > -1: line = line[:p1] - if not "M" in line: + if "M" not in line: continue p = line.find("M") - num=line[p+1] - if num not in list(map(str,range(6))): + num = line[p + 1] + if num not in list(map(str, range(6))): continue M.add(num) return len(M) -def clean (): - """ do the usual cleaning, but consider only files older than 2 hrs """ + +def clean(): + """do the usual cleaning, but consider only files older than 2 hrs""" t = tempDir() b = baseDir() files = [] - for i in [ "mg5cmd*", "mg5proc*", "tmp*slha", "run*card" ]: - pattern = f"{t}/{i}" - files += glob.glob ( pattern ) - for i in [ "recast*", "ma5cmd*" ]: + for i in ["mg5cmd*", "mg5proc*", "tmp*slha", "run*card"]: + pattern = f"{t}/{i}" + files += glob.glob(pattern) + for i in ["recast*", "ma5cmd*"]: pattern = f"{b}/ma5/{i}" - files += glob.glob ( pattern ) - for i in [ "ma5_T*" ]: - pattern = f"{b}/{i}" - files += glob.glob ( pattern ) - for i in [ "T*jet.*" ]: - pattern = f"{b}/{i}" - files += glob.glob ( pattern ) - files += glob.glob ( f"{b}/.lock*" ) - files += glob.glob ( f"{b}/../clip/_B*sh" ) - files += glob.glob ( f"{os.environ['HOME']}/B*sh" ) - files += glob.glob ( f"/scratch-cbe/users/{os.environ['USER']}/outputs/slurm*out" ) + files += glob.glob(pattern) + for i in ["ma5_T*"]: + pattern = f"{b}/{i}" + files += glob.glob(pattern) + for i in ["T*jet.*"]: + pattern = f"{b}/{i}" + files += glob.glob(pattern) + files += glob.glob(f"{b}/.lock*") + files += glob.glob(f"{b}/../clip/_B*sh") + files += glob.glob(f"{os.environ['HOME']}/B*sh") + files += glob.glob(f"/scratch-cbe/users/{os.environ['USER']}/outputs/slurm*out") cleaned = [] for f in files: - dt = getAge ( f ) - if dt < 5.: + dt = getAge(f) + if dt < 5.0: continue - subprocess.getoutput ( f"rm -rf {f}" ) - cleaned.append ( f ) - print ( f"[bakeryHelpers] Cleaned {len(cleaned)} temporary files" ) + subprocess.getoutput(f"rm -rf {f}") + cleaned.append(f) + print(f"[bakeryHelpers] Cleaned {len(cleaned)} temporary files") checkEventFiles() + def checkEventFiles(): - """ look at the event files, remove all that are old and cannot be opened """ + """look at the event files, remove all that are old and cannot be opened""" files = glob.glob("mg5results/T*hepmc.gz") for f in files: - dt = getAge ( f ) - if dt < 5.: + dt = getAge(f) + if dt < 5.0: continue - subprocess.getoutput ( f"rm {f}" ) - print ( f"{f}: {dt:.2f}h" ) + subprocess.getoutput(f"rm {f}") + print(f"{f}: {dt:.2f}h") + def cleanAll(): clean() b = baseDir() t = tempDir() files = [] - files += glob.glob ( "%s/*" % t ) - files += glob.glob ( "%s/T*jet*" % b ) - files += glob.glob ( "%s/ma5_T*jet*" % b ) - for i in [ "mg5cmd*", "mg5proc*", "tmp*slha", "run*card" ]: - files += glob.glob ( "%s/%s" % ( t, i ) ) - for i in [ "recast*", "ma5cmd*" ]: - files += glob.glob ( "%s/ma5/%s" % ( b, i ) ) - files += glob.glob ( "%s/.lock*" % b ) - files += glob.glob ( "%s/../clip/_B*sh" % b ) - files += glob.glob ( f"{os.environ['HOME']}/B*sh" ) - files += glob.glob ( f"/scratch-cbe/users/{os.environ['USER']}/outputs/slurm*out" ) + files += glob.glob("%s/*" % t) + files += glob.glob("%s/T*jet*" % b) + files += glob.glob("%s/ma5_T*jet*" % b) + for i in ["mg5cmd*", "mg5proc*", "tmp*slha", "run*card"]: + files += glob.glob("%s/%s" % (t, i)) + for i in ["recast*", "ma5cmd*"]: + files += glob.glob("%s/ma5/%s" % (b, i)) + files += glob.glob("%s/.lock*" % b) + files += glob.glob("%s/../clip/_B*sh" % b) + files += glob.glob(f"{os.environ['HOME']}/B*sh") + files += glob.glob(f"/scratch-cbe/users/{os.environ['USER']}/outputs/slurm*out") cleaned = [] for f in files: - dt = getAge ( f ) - #if dt < 0.: + dt = getAge(f) + # if dt < 0.: # continue - subprocess.getoutput ( f"rm -rf {f}" ) - cleaned.append ( f ) - print ( f"[bakeryHelpers] Cleaned {len(cleaned)} temporary files" ) + subprocess.getoutput(f"rm -rf {f}") + cleaned.append(f) + print(f"[bakeryHelpers] Cleaned {len(cleaned)} temporary files") -def rmLocksOlderThan ( hours=8 ): - """ remove all locks older than """ - files = glob.glob ( ".lock*" ) + +def rmLocksOlderThan(hours=8): + """remove all locks older than """ + files = glob.glob(".lock*") t = time.time() for f in files: try: ts = os.stat(f).st_mtime - dt = ( t - ts ) / 60. / 60. + dt = (t - ts) / 60.0 / 60.0 if dt > hours: - self.msg ( f"removing old lock {f} [{int(dt)} hrs old]" ) - subprocess.getoutput ( f"rm -f {f}" ) + self.msg(f"removing old lock {f} [{int(dt)} hrs old]") + subprocess.getoutput(f"rm -f {f}") except: pass + def nCPUs(): - """ obtain the number of *available* CPU cores on the machine, for several - platforms and python versions. """ + """obtain the number of *available* CPU cores on the machine, for several + platforms and python versions.""" try: # next few lines taken from # https://stackoverflow.comhttps//stackoverflow.com/questions/1006289/how-to-find-out-the-number-of-cpus-using-python/questions/1006289/how-to-find-out-the-number-of-cpus-using-python import re - with open('/proc/self/status') as f: - m = re.search(r'(?m)^Cpus_allowed:\s*(.*)$', f.read()) + + with open("/proc/self/status") as f: + m = re.search(r"(?m)^Cpus_allowed:\s*(.*)$", f.read()) if m: - res = bin(int(m.group(1).replace(',', ''), 16)).count('1') + res = bin(int(m.group(1).replace(",", ""), 16)).count("1") if res > 0: return res except IOError: pass -def execute( cmd:List[str], logfile:str=None, maxLength=100, cwd:str=None, - exit_on_fail=False ): - """ execute cmd in shell +def execute( + cmd: List[str], + logfile: str = None, + maxLength=100, + cwd: str = None, + exit_on_fail=False, +): + """execute cmd in shell :param maxLength: maximum length of output to be printed, if == -1 then all output will be printed :param cmd List of strings that make the command @@ -722,64 +858,75 @@ def execute( cmd:List[str], logfile:str=None, maxLength=100, cwd:str=None, :param exit_on_fail Whether to invoke sys.exit() on nonzero return value :return return value of the command """ - shell=False + shell = False scmd = " ".join(cmd) - if type(cmd)==str: - shell=True - scmd=cmd + if type(cmd) == str: + shell = True + scmd = cmd if cwd is None: directory = os.getcwd() else: directory = cwd - print(f'[bakeryHelpers:{directory}] {scmd}') - ctr=0 + print(f"[bakeryHelpers:{directory}] {scmd}") + ctr = 0 while ctr < 5: try: - proc = subprocess.Popen( cmd, cwd=cwd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, shell=shell ) + proc = subprocess.Popen( + cmd, + cwd=cwd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=shell, + ) for c in iter(lambda: proc.stdout.read(1), b""): sys.stdout.buffer.write(c) # # f.buffer.write(c) - #out, err = proc.communicate() - #print(out.decode('utf-8')) - #print(err.decode('utf-8')) + # out, err = proc.communicate() + # print(out.decode('utf-8')) + # print(err.decode('utf-8')) proc.wait() if logfile is not None: with open(logfile, "a") as log: - log.write(f'exec: {directory} $$ {scmd}') + log.write(f"exec: {directory} $$ {scmd}") if not (proc.returncode == 0): - print(f"[bakeryHelpers] Executed process: \n{scmd}\n\nin" - f" directory:\n{directory}\n\nproduced an error\n\n" - f"value {proc.returncode}.") + print( + f"[bakeryHelpers] Executed process: \n{scmd}\n\nin" + f" directory:\n{directory}\n\nproduced an error\n\n" + f"value {proc.returncode}." + ) if exit_on_fail is True: sys.exit() return proc.returncode - except BlockingIOError as e: - print( "[bakeryHelpers] ran into blocking io error. wait a bit then try again." ) - time.sleep ( random.uniform(1,10)+ctr*30 ) + except BlockingIOError: + print( + "[bakeryHelpers] ran into blocking io error. wait a bit then try again." + ) + time.sleep(random.uniform(1, 10) + ctr * 30) ctr += 1 sys.exit() -def checkDelphesInstall( installdir : PathLike = "delphes", - autocompile : bool = True ) -> bool: - """ check if we have a functioning delphes installation at - installdir + +def checkDelphesInstall( + installdir: PathLike = "delphes", autocompile: bool = True +) -> bool: + """check if we have a functioning delphes installation at + installdir :returns: True, if all is ok """ - if not os.path.isdir( installdir ): + if not os.path.isdir(installdir): print("[delphesInstaller] Delphes directory missing, download from github!") - if True: # self._confirmation("Download from github?"): - args = ['git', 'clone', '-b', '3.5.0', 'https://github.com/delphes/delphes'] - #args = ['git', 'clone', 'https://github.com/delphes/delphes'] + if True: # self._confirmation("Download from github?"): + args = ["git", "clone", "-b", "3.5.0", "https://github.com/delphes/delphes"] + # args = ['git', 'clone', 'https://github.com/delphes/delphes'] execute(args, exit_on_fail=True) - args = [ 'cp', 'templates/delphes_card_CMS.tcl', 'delphes/cards/' ] + args = ["cp", "templates/delphes_card_CMS.tcl", "delphes/cards/"] execute(args, exit_on_fail=True) else: print("[delphesInstaller] ERROR: No Delphes dir. Exiting.") # if there is no executable, compile it - delphes_exe = os.path.abspath( installdir + "/DelphesHepMC2") - if not os.path.exists( delphes_exe): - print(f"[delphesInstaller] Cannot find delphes installation at {delphes_exe}" ) + delphes_exe = os.path.abspath(installdir + "/DelphesHepMC2") + if not os.path.exists(delphes_exe): + print(f"[delphesInstaller] Cannot find delphes installation at {delphes_exe}") compile_path = os.path.abspath(installdir) # Check for existence of makefile, if not present exit, else make makefile_path = os.path.join(compile_path, "Makefile") @@ -787,40 +934,73 @@ def checkDelphesInstall( installdir : PathLike = "delphes", print("[delphesInstaller] No executable and no Makefile. Bailing out.") sys.exit() print("[delphesInstaller] Compiling Delphes...") - args = ['make'] + args = ["make"] if autocompile: - execute(args, cwd=compile_path, exit_on_fail=True ) + execute(args, cwd=compile_path, exit_on_fail=True) print("[delphesInstaller] Delphes initialised.") print("[delphesInstaller] Initialisation complete.") return delphes_exe + if __name__ == "__main__": import argparse - argparser = argparse.ArgumentParser(description='show the masses for a given mass string') - argparser.add_argument ( '-m', '--masses', help='masses', - type=str, default=None ) - argparser.add_argument ( '--maxgap2', help='maximum mass gap between second and third, to force offshell [None]', - type=float, default=None ) - argparser.add_argument ( '--mingap1', help='minimum mass gap between first and second, to force onshell or a mass hierarchy [None]', - type=float, default=None ) - argparser.add_argument ( '--mingap2', help='minimum mass gap between second and third, to force onshell or a mass hierarchy [None]', - type=float, default=None ) - argparser.add_argument ( '--mingap13', help='minimum mass gap between first and third, to force onshell or a mass hierarchy [None]', - type=float, default=None ) - argparser.add_argument ( '--maxgap13', help='maximum mass gap between first and third, to force offshell [None]', - type=float, default=None ) - argparser.add_argument ( '--maxgap1', help='maximum mass gap between first and second, to force offshell [None]', - type=float, default=None ) + + argparser = argparse.ArgumentParser( + description="show the masses for a given mass string" + ) + argparser.add_argument("-m", "--masses", help="masses", type=str, default=None) + argparser.add_argument( + "--maxgap2", + help="maximum mass gap between second and third, to force offshell [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--mingap1", + help="minimum mass gap between first and second, to force onshell or a mass hierarchy [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--mingap2", + help="minimum mass gap between second and third, to force onshell or a mass hierarchy [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--mingap13", + help="minimum mass gap between first and third, to force onshell or a mass hierarchy [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--maxgap13", + help="maximum mass gap between first and third, to force offshell [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--maxgap1", + help="maximum mass gap between first and second, to force offshell [None]", + type=float, + default=None, + ) args = argparser.parse_args() - masses=parseMasses ( args.masses, mingap1 = args.mingap1, maxgap1 = args.maxgap1, - mingap2 = args.mingap2, maxgap2 = args.maxgap2, mingap13 = args.mingap13, - maxgap13 = args.maxgap13 ) - print ( f"the input will produce {len(masses)} mass vectors:" ) - for c,m in enumerate ( masses ): - print ( f" {m}", end="" ) - if c%3==0: + masses = parseMasses( + args.masses, + mingap1=args.mingap1, + maxgap1=args.maxgap1, + mingap2=args.mingap2, + maxgap2=args.maxgap2, + mingap13=args.mingap13, + maxgap13=args.maxgap13, + ) + print(f"the input will produce {len(masses)} mass vectors:") + for c, m in enumerate(masses): + print(f" {m}", end="") + if c % 3 == 0: print() - if c == 30: - print ( " ..... " ) + if c == 30: + print(" ..... ") break print() diff --git a/mg5Wrapper.py b/mg5Wrapper.py index 6e4f1c3..eb6856e 100755 --- a/mg5Wrapper.py +++ b/mg5Wrapper.py @@ -8,16 +8,29 @@ .. moduleauthor:: Wolfgang Waltenberger """ -import os, sys, colorama, subprocess, shutil, tempfile, time, socket, random, ast +import os +import sys +import subprocess +import shutil +import tempfile +import time +import socket +import random from colorama import Fore as ansi -import multiprocessing, glob, io +import multiprocessing +import glob +import io import bakeryHelpers from bakeryHelpers import rmLocksOlderThan import locker from typing import Dict, List +import logging + +LOG = logging.getLogger(__name__) + class MG5Wrapper: - def __init__ ( self, args : Dict, recaster : List ): + def __init__(self, args: Dict, recaster: List): """ :param args: the command line args, as a dictionary :param recaster: perform recasting (ma5 or cutlang) @@ -26,15 +39,14 @@ def __init__ ( self, args : Dict, recaster : List ): self.nevents = args["nevents"] self.checkHost() self.basedir = bakeryHelpers.baseDir() - os.chdir ( self.basedir ) + os.chdir(self.basedir) self.tempdir = bakeryHelpers.tempDir() self.resultsdir = os.path.join(self.basedir, "mg5results") self.recast = args["recast"] self.adl_file = args["adl_file"] self.event_condition = args["event_condition"] - self.mkdir ( self.resultsdir ) - self.locker = locker.Locker ( args["sqrts"], args["topo"], - args["ignore_locks"] ) + self.mkdir(self.resultsdir) + self.locker = locker.Locker(args["sqrts"], args["topo"], args["ignore_locks"]) self.topo = args["topo"] self.keep = args["keep"] self.keephepmc = args["keephepmc"] @@ -46,117 +58,137 @@ def __init__ ( self, args : Dict, recaster : List ): self.tempf = None self.sqrts = args["sqrts"] self.recaster = recaster - self.pyver = 3 ## python version + self.pyver = 3 ## python version self.setMG5Version() if "py3" in self.ver: self.pyver = 3 - if not os.path.isdir ( self.mg5install ): - self.error ( "mg5 install is missing??" ) + if not os.path.isdir(self.mg5install): + self.error("mg5 install is missing??") self.executable = os.path.join(self.mg5install, "bin/mg5_aMC") - if not os.path.exists ( self.executable ): - self.info ( "cannot find mg5 installation at %s" % self.mg5install ) - if os.path.exists ( self.mg5install ): - subprocess.getoutput ( f"rm -rf {self.mg5install}" ) - self.info ( "cannot even find mg5/ directory." ) + if not os.path.exists(self.executable): + self.info("cannot find mg5 installation at %s" % self.mg5install) + if os.path.exists(self.mg5install): + subprocess.getoutput(f"rm -rf {self.mg5install}") + self.info("cannot even find mg5/ directory.") localbackup = f"{self.basedir}/mg5.backup/" backupdir = "/groups/hephy/pheno/ww/mg5" templatedir = f"{self.basedir}/mg5.template" destdir = f"{self.basedir}/mg5" - if os.path.exists ( localbackup ): - self.info ( f"local backup found at {localbackup}" ) - self.exe ( f"cp -r {localbackup} {destdir}" ) - elif os.path.exists ( backupdir ): - self.info ( f"NO local backup found at {localbackup}" ) - self.exe ( f"cp -r {backupdir} {destdir}" ) - self.info ( f"clip backup found at {backupdir}" ) - elif os.path.exists ( templatedir ): - self.info ( f"NO clip backup found at {backupdir}" ) - self.exe ( f"cp -r {templatedir} {destdir}" ) - self.info ( f"template backup found at {templatedir}" ) - self.exe ( "mg5/make.py" ) - if not os.path.exists ( f"{self.mg5install}/idm" ): - subprocess.getoutput ( f"cp -r idm {self.mg5install}" ) + if os.path.exists(localbackup): + self.info(f"local backup found at {localbackup}") + self.exe(f"cp -r {localbackup} {destdir}") + elif os.path.exists(backupdir): + self.info(f"NO local backup found at {localbackup}") + self.exe(f"cp -r {backupdir} {destdir}") + self.info(f"clip backup found at {backupdir}") + elif os.path.exists(templatedir): + self.info(f"NO clip backup found at {backupdir}") + self.exe(f"cp -r {templatedir} {destdir}") + self.info(f"template backup found at {templatedir}") + self.exe("mg5/make.py") + if not os.path.exists(f"{self.mg5install}/idm"): + subprocess.getoutput(f"cp -r idm {self.mg5install}") self.determineMG5Version() self.templateDir = os.path.join(self.basedir, "templates/") - ebeam = str(int(self.sqrts*1000/2)) + ebeam = str(int(self.sqrts * 1000 / 2)) ptlund = "-1" ptlund = "2." ktdurham = "-1." - self.mgParams = { 'EBEAM': ebeam, # Single Beam Energy expressed in GeV - 'NEVENTS': str(self.nevents), 'MAXJETFLAVOR': '5', + self.mgParams = { + "EBEAM": ebeam, # Single Beam Energy expressed in GeV + "NEVENTS": str(self.nevents), + "MAXJETFLAVOR": "5", # 'PDFLABEL': "'lhapdf'", 'XQCUT': '20', 'QCUT': '10', - 'PDFLABEL': "'nn23lo1'", 'XQCUT': 'M[0]/4', - 'PTLUND': ptlund, 'KTDURHAM': ktdurham, - ## xqcut for gluino-gluino production: mgluino/4 - }#,'qcut': '90'} + "PDFLABEL": "'nn23lo1'", + "XQCUT": "M[0]/4", + "PTLUND": ptlund, + "KTDURHAM": ktdurham, + ## xqcut for gluino-gluino production: mgluino/4 + } # ,'qcut': '90'} if "TChi" in self.topo or "THig" in self.topo: # for electroweakinos go lower in xqcut - self.mgParams["XQCUT"]="M[0]/6" - + self.mgParams["XQCUT"] = "M[0]/6" + self.correctPythia8CfgFile() - self.msg ( "remove potential old cruft" ) - rmLocksOlderThan ( 3 ) ## remove locks older than 3 hours + self.msg("remove potential old cruft") + rmLocksOlderThan(3) ## remove locks older than 3 hours from utils import rmOld + stats = rmOld.createStats() # rmOld.rmOlderThan ( stats, 8, False ) - self.info ( f"initialised MG5 {self.ver}" ) + self.info(f"initialised MG5 {self.ver}") - def setMG5Version ( self ): + def setMG5Version(self): self.ver = "???" versionfile = f"{self.mg5install}/VERSION" - if os.path.exists ( versionfile ): - with open ( versionfile, "rt" ) as f: + if os.path.exists(versionfile): + with open(versionfile, "rt") as f: lines = f.readlines() f.close() for line in lines: if "version =" in line: - self.ver = line.replace("version =","").strip() + self.ver = line.replace("version =", "").strip() - def checkHost ( self ): - """ check which host and environment we are in. Warn against running - outside of singularity container, on clip """ + def checkHost(self): + """check which host and environment we are in. Warn against running + outside of singularity container, on clip""" import socket + hostname = socket.gethostname() if "clip-login-1" in hostname: - self.error ( "WARNING: running on the login node!" ) - #if self.nevents > 200: + self.error("WARNING: running on the login node!") + # if self.nevents > 200: # # sys.exit() if "clip" in hostname: try: singularity = os.environ["SINGULARITY_NAME"] - except KeyError as e: + except KeyError: pass - #self.error ( "we seem to not be inside of a singularity container!" ) - #sys.exit(-1) + # self.error ( "we seem to not be inside of a singularity container!" ) + # sys.exit(-1) + + def checkFortranInstallation(self): + """MG5 needs Fortran compiler to run correctly. + If gfortran is not found we throw exception. + """ + # FIXME: Cover any additional ways to specify fortran compiler for MG5 + LOG.debug("Checking Fortran installation.") + from shutil import which - def determineMG5Version ( self ): - """ find out version of mg5, by peeking into mg5 directory """ - files = glob.glob ( "mg5/MG5_aMC_v*.tar.gz" ) + if which("gfortran") is None: + LOG.error( + "Could not find gfortran installation. MG5 will not work correctly without Fortran compiler." + ) + raise Exception("MG5 requires gfortran to run correctly.") + + def determineMG5Version(self): + """find out version of mg5, by peeking into mg5 directory""" + files = glob.glob("mg5/MG5_aMC_v*.tar.gz") if len(files) != 1: - self.msg ( "I dont understand, I see %d MG5 tarballs" % len(files) ) + self.msg("I dont understand, I see %d MG5 tarballs" % len(files)) return - ver = files[0].replace(".tar.gz","").replace("mg5/","") - ver = ver.replace ( "MG5_aMC_", "" ).replace(".","_") - if not ver.startswith ( "v" ): - self.msg ( "I dont understand the version id %s" % ver ) + ver = files[0].replace(".tar.gz", "").replace("mg5/", "") + ver = ver.replace("MG5_aMC_", "").replace(".", "_") + if not ver.startswith("v"): + self.msg("I dont understand the version id %s" % ver) return - self.debug ( f"this is MG5 {ver}" ) - if ver.startswith ( "v" ): + self.debug(f"this is MG5 {ver}") + if ver.startswith("v"): self.ver = ver if "py3" in ver: self.pyver = 3 - def correctPythia8CfgFile ( self ): - """ a simple method intended to check if we have to add SysCalc:qCutList=90 - to the pythia8 configuration """ + def correctPythia8CfgFile(self): + """a simple method intended to check if we have to add SysCalc:qCutList=90 + to the pythia8 configuration""" ## qcut: SysCalc:qCutList in mg5/Template/LO/Cards/pythia8_card_default.dat - #self.msg ( "FIXME we shouldnt be using this!" ) + # self.msg ( "FIXME we shouldnt be using this!" ) return - self.msg ( "now checking if pythia8 config needs correction" ) + self.msg("now checking if pythia8 config needs correction") needsCorrection = True cfgFile = "mg5/Template/LO/Cards/pythia8_card_default.dat" - f = open ( cfgFile, "rt" ) + f = open(cfgFile, "rt") lines = f.readlines() f.close() for line in lines: @@ -168,41 +200,37 @@ def correctPythia8CfgFile ( self ): continue if "SysCalc:qCutList" in line: needsCorrection = False - if "2_6" in self.ver: # only needed for 2_7 i think + if "2_6" in self.ver: # only needed for 2_7 i think needsCorrection = False if not needsCorrection: - self.msg ( "%s does not need correction" % cfgFile ) + self.msg("%s does not need correction" % cfgFile) return - self.msg ( "seems like %s needs qCutList added" % cfgFile ) - f = open ( cfgFile, "at" ) - f.write ( "SysCalc:qCutList = 90.\n" ) + self.msg("seems like %s needs qCutList added" % cfgFile) + f = open(cfgFile, "at") + f.write("SysCalc:qCutList = 90.\n") f.close() - def info ( self, *msg ): - print ( "%s[mg5Wrapper] %s%s" % ( ansi.YELLOW, " ".join ( msg ), \ - ansi.RESET ) ) + def info(self, *msg): + print("%s[mg5Wrapper] %s%s" % (ansi.YELLOW, " ".join(msg), ansi.RESET)) - def announce ( self, *msg ): - print ( "%s[mg5Wrapper] %s%s" % ( ansi.GREEN, " ".join ( msg ), \ - ansi.RESET ) ) + def announce(self, *msg): + print("%s[mg5Wrapper] %s%s" % (ansi.GREEN, " ".join(msg), ansi.RESET)) - def debug( self, *msg ): + def debug(self, *msg): pass - def msg ( self, *msg): - print ( "[mg5Wrapper] %s" % " ".join ( msg ) ) + def msg(self, *msg): + print("[mg5Wrapper] %s" % " ".join(msg)) - def error ( self, *msg ): - print ( "%s[mg5Wrapper] %s%s" % ( ansi.RED, " ".join ( msg ), \ - ansi.RESET ) ) + def error(self, *msg): + print("%s[mg5Wrapper] %s%s" % (ansi.RED, " ".join(msg), ansi.RESET)) - def writePythiaCard ( self, process="", masses="" ): - """ this method writes the pythia card for within mg5. + def writePythiaCard(self, process="", masses=""): + """this method writes the pythia card for within mg5. :param process: fixme (eg T2_1jet) """ - self.runcard = tempfile.mktemp ( prefix="run", suffix=".card", - dir=self.tempdir ) - self.debug ( f"writing pythia run card {self.runcard}" ) + self.runcard = tempfile.mktemp(prefix="run", suffix=".card", dir=self.tempdir) + self.debug(f"writing pythia run card {self.runcard}") templatefile = f"{self.templateDir}/template_run_card.dat" if "TRV1bias" in self.topo: templatefile = f"{self.templateDir}/template_run_card_TRV1_bias.dat" @@ -212,200 +240,233 @@ def writePythiaCard ( self, process="", masses="" ): templatefile = f"{self.templateDir}/template_run_card_bias.dat" if self.topo == "TRV1" or self.topo == "TRS1": templatefile = f"{self.templateDir}/template_run_card_TRV1_TRS1_no-bias.dat" - if not os.path.exists ( templatefile ): - self.error ( f"cannot find {templatefile}" ) + if not os.path.exists(templatefile): + self.error(f"cannot find {templatefile}") sys.exit() - tfile = open( templatefile,'r') + tfile = open(templatefile, "r") lines = tfile.readlines() tfile.close() - g = open ( self.runcard, "w" ) + g = open(self.runcard, "w") for line in lines: - for k,v in self.mgParams.items(): + for k, v in self.mgParams.items(): if k in line: vold = v - if type(v)==str and "M[0]" in v: + if type(v) == str and "M[0]" in v: m0 = masses[0] - if bakeryHelpers.isAssociateProduction ( self.topo ): - m0 = ( masses[0] + masses[1] ) / 2. ## mean! + if bakeryHelpers.isAssociateProduction(self.topo): + m0 = (masses[0] + masses[1]) / 2.0 ## mean! # m0 = min( masses[0], masses[1] ) - v = v.replace("M[0]",str(m0)) - v = str(eval (v )) - line = line.replace( f"@@{k}@@",v) - g.write ( line ) - if False and self.topo in [ "TChiQ" ]: - self.info( f"topo is {self.topo}: switch to new sde strategy (2)" ) - g.write ( f"# for topos like {self.topo} we use the new sde strategy\n" ) - g.write ( f"# see: https://indico.cern.ch/event/1041378/contributions/4374468/attachments/2258140/3832110/21_06_04_VBFSCAN_new_ps.pdf" ) - g.write ( "\n" ) - g.write ( " 2 = sde_strategy ! the new integration strategy\n" ) + v = v.replace("M[0]", str(m0)) + v = str(eval(v)) + line = line.replace(f"@@{k}@@", v) + g.write(line) + if False and self.topo in ["TChiQ"]: + self.info(f"topo is {self.topo}: switch to new sde strategy (2)") + g.write(f"# for topos like {self.topo} we use the new sde strategy\n") + g.write( + "# see: https://indico.cern.ch/event/1041378/contributions/4374468/attachments/2258140/3832110/21_06_04_VBFSCAN_new_ps.pdf" + ) + g.write("\n") + g.write(" 2 = sde_strategy ! the new integration strategy\n") # g.write ( " 2 = hard_survey ! not sure if this is needed, nor what it does\n" ) g.close() self.info(f"wrote run card {self.runcard} for {str(masses)}[{self.topo}]") - def writeCommandFile ( self, process = "", masses = None ): - """ this method writes the commands file for mg5. + def writeCommandFile(self, process="", masses=None): + """this method writes the commands file for mg5. :param process: fixme (eg T2tt_1jet) """ - self.commandfile = tempfile.mktemp ( prefix="mg5cmd", dir=self.tempdir ) - f = open(self.commandfile,'w') - f.write('set automatic_html_opening False\n' ) - f.write('launch %s\n' % bakeryHelpers.dirName(process,masses)) - f.write('shower=Pythia8\n') - f.write('detector=OFF\n') - #f.write('detector=Delphes\n') - #f.write('pythia=ON\n') - #f.write('madspin=OFF\n') + self.commandfile = tempfile.mktemp(prefix="mg5cmd", dir=self.tempdir) + f = open(self.commandfile, "w") + f.write("set automatic_html_opening False\n") + f.write("launch %s\n" % bakeryHelpers.dirName(process, masses)) + f.write("shower=Pythia8\n") + f.write("detector=OFF\n") + # f.write('detector=Delphes\n') + # f.write('pythia=ON\n') + # f.write('madspin=OFF\n') # f.write('order=LO\n') # f.write('reweight=ON\n') - f.write('0\n') - f.write('0\n') + f.write("0\n") + f.write("0\n") f.close() - def pluginMasses( self, slhaTemplate, masses ): - """ take the template slha file and plug in - masses """ - f=open( self.templateDir+"/"+slhaTemplate,"r") - lines=f.readlines() + def pluginMasses(self, slhaTemplate, masses): + """take the template slha file and plug in + masses""" + f = open(self.templateDir + "/" + slhaTemplate, "r") + lines = f.readlines() f.close() - self.slhafile = tempfile.mktemp(suffix=".slha",dir=self.tempdir ) - f=open( self.slhafile,"w") - n=len(masses) + self.slhafile = tempfile.mktemp(suffix=".slha", dir=self.tempdir) + f = open(self.slhafile, "w") + n = len(masses) for line in lines: tokens = line.split() for i in range(n): repl = str(masses[i]) - prev = f"M{n-i-1}" - if len(tokens)>1: + prev = f"M{n - i - 1}" + if len(tokens) > 1: mt = tokens[1] - if "+" in mt and not "+" in repl: - mt = mt.replace ( prev, str(masses[i]) ) + if "+" in mt and "+" not in repl: + mt = mt.replace(prev, str(masses[i])) try: - mt=str(eval(mt)) - repl=mt - except Exception as e: + mt = str(eval(mt)) + repl = mt + except Exception: pass if "+2" in line and False: - print ( "mt", mt ) - print ( f"replacing with {repl}" ) - print ( f"line is {line}" ) + print("mt", mt) + print(f"replacing with {repl}") + print(f"line is {line}") p1 = line.find("+") - p2 = line.find(" ",p1) + p2 = line.find(" ", p1) addOn = line[p1:p2] - if addOn in [ "+00", "+" ]: + if addOn in ["+00", "+"]: addOn = "" - line = line.replace ( prev+addOn, repl ) - f.write ( line ) + line = line.replace(prev + addOn, repl) + f.write(line) f.close() - def sleep ( self ): - """ if a file called sleep exists with an integer number in it, + def sleep(self): + """if a file called sleep exists with an integer number in it, then sleep that long before starting the next point. - meant to be used to control the production on an HPC cluster """ + meant to be used to control the production on an HPC cluster""" sleepFile = f"{self.basedir}/sleep" s = 0 - if os.path.exists ( sleepFile ): - f = open(sleepFile,"rt") + if os.path.exists(sleepFile): + f = open(sleepFile, "rt") txt = f.read() f.close() try: - s = int ( txt ) - except Exception as e: + s = int(txt) + except Exception: pass if s > 0: - self.info ( f"sleeping for {s} seconds before next point" ) - time.sleep ( s ) + self.info(f"sleeping for {s} seconds before next point") + time.sleep(s) - def run( self, masses, analyses, pid=None ): - """ Run MG5 for topo, with njets additional ISR jets, giving + def run(self, masses, analyses, pid=None): + """Run MG5 for topo, with njets additional ISR jets, giving also the masses as a list. """ os.system("echo $LD_LIBRARY_PATH") self.sleep() + # Fortran is needed for MG5 to run correctly + self.checkFortranInstallation() self.checkInstallation() import emCreator - isIn = emCreator.massesInEmbakedFile ( masses, analyses, self.topo, self.recaster ) + + isIn = emCreator.massesInEmbakedFile(masses, analyses, self.topo, self.recaster) # print ( f"is the point {masses} for {analyses} in embakedfile? {isIn} rerun: {self.rerun}" ) # sys.exit() if isIn and not self.rerun: return - if not "adl" in self.recaster and self.locker.hasMA5Files ( masses ) and not self.rerun: + if ( + "adl" not in self.recaster + and self.locker.hasMA5Files(masses) + and not self.rerun + ): return - if "adl" in self.recaster and self.locker.hasCutlangFiles ( masses ) and not self.rerun: + if ( + "adl" in self.recaster + and self.locker.hasCutlangFiles(masses) + and not self.rerun + ): return - locked = self.locker.lock ( masses ) + locked = self.locker.lock(masses) if locked: - self.info ( "%s[%s] is locked. Skip it" % ( masses, self.topo ) ) - self.info ( f"If you wish to remove it:\nrm {self.locker.lockfile(masses)}" ) + self.info("%s[%s] is locked. Skip it" % (masses, self.topo)) + self.info(f"If you wish to remove it:\nrm {self.locker.lockfile(masses)}") return - self.process = "%s_%djet" % ( self.topo, self.njets ) - if self.locker.hasHEPMC ( masses ): + self.process = "%s_%djet" % (self.topo, self.njets) + if self.locker.hasHEPMC(masses): if not self.rerun: - which = self.recaster[0] - self.info ( "hepmc file for %s[%s] exists. go directly to %s." % \ - ( str(masses), self.topo, which ) ) - self.runRecasting ( masses, analyses, pid ) - self.locker.unlock ( masses ) + which = self.recaster[0] + self.info( + "hepmc file for %s[%s] exists. go directly to %s." + % (str(masses), self.topo, which) + ) + self.runRecasting(masses, analyses, pid) + self.locker.unlock(masses) return else: - self.info ( "hepmc file for %s exists, but rerun requested." % str(masses) ) - - if "TRV1" in self.topo and float(masses[0]) >= 450. : - self.mgParams["XQCUT"]="M[0]/15" - if "TRV1" in self.topo and float(masses[0]) < 450. : - self.mgParams["XQCUT"]="30" - if "TRS1" in self.topo and float(masses[0]) >= 525. : - self.mgParams["XQCUT"]="M[0]/15" - if "TRS1" in self.topo and float(masses[0]) < 525. : - self.mgParams["XQCUT"]="35" - if "ISR" in self.topo and float(masses[0]) <= 25. : - self.mgParams["XQCUT"]="15" - if "ISR" in self.topo and float(masses[0]) > 25. and float(masses[0]) <= 60.: - self.mgParams["XQCUT"]="M[0]/2" - - self.announce ( "starting MG5 on %s[%s] at %s in job #%s" % (masses, self.topo, time.asctime(), pid ) ) + self.info( + "hepmc file for %s exists, but rerun requested." % str(masses) + ) + + if "TRV1" in self.topo and float(masses[0]) >= 450.0: + self.mgParams["XQCUT"] = "M[0]/15" + if "TRV1" in self.topo and float(masses[0]) < 450.0: + self.mgParams["XQCUT"] = "30" + if "TRS1" in self.topo and float(masses[0]) >= 525.0: + self.mgParams["XQCUT"] = "M[0]/15" + if "TRS1" in self.topo and float(masses[0]) < 525.0: + self.mgParams["XQCUT"] = "35" + if "ISR" in self.topo and float(masses[0]) <= 25.0: + self.mgParams["XQCUT"] = "15" + if "ISR" in self.topo and float(masses[0]) > 25.0 and float(masses[0]) <= 60.0: + self.mgParams["XQCUT"] = "M[0]/2" + + self.announce( + "starting MG5 on %s[%s] at %s in job #%s" + % (masses, self.topo, time.asctime(), pid) + ) slhaTemplate = f"slha/{self.topo}_template.slha" - self.pluginMasses( slhaTemplate, masses ) + self.pluginMasses(slhaTemplate, masses) # first write pythia card - self.writePythiaCard ( process=self.process, masses=masses ) + self.writePythiaCard(process=self.process, masses=masses) # then write command file - self.writeCommandFile( process=self.process, masses=masses ) + self.writeCommandFile(process=self.process, masses=masses) # then run madgraph5 - r=self.execute ( self.slhafile, masses ) - self.unlink ( self.slhafile ) + r = self.execute(self.slhafile, masses) + self.unlink(self.slhafile) if r: - self.runRecasting ( masses, analyses, pid ) - self.locker.unlock ( masses ) + self.runRecasting(masses, analyses, pid) + self.locker.unlock(masses) - def runRecasting ( self, masses, analyses, pid ): - """ run the recasting. cutlang or ma5 """ + def runRecasting(self, masses, analyses, pid): + """run the recasting. cutlang or ma5""" try: if not self.recast: return if "adl" in self.recaster: - self.runCutlang ( masses, analyses, pid ) + self.runCutlang(masses, analyses, pid) if "cm2" in self.recaster: - self.runCheckmate ( masses, analyses, pid ) + self.runCheckmate(masses, analyses, pid) if "colliderbit" in self.recaster: - self.runColliderbit ( masses, analyses, pid ) + self.runColliderbit(masses, analyses, pid) if "MA5" in self.recaster: - self.runMA5 ( masses, analyses, pid ) + self.runMA5(masses, analyses, pid) except Exception as e: - if self.keep: # if keep is on, we remove the lock. seems like were debugging - self.locker.unlock ( masses ) + if ( + self.keep + ): # if keep is on, we remove the lock. seems like were debugging + self.locker.unlock(masses) raise e - def runMA5 ( self, masses, analyses, pid ): - """ run ma5, if desired """ - spid="" + def runMA5(self, masses, analyses, pid): + """run ma5, if desired""" + spid = "" if pid != None: spid = " in job #%d" % pid - self.announce ( "starting MA5 on %s[%s] at %s%s" % ( str(masses), self.topo, time.asctime(), spid ) ) + self.announce( + "starting MA5 on %s[%s] at %s%s" + % (str(masses), self.topo, time.asctime(), spid) + ) from ma5Wrapper import MA5Wrapper - ma5 = MA5Wrapper ( self.topo, self.njets, self.rerun, analyses, self.keep, - self.sqrts, keephepmc = self.keephepmc ) - self.debug ( "now call ma5Wrapper" ) - hepmcfile = self.locker.hepmcFileName ( masses ) - ma5_runs = ma5.run ( masses, hepmcfile, pid ) + + ma5 = MA5Wrapper( + self.topo, + self.njets, + self.rerun, + analyses, + self.keep, + self.sqrts, + keephepmc=self.keephepmc, + ) + self.debug("now call ma5Wrapper") + hepmcfile = self.locker.hepmcFileName(masses) + ma5_runs = ma5.run(masses, hepmcfile, pid) ret = ma5_runs["exit_status"] msg = "finished MG5+MA5" @@ -413,203 +474,235 @@ def runMA5 ( self, masses, analyses, pid ): msg = "nothing needed to be done" if ret < 0: msg = "error encountered" - self.announce ( "%s for %s[%s] at %s%s" % ( msg, str(masses), self.topo, time.asctime(), spid ) ) + self.announce( + "%s for %s[%s] at %s%s" + % (msg, str(masses), self.topo, time.asctime(), spid) + ) - def runCutlang ( self, masses, analyses, pid ): - """ run cutlang, if desired """ - spid="" + def runCutlang(self, masses, analyses, pid): + """run cutlang, if desired""" + spid = "" if pid != None: spid = " in job #%d" % pid - self.announce ( "starting cutlang on %s[%s] at %s%s" % ( str(masses), self.topo, time.asctime(), spid ) ) + self.announce( + "starting cutlang on %s[%s] at %s%s" + % (str(masses), self.topo, time.asctime(), spid) + ) from cutlangWrapper import CutLangWrapper + rerun = self.rerun # rerun = True analist = analyses.split(",") for ana in analist: ana = ana.strip() - cl = CutLangWrapper ( self.topo, self.njets, rerun, ana, - auto_confirm = True, keep = self.keep, adl_file = self.adl_file, - event_condition = self.event_condition ) + cl = CutLangWrapper( + self.topo, + self.njets, + rerun, + ana, + auto_confirm=True, + keep=self.keep, + adl_file=self.adl_file, + event_condition=self.event_condition, + ) # self.sqrts ) - self.debug ( f"now call cutlangWrapper for {ana}" ) - hepmcfile = self.locker.hepmcFileName ( masses ) - ret = cl.run ( masses, hepmcfile, pid ) + self.debug(f"now call cutlangWrapper for {ana}") + hepmcfile = self.locker.hepmcFileName(masses) + ret = cl.run(masses, hepmcfile, pid) msg = "finished MG5+Cutlang: " if ret > 0: msg += "nothing needed to be done" if ret < 0: msg += "error encountered" - self.announce ( "%s for %s[%s] at %s%s" % ( msg, str(masses), self.topo, time.asctime(), spid ) ) + self.announce( + "%s for %s[%s] at %s%s" + % (msg, str(masses), self.topo, time.asctime(), spid) + ) - def runCheckmate ( self, masses, analyses, pid ): - """ run checkmate, if desired """ - spid="" + def runCheckmate(self, masses, analyses, pid): + """run checkmate, if desired""" + spid = "" if pid != None: spid = " in job #%d" % pid - self.announce ( "starting checkmate on %s[%s] at %s%s" % ( str(masses), self.topo, time.asctime(), spid ) ) + self.announce( + "starting checkmate on %s[%s] at %s%s" + % (str(masses), self.topo, time.asctime(), spid) + ) from cm2Wrapper import CM2Wrapper + rerun = self.rerun # rerun = True analist = analyses.split(",") for ana in analist: ana = ana.strip() - cl = CM2Wrapper ( self.topo, self.njets, rerun, ana, keep = self.keep ) + cl = CM2Wrapper(self.topo, self.njets, rerun, ana, keep=self.keep) # self.sqrts ) - self.debug ( f"now call cutlangWrapper for {ana}" ) - hepmcfile = self.locker.hepmcFileName ( masses ) - ret = cl.run ( masses, hepmcfile, pid ) + self.debug(f"now call cutlangWrapper for {ana}") + hepmcfile = self.locker.hepmcFileName(masses) + ret = cl.run(masses, hepmcfile, pid) msg = "finished MG5+Checkmate: " if ret > 0: msg += "nothing needed to be done" if ret < 0: msg += "error encountered" - self.announce ( "%s for %s[%s] at %s%s" % ( msg, str(masses), self.topo, time.asctime(), spid ) ) + self.announce( + "%s for %s[%s] at %s%s" + % (msg, str(masses), self.topo, time.asctime(), spid) + ) - def runColliderbit ( self, masses, analyses, pid ): - """ run colliderbit, if desired """ - spid="" + def runColliderbit(self, masses, analyses, pid): + """run colliderbit, if desired""" + spid = "" if pid != None: spid = f" in job #{pid}" - self.announce ( f"starting colliderbit on {str(masses)}[{self.topo}] at {time.asctime()}{spid}" ) + self.announce( + f"starting colliderbit on {str(masses)}[{self.topo}] at {time.asctime()}{spid}" + ) from gambitWrapper import GambitWrapper + rerun = self.rerun # rerun = True analist = analyses.split(",") for ana in analist: ana = ana.strip() - cl = GambitWrapper ( self.topo, self.njets, rerun, ana, - keep = self.keep ) + cl = GambitWrapper(self.topo, self.njets, rerun, ana, keep=self.keep) cl.nevents = self.nevents - self.debug ( f"now call gambitWrapper for {ana}" ) - hepmcfile = self.locker.hepmcFileName ( masses ) - ret = cl.run ( masses, hepmcfile, pid ) + self.debug(f"now call gambitWrapper for {ana}") + hepmcfile = self.locker.hepmcFileName(masses) + ret = cl.run(masses, hepmcfile, pid) msg = "finished MG5+colliderbit: " if ret > 0: msg += "nothing needed to be done" if ret < 0: msg += "error encountered" - self.announce ( f"{msg} for {str(masses)}[{self.topo}] at {time.asctime()}{spid}" ) + self.announce( + f"{msg} for {str(masses)}[{self.topo}] at {time.asctime()}{spid}" + ) - def unlink ( self, f ): - """ remove a file, if keep is not true """ + def unlink(self, f): + """remove a file, if keep is not true""" if self.keep: return if f == None: return - if os.path.exists ( f ): - subprocess.getoutput ( f"rm -rf {f}" ) + if os.path.exists(f): + subprocess.getoutput(f"rm -rf {f}") - def exe ( self, cmd, masses="" ): + def exe(self, cmd, masses=""): sm = "" if masses != "": - sm=f"[{str(masses)}]" - self.msg ( f"exec {self.topo}{sm}:: {cmd[:]}" ) - pipe = subprocess.Popen ( cmd, shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE ) - ret="" + sm = f"[{str(masses)}]" + self.msg(f"exec {self.topo}{sm}:: {cmd[:]}") + pipe = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + ret = "" for line in io.TextIOWrapper(pipe.stdout, encoding="latin1"): - ret+=line + ret += line for line in io.TextIOWrapper(pipe.stderr, encoding="latin1"): - ret+=line - if len(ret)==0: + ret += line + if len(ret) == 0: return - maxLength=400 + maxLength = 400 # maxLength=100000 - if len(ret) bool: - """ make directory + def mkdir(self, dirname) -> bool: + """make directory :returns: true, if all went well """ - if not os.path.exists ( dirname ): + if not os.path.exists(dirname): try: - os.mkdir ( dirname ) + os.mkdir(dirname) return True - except FileExistsError as e: + except FileExistsError: # can happen if many processses start at once return False return False - def fixPythia8 ( self ): - """ fix pythia8, first remove it """ - path = os.path.join ( self.mg5install, "HEPTools", "pythia8" ) + def fixPythia8(self): + """fix pythia8, first remove it""" + path = os.path.join(self.mg5install, "HEPTools", "pythia8") cmd = f"rm -rf {path}" - o = subprocess.getoutput ( cmd ) - backup = os.path.join ( self.basedir, "backup", "pythia8" ) - if not os.path.exists ( backup ): + o = subprocess.getoutput(cmd) + backup = os.path.join(self.basedir, "backup", "pythia8") + if not os.path.exists(backup): line = f"pythia8 install broken and no backup available at {backup}" - self.error ( line ) - raise Exception ( line ) + self.error(line) + raise Exception(line) cmd = f"cp -r {backup} {path}" - o = subprocess.getoutput ( cmd ) + o = subprocess.getoutput(cmd) - def checkInstallation ( self ): - """ check the mg5 installation, including plugins + def checkInstallation(self): + """check the mg5 installation, including plugins :raises: Exception, if anything is wrong :returns: True, if all is ok """ - path = os.path.join ( self.mg5install, "HEPTools", "bin" ) - pythiaconfig = os.path.join ( path, "pythia8-config" ) + path = os.path.join(self.mg5install, "HEPTools", "bin") + pythiaconfig = os.path.join(path, "pythia8-config") # print ( f"[mg5Wrapper] checking in {path}" ) - if not os.path.exists ( pythiaconfig ): + if not os.path.exists(pythiaconfig): self.fixPythia8() - path = os.path.join ( self.mg5install, "HEPTools", "pythia8", "share", "Pythia8", "xmldoc" ) - pythiaxml = os.path.join ( path, "Welcome.xml" ) - if not os.path.exists ( pythiaxml ): + path = os.path.join( + self.mg5install, "HEPTools", "pythia8", "share", "Pythia8", "xmldoc" + ) + pythiaxml = os.path.join(path, "Welcome.xml") + if not os.path.exists(pythiaxml): self.fixPythia8() cmd = f"{pythiaconfig} --with-hepmc2" - o = subprocess.getoutput ( cmd ) + o = subprocess.getoutput(cmd) if o == "false": - raise Exception ( f"pythia8 has no hepmc2 support" ) + raise Exception("pythia8 has no hepmc2 support") if False: cmd = f"{pythiaconfig} --with-lhapdf6" - o = subprocess.getoutput ( cmd ) + o = subprocess.getoutput(cmd) if o == "false": - raise Exception ( f"pythia8 has no lhapdf6 support" ) + raise Exception("pythia8 has no lhapdf6 support") return True - def execute ( self, slhaFile, masses ): - templatefile = self.templateDir + '/MG5_Process_Cards/'+self.topo+'.txt' - if not os.path.isfile( templatefile ): - self.error ( "The process card %s does not exist." % templatefile ) + def execute(self, slhaFile, masses): + templatefile = self.templateDir + "/MG5_Process_Cards/" + self.topo + ".txt" + if not os.path.isfile(templatefile): + self.error("The process card %s does not exist." % templatefile) sys.exit() - f=open(templatefile,"r") - lines=f.readlines() + f = open(templatefile, "r") + lines = f.readlines() f.close() - self.tempf = tempfile.mktemp(prefix="mg5proc",dir=self.tempdir ) - f=open(self.tempf,"w") - f.write ( "set auto_convert_model T\n" ) + self.tempf = tempfile.mktemp(prefix="mg5proc", dir=self.tempdir) + f = open(self.tempf, "w") + f.write("set auto_convert_model T\n") """ #if self.topo == "TChiQ": # f.write ( "import model MSSM_SLHA2-full --modelname\n" ) @@ -629,93 +722,105 @@ def execute ( self, slhaFile, masses ): f.write ( "import model_v4 mssm\n" ) """ for line in lines: - f.write ( line ) - for i in [ 1, 2, 3 ]: + f.write(line) + for i in [1, 2, 3]: if self.njets >= i: - self.addJet ( lines, i, f ) + self.addJet(lines, i, f) - Dir = bakeryHelpers.dirName ( self.process, masses ) - f.write ( "output %s\n" % Dir ) + Dir = bakeryHelpers.dirName(self.process, masses) + f.write("output %s\n" % Dir) f.close() - self.info ( "run mg5 for %s[%s]: %s" % ( masses, self.topo, self.tempf ) ) - self.logfile = tempfile.mktemp () - if os.path.exists ( Dir ): - subprocess.getoutput ( f"rm -rf {Dir}" ) - r = self.mkdir ( Dir ) + self.info("run mg5 for %s[%s]: %s" % (masses, self.topo, self.tempf)) + self.logfile = tempfile.mktemp() + if os.path.exists(Dir): + subprocess.getoutput(f"rm -rf {Dir}") + r = self.mkdir(Dir) if r == False: - self.error ( f"{Dir} already exists! Skipping!" ) + self.error(f"{Dir} already exists! Skipping!") return False if self.keep: - self.mkdir ( "keep/" ) - shutil.copy ( self.tempf, "keep/" + Dir + "mg5proc" ) - shutil.move ( self.tempf, Dir + "/mg5proc" ) - cmd = "python%d %s %s/mg5proc 2>&1 | tee %s" % \ - ( self.pyver, self.executable, Dir, self.logfile ) - self.exe ( cmd, masses ) + self.mkdir("keep/") + shutil.copy(self.tempf, "keep/" + Dir + "mg5proc") + shutil.move(self.tempf, Dir + "/mg5proc") + cmd = "python%d %s %s/mg5proc 2>&1 | tee %s" % ( + self.pyver, + self.executable, + Dir, + self.logfile, + ) + self.exe(cmd, masses) ## copy slha file - if not os.path.exists ( Dir+"/Cards" ): + if not os.path.exists(Dir + "/Cards"): cmd = f"rm -rf {Dir}" - o = subprocess.getoutput ( cmd ) - o = subprocess.getoutput ( f"cat {self.logfile}" ) - self.error ( f"{Dir}/Cards does not exist! Skipping! {o}" ) - self.exe ( cmd, masses ) + o = subprocess.getoutput(cmd) + o = subprocess.getoutput(f"cat {self.logfile}") + self.error(f"{Dir}/Cards does not exist! Skipping! {o}") + self.exe(cmd, masses) return False - with open ( f"{Dir}/analysis", "wt" ) as f: + with open(f"{Dir}/analysis", "wt") as f: # pen down the analysis name - ana = self.args["analyses"].upper().replace("_","-") - f.write ( ana+"\n" ) + ana = self.args["analyses"].upper().replace("_", "-") + f.write(ana + "\n") f.close() if "bias" in self.topo: - shutil.copy("templates/pythia8_card_match.dat", Dir+'/Cards/pythia8_card.dat') - shutil.move(slhaFile, Dir+'/Cards/param_card.dat' ) - shutil.move(self.runcard, Dir+'/Cards/run_card.dat' ) - shutil.move(self.commandfile, Dir+"/mg5cmd" ) - if (os.path.isdir(Dir+'/Events/run_01')): - shutil.rmtree(Dir+'/Events/run_01') - self.logfile2 = tempfile.mktemp () + shutil.copy( + "templates/pythia8_card_match.dat", Dir + "/Cards/pythia8_card.dat" + ) + shutil.move(slhaFile, Dir + "/Cards/param_card.dat") + shutil.move(self.runcard, Dir + "/Cards/run_card.dat") + shutil.move(self.commandfile, Dir + "/mg5cmd") + if os.path.isdir(Dir + "/Events/run_01"): + shutil.rmtree(Dir + "/Events/run_01") + self.logfile2 = tempfile.mktemp() cmd = f"python{self.pyver} {self.executable} {Dir}/mg5cmd 2>&1 | tee {self.logfile2}" - self.exe ( cmd, masses ) - hepmcfile = self.orighepmcFileName( masses ) - if self.hasorigHEPMC ( masses ): - dest = self.locker.hepmcFileName ( masses ) - self.msg ( "moving", hepmcfile, "to", dest ) - shutil.move ( hepmcfile, dest ) + self.exe(cmd, masses) + hepmcfile = self.orighepmcFileName(masses) + if self.hasorigHEPMC(masses): + dest = self.locker.hepmcFileName(masses) + self.msg("moving", hepmcfile, "to", dest) + shutil.move(hepmcfile, dest) else: - self.error ( f"could not find orig hepmc file {self.orighepmcFileName( masses )}! maybe there is something wrong with the mg5 installation?" ) - self.clean( Dir ) + self.error( + f"could not find orig hepmc file {self.orighepmcFileName(masses)}! maybe there is something wrong with the mg5 installation?" + ) + self.clean(Dir) return True - def clean ( self, Dir=None ): - """ clean up temporary files + def clean(self, Dir=None): + """clean up temporary files :param Dir: if given, then assume its the runtime directory, and remove "Source", "lib", "SubProcesses" and other subdirs """ if self.keep: return - self.info ( "cleaning up %s, %s, %s, %s" % \ - ( self.commandfile, self.tempf, self.logfile, self.logfile2 ) ) - self.unlink ( ".lock*" ) - #self.unlink ( self.commandfile ) - #self.unlink ( self.tempf ) - self.unlink ( self.logfile ) - self.unlink ( self.logfile2 ) + self.info( + "cleaning up %s, %s, %s, %s" + % (self.commandfile, self.tempf, self.logfile, self.logfile2) + ) + self.unlink(".lock*") + # self.unlink ( self.commandfile ) + # self.unlink ( self.tempf ) + self.unlink(self.logfile) + self.unlink(self.logfile2) if Dir != None: cmd = "rm -rf %s" % Dir - o = subprocess.getoutput ( cmd ) - self.info ( "clean up %s: %s" % ( cmd, o ) ) + o = subprocess.getoutput(cmd) + self.info("clean up %s: %s" % (cmd, o)) - def orighepmcFileName ( self, masses ): - """ return the hepmc file name *before* moving """ - hepmcfile = bakeryHelpers.dirName( self.process,masses)+\ - "/Events/run_01/tag_1_pythia8_events.hepmc.gz" + def orighepmcFileName(self, masses): + """return the hepmc file name *before* moving""" + hepmcfile = ( + bakeryHelpers.dirName(self.process, masses) + + "/Events/run_01/tag_1_pythia8_events.hepmc.gz" + ) return hepmcfile - def hasorigHEPMC ( self, masses ): - """ does it have a valid HEPMC file? if yes, then skip the point """ - hepmcfile = self.orighepmcFileName( masses ) - if not os.path.exists ( hepmcfile ): + def hasorigHEPMC(self, masses): + """does it have a valid HEPMC file? if yes, then skip the point""" + hepmcfile = self.orighepmcFileName(masses) + if not os.path.exists(hepmcfile): return False - if os.stat ( hepmcfile ).st_size < 100: + if os.stat(hepmcfile).st_size < 100: ## too small to be real return False return True @@ -723,98 +828,201 @@ def hasorigHEPMC ( self, masses ): def main(): import argparse - argparser = argparse.ArgumentParser(description='madgraph5 runner.') - argparser.add_argument ( '-n', '--nevents', help='number of events to generate [10000]', - type=int, default=10000 ) - argparser.add_argument ( '-j', '--njets', help='number of ISR jets [1]', - type=int, default=1 ) - argparser.add_argument ( '--sqrts', help='sqrts [13]', - type=int, default=13 ) - argparser.add_argument ( '-p', '--nprocesses', help='number of process to run in parallel. 0 means 1 per CPU [1]', - type=int, default=1 ) - argparser.add_argument ( '-T', '--topo', help='topology [T2]', - type=str, default="T2" ) - argparser.add_argument ( '-k', '--keep', help='keep temporary files', - action="store_true" ) - argparser.add_argument ( '-K', '--keephepmc', help='keep hepmc files', - action="store_true" ) - argparser.add_argument ( '--show', help='show production stats', - action="store_true" ) - argparser.add_argument ( '-a', '--recast', help='run also recasting after producing the events', - action="store_true" ) - argparser.add_argument ( '-c', '--clean', help='clean all temporary files, then quit', - action="store_true" ) - argparser.add_argument ( '-b', '--bake', help='call emCreator, bake .embaked files', - action="store_true" ) - argparser.add_argument ( '-C', '--clean_all', help='clean all temporary files, even Tx directories, then quit', - action="store_true" ) - argparser.add_argument ( '--cutlang', help='use cutlang instead of MA5', - action="store_true" ) - argparser.add_argument ( '--checkmate', help='use checkmate instead of MA5', - action="store_true" ) - argparser.add_argument ( '--colliderbit', help='use colliderbit instead of MA5', - action="store_true" ) - argparser.add_argument ( '--adl_file', help='specify the name of the adl description to be used [if not specified, try to guess]', - type=str, default=None ) - argparser.add_argument ( '--event_condition', help='specify conditions on the events, filter out the rest, e.g. {"higgs":1}: one and only one higgs [None]', - type=str, default=None ) - argparser.add_argument ( '--copy', help='copy embaked file to smodels-database', - action="store_true" ) - argparser.add_argument ( '-l', '--list_analyses', help='print a list of MA5 analyses, then quit', - action="store_true" ) + + argparser = argparse.ArgumentParser(description="madgraph5 runner.") + argparser.add_argument( + "-n", + "--nevents", + help="number of events to generate [10000]", + type=int, + default=10000, + ) + argparser.add_argument( + "-j", "--njets", help="number of ISR jets [1]", type=int, default=1 + ) + argparser.add_argument("--sqrts", help="sqrts [13]", type=int, default=13) + argparser.add_argument( + "-p", + "--nprocesses", + help="number of process to run in parallel. 0 means 1 per CPU [1]", + type=int, + default=1, + ) + argparser.add_argument("-T", "--topo", help="topology [T2]", type=str, default="T2") + argparser.add_argument( + "-k", "--keep", help="keep temporary files", action="store_true" + ) + argparser.add_argument( + "-K", "--keephepmc", help="keep hepmc files", action="store_true" + ) + argparser.add_argument("--show", help="show production stats", action="store_true") + argparser.add_argument( + "-a", + "--recast", + help="run also recasting after producing the events", + action="store_true", + ) + argparser.add_argument( + "-c", + "--clean", + help="clean all temporary files, then quit", + action="store_true", + ) + argparser.add_argument( + "-b", "--bake", help="call emCreator, bake .embaked files", action="store_true" + ) + argparser.add_argument( + "-C", + "--clean_all", + help="clean all temporary files, even Tx directories, then quit", + action="store_true", + ) + argparser.add_argument( + "--cutlang", help="use cutlang instead of MA5", action="store_true" + ) + argparser.add_argument( + "--checkmate", help="use checkmate instead of MA5", action="store_true" + ) + argparser.add_argument( + "--colliderbit", help="use colliderbit instead of MA5", action="store_true" + ) + argparser.add_argument( + "--adl_file", + help="specify the name of the adl description to be used [if not specified, try to guess]", + type=str, + default=None, + ) + argparser.add_argument( + "--event_condition", + help='specify conditions on the events, filter out the rest, e.g. {"higgs":1}: one and only one higgs [None]', + type=str, + default=None, + ) + argparser.add_argument( + "--copy", help="copy embaked file to smodels-database", action="store_true" + ) + argparser.add_argument( + "-l", + "--list_analyses", + help="print a list of MA5 analyses, then quit", + action="store_true", + ) anadef = "atlas_susy_2016_07" anadef = "cms_sus_19_006" - argparser.add_argument ( '--analyses', help='analyses, comma separated [%s]' % anadef, - type=str, default=anadef ) - argparser.add_argument ( '--maxgap2', help='maximum mass gap between second and third, to force offshell [None]', - type=float, default=None ) - argparser.add_argument ( '--mingap1', help='minimum mass gap between first and second, to force onshell or a mass hierarchy [None]', - type=float, default=None ) - argparser.add_argument ( '--mingap2', help='minimum mass gap between second and third, to force onshell or a mass hierarchy [None]', - type=float, default=None ) - argparser.add_argument ( '--mingap13', help='minimum mass gap between first and third, to force onshell or a mass hierarchy [None]', - type=float, default=None ) - argparser.add_argument ( '--maxgap13', help='maximum mass gap between first and third, to force offshell [None]', - type=float, default=None ) - argparser.add_argument ( '--maxgap1', help='maximum mass gap between first and second, to force offshell [None]', - type=float, default=None ) - argparser.add_argument ( '-r', '--rerun', help='force rerun, even if there is a summary file already', - action="store_true" ) - argparser.add_argument ( '--dry_run', help='dry run, just print out the mass points', - action="store_true" ) - argparser.add_argument ( '--ignore_locks', help='ignore any locks. for debugging only.', - action="store_true" ) - #mdefault = "(2000,1000,10),(2000,1000,10)" + argparser.add_argument( + "--analyses", + help="analyses, comma separated [%s]" % anadef, + type=str, + default=anadef, + ) + argparser.add_argument( + "--maxgap2", + help="maximum mass gap between second and third, to force offshell [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--mingap1", + help="minimum mass gap between first and second, to force onshell or a mass hierarchy [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--mingap2", + help="minimum mass gap between second and third, to force onshell or a mass hierarchy [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--mingap13", + help="minimum mass gap between first and third, to force onshell or a mass hierarchy [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--maxgap13", + help="maximum mass gap between first and third, to force offshell [None]", + type=float, + default=None, + ) + argparser.add_argument( + "--maxgap1", + help="maximum mass gap between first and second, to force offshell [None]", + type=float, + default=None, + ) + argparser.add_argument( + "-r", + "--rerun", + help="force rerun, even if there is a summary file already", + action="store_true", + ) + argparser.add_argument( + "--dry_run", help="dry run, just print out the mass points", action="store_true" + ) + argparser.add_argument( + "--ignore_locks", + help="ignore any locks. for debugging only.", + action="store_true", + ) + # mdefault = "(2000,1000,10),(2000,1000,10)" mdefault = "(1000,2000,50),'half',(1000,2000,50)" - argparser.add_argument ( '-m', '--masses', help='mass ranges, comma separated list of tuples. One tuple gives the range for one mass parameter, as (m_lowest, m_highest, delta_m). m_highest and delta_m may be omitted. Keywords "half" and "same" (add quotes) are accepted for intermediate masses. [%s]' % mdefault, - type=str, default=mdefault ) + argparser.add_argument( + "-m", + "--masses", + help='mass ranges, comma separated list of tuples. One tuple gives the range for one mass parameter, as (m_lowest, m_highest, delta_m). m_highest and delta_m may be omitted. Keywords "half" and "same" (add quotes) are accepted for intermediate masses. [%s]' + % mdefault, + type=str, + default=mdefault, + ) args = argparser.parse_args() - if "bias" in args.topo and args.njets>0: - print ( f"{ansi.RED}[mg5Wrapper] topo {args.topo} seems a biased one but njets not equal zero?! {ansi.RESET}" ) + if "bias" in args.topo and args.njets > 0: + print( + f"{ansi.RED}[mg5Wrapper] topo {args.topo} seems a biased one but njets not equal zero?! {ansi.RESET}" + ) sys.exit() - if args.topo in [ "T1", "T2", "T1bbbb", "T2bb", "T2ttoff", "T1ttttoff" ] and args.mingap1 == None and not args.list_analyses and not args.clean and not args.clean_all: + if ( + args.topo in ["T1", "T2", "T1bbbb", "T2bb", "T2ttoff", "T1ttttoff"] + and args.mingap1 == None + and not args.list_analyses + and not args.clean + and not args.clean_all + ): if "(" in args.masses: - print ( f"[mg5Wrapper] for topo {args.topo} we set mingap1 to 1." ) - args.mingap1 = 1. - if args.topo in [ "T1tttt", "T2tt" ] and args.mingap1 == None and not args.list_analyses and not args.clean and not args.clean_all: + print(f"[mg5Wrapper] for topo {args.topo} we set mingap1 to 1.") + args.mingap1 = 1.0 + if ( + args.topo in ["T1tttt", "T2tt"] + and args.mingap1 == None + and not args.list_analyses + and not args.clean + and not args.clean_all + ): if "(" in args.masses: - print ( f"[mg5Wrapper] for topo {args.topo} we set mingap1 to 1." ) - ## also for these we set to 1. because usually this is also used for offshell - print ( f"[mg5Wrapper] for topo {args.topo} we set mingap1 to 1." ) - args.mingap1 = 1. # 170. - if args.topo in [ "T1ttttoff", "T2ttoff" ] and args.maxgap1 == None and not args.list_analyses and not args.clean and not args.clean_all: + print(f"[mg5Wrapper] for topo {args.topo} we set mingap1 to 1.") + ## also for these we set to 1. because usually this is also used for offshell + print(f"[mg5Wrapper] for topo {args.topo} we set mingap1 to 1.") + args.mingap1 = 1.0 # 170. + if ( + args.topo in ["T1ttttoff", "T2ttoff"] + and args.maxgap1 == None + and not args.list_analyses + and not args.clean + and not args.clean_all + ): if "(" in args.masses: - print ( f"[mg5Wrapper] for topo {args.topo} we set maxgap1 to 180." ) - args.maxgap1 = 180. + print(f"[mg5Wrapper] for topo {args.topo} we set maxgap1 to 180.") + args.maxgap1 = 180.0 if args.list_analyses: - bakeryHelpers.listAnalyses( args.cutlang, args.checkmate, args.colliderbit ) + bakeryHelpers.listAnalyses(args.cutlang, args.checkmate, args.colliderbit) sys.exit() if args.show: import printProdStats + anas = args.analyses.split(",") for ana in anas: - ana = bakeryHelpers.ma5AnaNameToSModelSName ( ana ) - printProdStats.main( ana ) + ana = bakeryHelpers.ma5AnaNameToSModelSName(ana) + printProdStats.main(ana) sys.exit() if args.clean_all: @@ -826,40 +1034,45 @@ def main(): sys.exit() hname = socket.gethostname() - if hname.find(".")>0: - hname=hname[:hname.find(".")] + if hname.find(".") > 0: + hname = hname[: hname.find(".")] logfile = "baking.log" lastline = None - if os.path.exists ( logfile ): - f=open( logfile,"rt") + if os.path.exists(logfile): + f = open(logfile, "rt") lines = f.readlines() f.close() - if len(lines)>0: + if len(lines) > 0: lastline = lines[-1].strip() cmd = "" - for i,a in enumerate(sys.argv): - if i>0 and sys.argv[i-1] in [ "-m", "--masses" ]: - a='"%s"' % a - if i>0 and sys.argv[i-1] in [ "--analyses" ]: - a='"%s"' % a + for i, a in enumerate(sys.argv): + if i > 0 and sys.argv[i - 1] in ["-m", "--masses"]: + a = '"%s"' % a + if i > 0 and sys.argv[i - 1] in ["--analyses"]: + a = '"%s"' % a cmd += a + " " cmd = cmd[:-1] if lastline == None or lastline != cmd: - with open(logfile,"a") as f: - f.write ( f"[{hname}] {time.asctime()}:\n{cmd}\n" ) - nReqM = bakeryHelpers.nRequiredMasses ( args.topo ) - keepOrder=True + with open(logfile, "a") as f: + f.write(f"[{hname}] {time.asctime()}:\n{cmd}\n") + nReqM = bakeryHelpers.nRequiredMasses(args.topo) + keepOrder = True if args.topo == "TGQ": - keepOrder=False - masses = bakeryHelpers.parseMasses ( args.masses, - mingap1=args.mingap1, maxgap1=args.maxgap1, - mingap2=args.mingap2, maxgap2=args.maxgap2, - mingap13=args.mingap13, maxgap13=args.maxgap13 ) + keepOrder = False + masses = bakeryHelpers.parseMasses( + args.masses, + mingap1=args.mingap1, + maxgap1=args.maxgap1, + mingap2=args.mingap2, + maxgap2=args.maxgap2, + mingap13=args.mingap13, + maxgap13=args.maxgap13, + ) if args.dry_run: - print ( f"[mg5Wrapper] masses: {masses}" ) + print(f"[mg5Wrapper] masses: {masses}") sys.exit() - import random - random.shuffle ( masses ) + + random.shuffle(masses) nm = len(masses) if nm == 0: line = "[mg5Wrapper] no masses found within the constraints:" @@ -869,62 +1082,83 @@ def main(): line += f" gap(1,3) not in [{args.mingap13},{args.maxgap13 if args.maxgap13 is not None else '+inf'}];" if args.mingap2 != None or args.maxgap2 != None: line += f" gap(2,3) not in [{args.mingap2},{args.maxgap2 if args.maxgap2 is not None else '+inf'}];" - print ( f"{line}" ) - locker.Locker( args.sqrts, args.topo, args.ignore_locks ).unlock ( masses ) + print(f"{line}") + locker.Locker(args.sqrts, args.topo, args.ignore_locks).unlock(masses) sys.exit() if nReqM != len(masses[0]): - print ( "[mg5Wrapper] you gave %d masses, but %d are required for %s." % \ - ( len(masses[0]), nReqM, args.topo ) ) + print( + "[mg5Wrapper] you gave %d masses, but %d are required for %s." + % (len(masses[0]), nReqM, args.topo) + ) sys.exit() - nprocesses = bakeryHelpers.nJobs ( args.nprocesses, nm ) - recaster = [ "MA5" ] + nprocesses = bakeryHelpers.nJobs(args.nprocesses, nm) + recaster = ["MA5"] if args.cutlang or args.checkmate or args.colliderbit: - recaster = [ "adl" ] + recaster = ["adl"] if args.checkmate: - recaster = [ "cm2" ] + recaster = ["cm2"] if args.colliderbit: - recaster = [ "colliderbit" ] + recaster = ["colliderbit"] args.recast = True if args.checkmate and args.cutlang: - print ( "[mg5Wrapper] both checkmate and cutlang have been asked for. please choose!" ) + print( + "[mg5Wrapper] both checkmate and cutlang have been asked for. please choose!" + ) sys.exit() if args.checkmate and args.colliderbit: - print ( "[mg5Wrapper] both checkmate and colliderbit have been asked for. please choose!" ) + print( + "[mg5Wrapper] both checkmate and colliderbit have been asked for. please choose!" + ) sys.exit() if args.cutlang and args.colliderbit: - print ( "[mg5Wrapper] both cutlang and colliderbit have been asked for. please choose!" ) + print( + "[mg5Wrapper] both cutlang and colliderbit have been asked for. please choose!" + ) sys.exit() - mg5 = MG5Wrapper( vars(args), recaster ) + mg5 = MG5Wrapper(vars(args), recaster) # mg5.info( "%d points to produce, in %d processes" % (nm,nprocesses) ) - djobs = int(len(masses)/nprocesses) + djobs = int(len(masses) / nprocesses) - def runChunk ( chunk, pid ): + def runChunk(chunk, pid): for c in chunk: - mg5.run ( c, args.analyses, pid ) - print ( "%s[runChunk] finished chunk #%d%s" % \ - ( ansi.GREEN, pid, ansi.RESET ) ) + mg5.run(c, args.analyses, pid) + print("%s[runChunk] finished chunk #%d%s" % (ansi.GREEN, pid, ansi.RESET)) - jobs=[] + jobs = [] for i in range(nprocesses): - chunk = masses[djobs*i:djobs*(i+1)] - if i == nprocesses-1: - chunk = masses[djobs*i:] - p = multiprocessing.Process(target=runChunk, args=(chunk,i)) - jobs.append ( p ) + chunk = masses[djobs * i : djobs * (i + 1)] + if i == nprocesses - 1: + chunk = masses[djobs * i :] + p = multiprocessing.Process(target=runChunk, args=(chunk, i)) + jobs.append(p) p.start() for j in jobs: j.join() if args.bake: import emCreator from types import SimpleNamespace + # analyses = "atlas_susy_2016_07" analyses = args.analyses - args = SimpleNamespace ( masses="all", topo=args.topo, njets=args.njets, \ - analyses = analyses, copy=args.copy, keep=args.keep, sqrts=args.sqrts, - verbose=False, ma5=not args.cutlang, cutlang=args.cutlang, stats=True, - cleanup = False, checkmate=args.checkmate, colliderbit = args.colliderbit ) - emCreator.run ( args ) + args = SimpleNamespace( + masses="all", + topo=args.topo, + njets=args.njets, + analyses=analyses, + copy=args.copy, + keep=args.keep, + sqrts=args.sqrts, + verbose=False, + ma5=not args.cutlang, + cutlang=args.cutlang, + stats=True, + cleanup=False, + checkmate=args.checkmate, + colliderbit=args.colliderbit, + ) + emCreator.run(args) + def main_via_ini(): import sys @@ -933,10 +1167,10 @@ def main_via_ini(): from types import SimpleNamespace class ansi: - RED = '\033[91m' - GREEN = '\033[92m' - YELLOW = '\033[93m' - RESET = '\033[0m' + RED = "\033[91m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + RESET = "\033[0m" parser = argparse.ArgumentParser(description="Read emcreator config") parser.add_argument("--config", type=str, help="") @@ -951,31 +1185,31 @@ class ansi: config_flat_dict[short_key] = config_full_dict[key] defaults = { - "nevents" : 10000, - "njets" : 1, - "nprocesses" : 1, - "mingap1" : None, - "mingap2" : None, - "maxgap1" : None, - "maxgap2" : None, - "mingap13" : None, - "maxgap13" : None, - "list_analyses" : False, - "clean" : False, - "clean_all" : False, - "adl_file" : None, - "event_condition" : None, - "sqrts" :13.0, - "ignore_locks" :None, - "keephepmc" :None, - "rerun" :None, - "dry_run" :None, - "cutlang" :None, - "checkmate" :None, - "colliderbit" :None, - "copy" :None, - "recaster" :None, - "recast" :None, + "nevents": 10000, + "njets": 1, + "nprocesses": 1, + "mingap1": None, + "mingap2": None, + "maxgap1": None, + "maxgap2": None, + "mingap13": None, + "maxgap13": None, + "list_analyses": False, + "clean": False, + "clean_all": False, + "adl_file": None, + "event_condition": None, + "sqrts": 13.0, + "ignore_locks": None, + "keephepmc": None, + "rerun": None, + "dry_run": None, + "cutlang": None, + "checkmate": None, + "colliderbit": None, + "copy": None, + "recaster": None, + "recast": None, } ns = SimpleNamespace() @@ -985,62 +1219,86 @@ class ansi: for key, value in config_flat_dict.items(): setattr(ns, key, value) - if "bias" in ns.topo and ns.njets > 0: - print(f"{ansi.RED}[mg5Wrapper] topo {ns.topo} seems a biased one but njets not equal zero?!{ansi.RESET}") + print( + f"{ansi.RED}[mg5Wrapper] topo {ns.topo} seems a biased one but njets not equal zero?!{ansi.RESET}" + ) sys.exit() - if ns.topo in ["T1", "T2", "T1bbbb", "T2bb", "T2ttoff", "T1ttttoff"] and ns.mingap1 is None and not ns.list_analyses and not ns.clean and not ns.clean_all: + if ( + ns.topo in ["T1", "T2", "T1bbbb", "T2bb", "T2ttoff", "T1ttttoff"] + and ns.mingap1 is None + and not ns.list_analyses + and not ns.clean + and not ns.clean_all + ): if "(" in ns.mass: print(f"[mg5Wrapper] for topo {ns.topo} we set mingap1 to 1.") - ns.mingap1 = 1. + ns.mingap1 = 1.0 - if ns.topo in ["T1tttt", "T2tt"] and ns.mingap1 is None and not ns.list_analyses and not ns.clean and not ns.clean_all: + if ( + ns.topo in ["T1tttt", "T2tt"] + and ns.mingap1 is None + and not ns.list_analyses + and not ns.clean + and not ns.clean_all + ): if "(" in ns.mass: print(f"[mg5Wrapper] for topo {ns.topo} we set mingap1 to 1.") - ns.mingap1 = 1. + ns.mingap1 = 1.0 - if ns.topo in ["T1ttttoff", "T2ttoff"] and ns.maxgap1 is None and not ns.list_analyses and not ns.clean and not ns.clean_all: + if ( + ns.topo in ["T1ttttoff", "T2ttoff"] + and ns.maxgap1 is None + and not ns.list_analyses + and not ns.clean + and not ns.clean_all + ): if "(" in ns.mass: print(f"[mg5Wrapper] for topo {ns.topo} we set maxgap1 to 180.") - ns.maxgap1 = 180. - + ns.maxgap1 = 180.0 + hname = socket.gethostname() - if hname.find(".")>0: - hname=hname[:hname.find(".")] + if hname.find(".") > 0: + hname = hname[: hname.find(".")] logfile = "baking.log" lastline = None - if os.path.exists ( logfile ): - f=open( logfile,"rt") + if os.path.exists(logfile): + f = open(logfile, "rt") lines = f.readlines() f.close() - if len(lines)>0: + if len(lines) > 0: lastline = lines[-1].strip() cmd = "" - for i,a in enumerate(sys.argv): - if i>0 and sys.argv[i-1] in [ "-m", "--masses" ]: - a='"%s"' % a - if i>0 and sys.argv[i-1] in [ "--analyses" ]: - a='"%s"' % a + for i, a in enumerate(sys.argv): + if i > 0 and sys.argv[i - 1] in ["-m", "--masses"]: + a = '"%s"' % a + if i > 0 and sys.argv[i - 1] in ["--analyses"]: + a = '"%s"' % a cmd += a + " " cmd = cmd[:-1] if lastline == None or lastline != cmd: - with open(logfile,"a") as f: - f.write ( f"[{hname}] {time.asctime()}:\n{cmd}\n" ) - nReqM = bakeryHelpers.nRequiredMasses ( ns.topo ) - keepOrder=True + with open(logfile, "a") as f: + f.write(f"[{hname}] {time.asctime()}:\n{cmd}\n") + nReqM = bakeryHelpers.nRequiredMasses(ns.topo) + keepOrder = True if ns.topo == "TGQ": - keepOrder=False - masses = bakeryHelpers.parseMasses ( ns.mass, - mingap1=ns.mingap1, maxgap1=ns.maxgap1, - mingap2=ns.mingap2, maxgap2=ns.maxgap2, - mingap13=ns.mingap13, maxgap13=ns.maxgap13 ) + keepOrder = False + masses = bakeryHelpers.parseMasses( + ns.mass, + mingap1=ns.mingap1, + maxgap1=ns.maxgap1, + mingap2=ns.mingap2, + maxgap2=ns.maxgap2, + mingap13=ns.mingap13, + maxgap13=ns.maxgap13, + ) if ns.dry_run: - print ( f"[mg5Wrapper] masses: {masses}" ) + print(f"[mg5Wrapper] masses: {masses}") sys.exit() - import random - random.shuffle ( masses ) + + random.shuffle(masses) nm = len(masses) if nm == 0: line = "[mg5Wrapper] no masses found within the constraints:" @@ -1050,63 +1308,84 @@ class ansi: line += f" gap(1,3) not in [{ns.mingap13},{ns.maxgap13 if ns.maxgap13 is not None else '+inf'}];" if ns.mingap2 != None or ns.maxgap2 != None: line += f" gap(2,3) not in [{ns.mingap2},{ns.maxgap2 if ns.maxgap2 is not None else '+inf'}];" - print ( f"{line}" ) - locker.Locker( ns.sqrts, ns.topo, ns.ignore_locks ).unlock ( masses ) + print(f"{line}") + locker.Locker(ns.sqrts, ns.topo, ns.ignore_locks).unlock(masses) sys.exit() if nReqM != len(masses[0]): - print ( "[mg5Wrapper] you gave %d masses, but %d are required for %s." % \ - ( len(masses[0]), nReqM, ns.topo ) ) + print( + "[mg5Wrapper] you gave %d masses, but %d are required for %s." + % (len(masses[0]), nReqM, ns.topo) + ) sys.exit() - nprocesses = bakeryHelpers.nJobs ( ns.nprocesses, nm ) - - recaster = [ "MA5" ] + nprocesses = bakeryHelpers.nJobs(ns.nprocesses, nm) + + recaster = ["MA5"] if ns.cutlang or ns.checkmate or ns.colliderbit: - recaster = [ "adl" ] + recaster = ["adl"] if ns.checkmate: - recaster = [ "cm2" ] + recaster = ["cm2"] if ns.colliderbit: - recaster = [ "colliderbit" ] + recaster = ["colliderbit"] ns.recast = True if ns.checkmate and ns.cutlang: - print ( "[mg5Wrapper] both checkmate and cutlang have been asked for. please choose!" ) + print( + "[mg5Wrapper] both checkmate and cutlang have been asked for. please choose!" + ) sys.exit() if ns.checkmate and ns.colliderbit: - print ( "[mg5Wrapper] both checkmate and colliderbit have been asked for. please choose!" ) + print( + "[mg5Wrapper] both checkmate and colliderbit have been asked for. please choose!" + ) sys.exit() if ns.cutlang and ns.colliderbit: - print ( "[mg5Wrapper] both cutlang and colliderbit have been asked for. please choose!" ) + print( + "[mg5Wrapper] both cutlang and colliderbit have been asked for. please choose!" + ) sys.exit() - recaster = [ "MA5" ] - mg5 = MG5Wrapper( vars(ns), recaster ) + recaster = ["MA5"] + mg5 = MG5Wrapper(vars(ns), recaster) # mg5.info( "%d points to produce, in %d processes" % (nm,nprocesses) ) - djobs = int(len(masses)/nprocesses) + djobs = int(len(masses) / nprocesses) - def runChunk ( chunk, pid ): + def runChunk(chunk, pid): for c in chunk: - mg5.run ( c, ns.analyses, pid ) - print ( "%s[runChunk] finished chunk #%d%s" % \ - ( ansi.GREEN, pid, ansi.RESET ) ) + mg5.run(c, ns.analyses, pid) + print("%s[runChunk] finished chunk #%d%s" % (ansi.GREEN, pid, ansi.RESET)) - jobs=[] + jobs = [] for i in range(nprocesses): - chunk = masses[djobs*i:djobs*(i+1)] - if i == nprocesses-1: - chunk = masses[djobs*i:] - p = multiprocessing.Process(target=runChunk, args=(chunk,i)) - jobs.append ( p ) + chunk = masses[djobs * i : djobs * (i + 1)] + if i == nprocesses - 1: + chunk = masses[djobs * i :] + p = multiprocessing.Process(target=runChunk, args=(chunk, i)) + jobs.append(p) p.start() for j in jobs: j.join() if ns.bake: import emCreator from types import SimpleNamespace + # analyses = "atlas_susy_2016_07" analyses = ns.analyses - args = SimpleNamespace ( masses="all", topo=ns.topo, njets=ns.njets, \ - analyses = analyses, copy=ns.copy, keep=ns.keep, sqrts=ns.sqrts, - verbose=False, ma5=not ns.cutlang, cutlang=ns.cutlang, stats=True, - cleanup = False, checkmate=ns.checkmate, colliderbit = ns.colliderbit ) - emCreator.run ( args ) - -if __name__ == "__main__": - main() \ No newline at end of file + args = SimpleNamespace( + masses="all", + topo=ns.topo, + njets=ns.njets, + analyses=analyses, + copy=ns.copy, + keep=ns.keep, + sqrts=ns.sqrts, + verbose=False, + ma5=not ns.cutlang, + cutlang=ns.cutlang, + stats=True, + cleanup=False, + checkmate=ns.checkmate, + colliderbit=ns.colliderbit, + ) + emCreator.run(args) + + +if __name__ == "__main__": + main() diff --git a/mg5make.py b/mg5make.py index 817a0b7..432d16e 100755 --- a/mg5make.py +++ b/mg5make.py @@ -1,174 +1,203 @@ #!/usr/bin/env python3 -""" Simple script that handles the installation of MadGraph5, - and its plugins. +"""Simple script that handles the installation of MadGraph5, +and its plugins. """ -import subprocess, os, sys, glob, shutil - -def install_plugins( pyver=3 ): +import subprocess +import os +import sys +import glob +import shutil + + +def install_plugins(pyver=3): ## use modified installer script ## modifyBoostInstaller() - print ( "installing plugins (tail -f /tmp/mg5.install to monitor) ... " ) - f=open("install.script","r") - lines=f.readlines() + print("installing plugins (tail -f /tmp/mg5.install to monitor) ... ") + f = open("install.script", "r") + lines = f.readlines() f.close() for line in lines: - if line[0]=="#": + if line[0] == "#": continue - print ( " - %s" % line.strip() ) - f=open("installing.txt","w") + print(" - %s" % line.strip()) + f = open("installing.txt", "w") f.write(line) f.close() - cmd = "python%d bin/mg5_aMC -f installing.txt 2>&1 | tee /tmp/mg5.install" % pyver - subprocess.getoutput ( cmd ) - if os.path.exists ( "installing.txt" ): - os.unlink ( "installing.txt" ) + cmd = ( + "python%d bin/mg5_aMC -f installing.txt 2>&1 | tee /tmp/mg5.install" % pyver + ) + subprocess.getoutput(cmd) + # if os.path.exists ( "installing.txt" ): + # os.unlink ( "installing.txt" ) protectPythia8Install() addRPVMSSM() + def installExtraModels(): cmd = "cp -r ../models/* ./models/" - o = subprocess.getoutput ( cmd ) - print ( f"[installModels] {cmd}: {o}" ) + o = subprocess.getoutput(cmd) + print(f"[installModels] {cmd}: {o}") + -#def createPythia8Backup(): +# def createPythia8Backup(): # if not os.path.exists ( "backup" ): # os.mkdir ( "backup" ) -def install( ver, plugins = True, pyver = 3 ): + +def install(ver, plugins=True, pyver=3): """ :param ver: MG5 version (eg 3_5_1) :param plugins: install also plugins :param pyver: python version, 2 or 3 """ - if os.path.exists ( "bin/mg5_aMC" ): + if os.path.exists("bin/mg5_aMC"): ## seems like we have an install - if not os.path.exists ( "HEPTools" ): - install_plugins( pyver ) + if not os.path.exists("HEPTools"): + install_plugins(pyver) else: - print ( "[make.py] everything seems to be installed. Remove HEPTools or bin/mg5_aMC if you wish to trigger a reinstall" ) + print( + "[make.py] everything seems to be installed. Remove HEPTools or bin/mg5_aMC if you wish to trigger a reinstall" + ) return - if os.path.exists ( "bin" ): + if os.path.exists("bin"): ## bin exists, but not bin/mg5_aMC, make clean clean() - print ( "installing mg5 ..." ) - verdot = ver.replace("_",".") - url="https://smodels.github.io/downloads/tarballs/" + print("installing mg5 ...") + verdot = ver.replace("_", ".") + url = "https://smodels.github.io/downloads/tarballs/" tarball = "MG5_aMC_v%s.tar.gz" % verdot if pyver == 4: tarball = "MG5_aMC_v%s.py3.tar.gz" % verdot if verdot >= "3.5.2": tarball = "mg5amcnlo-%s.tar.gz" % verdot - if not os.path.exists ( tarball ): - cmd = "wget %s/%s" % ( url, tarball ) - a = subprocess.getoutput ( cmd ) - if not os.path.exists ( tarball ): - print ( "download failed: %s" % a ) + if not os.path.exists(tarball): + cmd = "wget %s/%s" % (url, tarball) + a = subprocess.getoutput(cmd) + if not os.path.exists(tarball): + print("download failed: %s" % a) sys.exit() cmd = "tar xzvf %s" % tarball # print ( f"cmd: {cmd}" ) - subprocess.getoutput ( cmd ) + subprocess.getoutput(cmd) foldername = f"MG5_aMC_v{ver}" - #print ( f"ver {ver}") + # print ( f"ver {ver}") if ver >= "3_5_2": foldername = f"mg5amcnlo-{verdot}" cmd = f"mv {foldername}/* ." if pyver == 4: cmd = f"mv {foldername}_py3/* ." - #print ( f"cmd: {cmd}" ) - subprocess.getoutput ( cmd ) + # print ( f"cmd: {cmd}" ) + subprocess.getoutput(cmd) cmd = f"rm -r {foldername}" if pyver == 4: cmd += "_py3" # cmd = "rmdir MG5_aMC_v%s_py3" % ver - #print ( f"cmd: {cmd}" ) - subprocess.getoutput ( cmd ) - if not os.path.exists ( "bin/mg5_aMC" ): - print ( "something went wrong with the install. please check manually" ) + # print ( f"cmd: {cmd}" ) + subprocess.getoutput(cmd) + if not os.path.exists("bin/mg5_aMC"): + print("something went wrong with the install. please check manually") sys.exit() if plugins: - install_plugins( pyver ) + install_plugins(pyver) + def addRPVMSSM(): - """ add the rpv mssm model. """ + """add the rpv mssm model.""" cmd = "cd mg5/models; wget https://feynrules.irmp.ucl.ac.be/raw-attachment/wiki/RPVMSSM/af1_ufo.tgz; tar xzvf af1_ufo.tgz" - o = subprocess.getoutput ( cmd ) - print ( o ) + o = subprocess.getoutput(cmd) + print(o) + def protectPythia8Install(): - """ protect the pythia8 files, sometimes they get magically deleted. """ - cmd = f"chmod -R u-w mg5/HEPTools/pythia8/" - o = subprocess.getoutput ( cmd ) + """protect the pythia8 files, sometimes they get magically deleted.""" + cmd = "chmod -R u-w mg5/HEPTools/pythia8/" + o = subprocess.getoutput(cmd) + def unprotectPythia8Install(): - """ unprotect the pythia8 files """ - cmd = f"chmod -R u+w mg5/HEPTools/pythia8/" - o = subprocess.getoutput ( cmd ) + """unprotect the pythia8 files""" + cmd = "chmod -R u+w mg5/HEPTools/pythia8/" + o = subprocess.getoutput(cmd) + def modifyBoostInstaller(): ## seems to get overwritten again boostscript = "HEPTools/HEPToolsInstallers/installBOOST.sh" - if not os.path.exists ( boostscript ): + if not os.path.exists(boostscript): return - f=open(boostscript,"r") - lines=f.readlines() + f = open(boostscript, "r") + lines = f.readlines() f.close() - f=open("/tmp/boostinstaller","w") + f = open("/tmp/boostinstaller", "w") for line in lines: - f.write ( line.replace("b2 install", "b2 -j`nproc` install" ) ) + f.write(line.replace("b2 install", "b2 -j`nproc` install")) f.close() cmd = "cp /tmp/boostinstaller %s" % boostscript - a=subprocess.getoutput ( cmd ) + a = subprocess.getoutput(cmd) cmd = f"chmod 500 {boostscript}" - a2=subprocess.getoutput ( cmd ) - print ( f"[mg5make.py] cmd {cmd} {a} {a2} {os.getcwd()}" ) + a2 = subprocess.getoutput(cmd) + print(f"[mg5make.py] cmd {cmd} {a} {a2} {os.getcwd()}") + def trim(): - """ trim the install down to what is needed """ - files = list ( glob.glob ( "MG5_aMC*" ) ) - files += [ "tests", "MadSpin/src/", "doc.tgz" ] - files += glob.glob ( "HEPTools/*/include/*" ) - files += glob.glob ( "**/src/", recursive=True ) - files += glob.glob ( "**/*.F", recursive=True ) + """trim the install down to what is needed""" + files = list(glob.glob("MG5_aMC*")) + files += ["tests", "MadSpin/src/", "doc.tgz"] + files += glob.glob("HEPTools/*/include/*") + files += glob.glob("**/src/", recursive=True) + files += glob.glob("**/*.F", recursive=True) # files += glob.glob ( "**/*.f", recursive=True ) - files += glob.glob ( "**/examples/", recursive=True ) + files += glob.glob("**/examples/", recursive=True) for f in files: - if not os.path.exists ( f ): + if not os.path.exists(f): continue - if os.path.isdir ( f ): - shutil.rmtree ( f ) + if os.path.isdir(f): + shutil.rmtree(f) else: - os.unlink ( f ) + os.unlink(f) + def clean(): - print ( "cleaning up ... " ) - for f in glob.glob ( "*" ): - if f not in [ "make.py", "install.script", "Makefile" ]: + print("cleaning up ... ") + for f in glob.glob("*"): + if f not in ["make.py", "install.script", "Makefile"]: cmd = "rm -rf %s" % f - subprocess.getoutput ( cmd ) + subprocess.getoutput(cmd) + if __name__ == "__main__": import inspect - D = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) - os.chdir ( D ) + + D = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + os.chdir(D) import argparse + argparser = argparse.ArgumentParser( - description='a utility script to help build MG5' ) - argparser.add_argument ( '--clean', help='clean all cruft files', action="store_true" ) - argparser.add_argument ( '--trim', help='trim the install', action="store_true" ) - argparser.add_argument ( '--models', help='install the extra models', action="store_true" ) - argparser.add_argument ( '--plugins', help='build the plugins', action="store_true" ) - argparser.add_argument ( '--noplugins', help='dont build the plugins, only the binary', - action="store_true" ) - argparser.add_argument ( '-p', '--pyver', help='python version [3]', - type=int, default=3 ) - argparser.add_argument ( '-V', '--version', help='MG5 version [3_5_1]', - type=str, default="3_5_1" ) + description="a utility script to help build MG5" + ) + argparser.add_argument("--clean", help="clean all cruft files", action="store_true") + argparser.add_argument("--trim", help="trim the install", action="store_true") + argparser.add_argument( + "--models", help="install the extra models", action="store_true" + ) + argparser.add_argument("--plugins", help="build the plugins", action="store_true") + argparser.add_argument( + "--noplugins", + help="dont build the plugins, only the binary", + action="store_true", + ) + argparser.add_argument( + "-p", "--pyver", help="python version [3]", type=int, default=3 + ) + argparser.add_argument( + "-V", "--version", help="MG5 version [3_5_1]", type=str, default="3_5_1" + ) args = argparser.parse_args() - args.version = args.version.replace(".","_") + args.version = args.version.replace(".", "_") if args.trim: trim() sys.exit() @@ -176,11 +205,11 @@ def clean(): clean() sys.exit() if args.plugins: - install_plugins( args.pyver ) + install_plugins(args.pyver) sys.exit() if args.models: installExtraModels() plugins = True if args.noplugins: - plugins = False - install( args.version, plugins=plugins, pyver= args.pyver ) + plugins = False + install(args.version, plugins=plugins, pyver=args.pyver) diff --git a/requirements.txt b/requirements.txt index d2a2b24..66c22d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ colorama numpy smodels -ROOT