def _getBestResult(dataSetResults): """ Returns the best result according to the expected upper limit :param datasetPredictions: list of TheoryPredictionList objects :return: best result (TheoryPrediction object) """ #In the case of UL analyses or efficiency-maps with a single signal region #return the single result: if len(dataSetResults) == 1: return dataSetResults[0] #For efficiency-map analyses with multipler signal regions, #select the best one according to the expected upper limit: bestExpectedR = 0. bestXsec = 0.*fb for predList in dataSetResults: if len(predList) != 1: logger.error("Multiple clusters should only exist for upper limit results!") raise SModelSError() dataset = predList[0].dataset if dataset.getType() != 'efficiencyMap': txt = "Multiple data sets should only exist for efficiency map results, but we have them for %s?" % (predList[0].analysisId()) logger.error( txt ) raise SModelSError( txt ) pred = predList[0] xsec = pred.xsection expectedR = (xsec.value/dataset.getSRUpperLimit(0.05,True,False) ).asNumber() if expectedR > bestExpectedR or (expectedR == bestExpectedR and xsec.value > bestXsec): bestExpectedR = expectedR bestPred = pred bestXsec = xsec.value return bestPred
def _evalConstraint(cluster): """ Evaluate the constraint inside an element cluster. If the cluster refers to a specific TxName, sum all the elements' weights according to the analysis constraint. For efficiency map results, sum all the elements' weights. :parameter cluster: cluster of elements (ElementCluster object) :returns: cluster cross section """ if cluster.getDataType() == 'efficiencyMap': return cluster.getTotalXSec() elif cluster.getDataType() == 'upperLimit': if len(cluster.txnames) != 1: logger.error("An upper limit cluster should never contain more than one TxName") raise SModelSError() txname = cluster.txnames[0] if not txname.constraint or txname.constraint == "not yet assigned": return txname.constraint exprvalue = _evalExpression(txname.constraint,cluster) return exprvalue else: logger.error("Unknown data type %s" %(str(cluster.getDataType()))) raise SModelSError()
def getDataType(self): """ Checks to which type of data (efficiency map or upper limit) the cluster refers to. It uses the cluster.txnames attribute. If not defined, returns None :return: upperLimits or efficiencyMap (string) """ if not hasattr(self, 'txnames') or not self.txnames: return None else: #Check the data types #for txname in self.txnames: # if not txname.txnameData._data: # txname.txnameData.loadData() #Make sure the _data is loaded dataTag = list(set([txname.txnameData.dataTag for txname in self.txnames])) if len(dataTag) != 1: logger.error("A single cluster contain mixed data types!") raise SModelSError() elif 'upperLimit' in dataTag[0]: return 'upperLimit' elif 'efficiencyMap' in dataTag[0]: return 'efficiencyMap' else: logger.error("Unknown data type %s" % (dataTag[0])) raise SModelSError()
def setMasses(self, mass, sameOrder=True, opposOrder=False): """ Set the element masses to the input mass array. :parameter mass: list of masses ([[masses for branch1],[masses for branch2]]) :parameter sameOrder: if True, set the masses to the same branch ordering If True and opposOrder=True, set the masses to the smaller of the two orderings. :parameter opposOrder: if True, set the masses to the opposite branch ordering. If True and sameOrder=True, set the masses to the smaller of the two orderings. """ if sameOrder and opposOrder: newmass = sorted(mass) elif sameOrder: newmass = mass elif opposOrder: newmass = [mass[1], mass[0]] else: logger.error("Called with no possible ordering") raise SModelSError() if len(newmass) != len(self.branches): logger.error("Called with wrong number of mass branches") raise SModelSError() for i, mass in enumerate(newmass): self.branches[i].masses = mass[:]
def __init__(self, smodelsFolder, slhaFolder, parameterFile, indexfile): """ Initializes the class. :parameter smodelsFolder: path to the folder containing the smodels (python) output files :parameter slhaFolder: path to the folder containing the SLHA input files :parameter parameterFile: path to the file containing the plotting definitions """ self.data_dict = [] self.smodelsFolder = smodelsFolder self.slhaFolder = slhaFolder self.indexfile = indexfile self.parameterFile = parameterFile self.slha_hover_information = None self.ctau_hover_information = None self.BR_hover_information = None self.SModelS_hover_information = None self.plot_data = None self.variable_x = None self.variable_y = None self.plot_list = None self.BR_get_top = None if not os.path.isfile(parameterFile): raise SModelSError('Parameters file %s not found' % parameterFile) if not os.path.isdir(smodelsFolder): raise SModelSError("Folder %s not found" % smodelsFolder) if not os.path.isdir(slhaFolder): raise SModelSError("Folder %s not found" % slhaFolder) self.loadParameters() self.initializeDataDict()
def __init__(self, info=None): """ Initializes the branch. If info is defined, tries to generate the branch using it. :parameter info: string describing the branch in bracket notation (e.g. [[e+],[jet]]) """ self.masses = [] self.particles = [] self.PIDs = [] self.maxWeight = None self.vertnumb = None self.vertparts = None if type(info) == type(str()): branch = elementsInStr(info) if not branch or len(branch) > 1: logger.error("Wrong input string " + info) raise SModelSError() else: branch = branch[0] vertices = elementsInStr(branch[1:-1]) for vertex in vertices: ptcs = vertex[1:-1].split(',') # Syntax check: for ptc in ptcs: if not ptc in rEven.values() \ and not ptc in ptcDic: logger.error("Unknown particle. Add " + ptc + " to smodels/particle.py") raise SModelSError() self.particles.append(ptcs) self.vertnumb = len(self.particles) self.vertparts = [len(v) for v in self.particles]
def elementsInStr(instring, removeQuotes=True): """ Parse instring and return a list of elements appearing in instring. instring can also be a list of strings. :param instring: string containing elements (e.g. "[[['e+']],[['e-']]]+[[['mu+']],[['mu-']]]") :param removeQuotes: If True, it will remove the quotes from the particle labels. Set to False, if one wants to run eval on the output. :returns: list of elements appearing in instring in string format """ outstr = "" if isinstance(instring, str): outstr = instring elif isinstance(instring, list): for st in instring: if not isinstance(st, str): logger.error("Input must be a string or a list of strings") raise SModelSError() # Combine list of strings in a single string outstr += st else: raise SModelSError ( "syntax error in constraint/condition: ``%s''." \ "Check your constraints and conditions in your database." % str(instring) ) elements = [] outstr = outstr.replace(" ", "") if removeQuotes: outstr = outstr.replace("'", "") elStr = "" nc = 0 # Parse the string and looks for matching ['s and ]'s, when the matching is # complete, store element for c in outstr: delta = 0 if c == '[': delta = -1 elif c == ']': delta = 1 nc += delta if nc != 0: elStr += c if nc == 0 and delta != 0: elements.append(elStr + c) elStr = "" # Syntax checks ptclist = elements[-1].replace(']', ',').replace('[', ',').\ split(',') for ptc in ptclist: ptc = ptc.replace("'", "") if not ptc: continue # Check if there are not unmatched ['s and/or ]'s in the string if nc != 0: raise SModelSError("Wrong input (incomplete elements?) " + instring) return elements
def simParticles(plist1, plist2, useDict=True): """ Compares two lists of particle names. Allows for dictionary labels (Ex: L = l, l+ = l, l = l-,...). Ignores particle ordering inside the list :param plist1: first list of particle names, e.g. ['l','jet'] :param plist2: second list of particle names :param useDict: use the translation dictionary, i.e. allow e to stand for e+ or e-, l+ to stand for e+ or mu+, etc :returns: True/False if the particles list match (ignoring order) """ if not isinstance(plist1, list) or type(plist1) != type(plist2): logger.error("Input must be a list") raise SModelSError() if len(plist1) != len(plist2): return False for i, p in enumerate(plist1): if not isinstance(p, str) or not isinstance(plist2[i], str): logger.error("Input must be a list of particle strings") raise SModelSError() elif not p in list(ptcDic.keys()) + list(rEven.values()): logger.error("Unknow particle: %s" % p) raise SModelSError() elif not plist2[i] in list(ptcDic.keys()) + list(rEven.values()): logger.error("Unknow particle: %s" % plist2[i]) raise SModelSError() l1 = sorted(plist1) l2 = sorted(plist2) if not useDict: return l1 == l2 #If dictionary is to be used, replace particles by their dictionay entries #e.g. [jet,mu+] -> [[q,g,c],[mu+]], [jet,mu] -> [[q,g,c],[mu+,mu-]] extendedL1 = [] for i, p in enumerate(plist1): if not p in ptcDic: extendedL1.append([p]) else: extendedL1.append(ptcDic[p]) extendedL2 = [] for i, p in enumerate(plist2): if not p in ptcDic: extendedL2.append([p]) else: extendedL2.append(ptcDic[p]) #Generate all combinations of particle lists (already sorted to avoid ordering issues) #e.g. [[q,g,c],[mu+]] -> [[q,mu+],[g,mu+],[c,mu+]] extendedL1 = [sorted(list(i)) for i in itertools.product(*extendedL1)] extendedL2 = [sorted(list(i)) for i in itertools.product(*extendedL2)] #Now compare the two lists and see if there is a match: for plist in extendedL1: if plist in extendedL2: return True return False
def elementsInStr(instring): """ Parse instring and return a list of elements appearing in instring. instring can also be a list of strings. :returns: list of elements appearing in instring in string format """ outstr = "" if type(instring) == type('st'): outstr = instring elif type(instring) == type([]): for st in instring: if type(st) != type('st'): logger.error("Input must be a string or a list of strings") raise SModelSError() # Combine list of strings in a single string outstr += st else: raise SModelSError ( "syntax error in constraint/condition: ``%s''." \ "Check your constraints and conditions in your database." % str(instring) ) elements = [] outstr = outstr.replace(" ", "").replace("'", "") elStr = "" nc = 0 # Parse the string and looks for matching ['s and ]'s, when the matching is # complete, store element for c in outstr: delta = 0 if c == '[': delta = -1 elif c == ']': delta = 1 nc += delta if nc != 0: elStr += c if nc == 0 and delta != 0: elements.append(elStr + c) elStr = "" # Syntax checks ptclist = elements[-1].replace(']', ',').replace('[', ',').\ split(',') for ptc in ptclist: if not ptc: continue if not ptc in rEven.values() and not ptc in ptcDic: logger.error("Unknown particle. Add " + ptc + " to smodels/particles.py") raise SModelSError() # Check if there are not unmatched ['s and/or ]'s in the string if nc != 0: logger.error("Wrong input (incomplete elements?) " + instring) raise SModelSError() return elements
def _checkSLHA(self, slhafile): if not os.path.isfile(slhafile): logger.error("SLHA file %s not found.", slhafile) raise SModelSError() try: f = pyslha.readSLHAFile(slhafile) except pyslha.ParseError as e: logger.error("File cannot be parsed as SLHA file: %s" % e) raise SModelSError()
def vertInStr(instring): """ Parses instring (or a list of strings) and returns the list of particle vertices appearing in instring. """ if type(instring) == type('st'): outstr = instring elif type(instring) == type([]): outstr = "" for st in instring: if type(st) != type('st'): logger.error("Input must be a string or a list of strings") raise SModelSError() # Combine list of strings in a single string outstr += st vertices = [] outstr = outstr.replace(" ", "").replace("'", "") vertStr = "" nc = 0 # Parse the string and looks for matching ['s and ]'s, when the matching is # complete, store element for c in outstr: delta = 0 if c == '[': delta = -1 elif c == ']': delta = 1 nc += delta if c == '[': vertStr = "" if nc != 0 and c != '[' and c != ']': vertStr += c if delta > 0 and vertStr: vertices.append(vertStr.split(',')) # Syntax checks: for ptc in vertices[-1]: if not ptc: continue if not ptc in rEven.values() and not ptc in ptcDic: logger.error("Unknown particle. Add " + ptc + " to smodels/particle.py") raise SModelSError() vertStr = "" # Check if there are not unmatched ['s and/or ]'s in the string if nc != 0: logger.error("Wrong input (incomplete elements?) " + instring) raise SModelSError() return vertices
def __init__(self, finalState=None, intermediateState=None, model=None): """ :parameter info: string describing the branch in bracket notation (e.g. [[e+],[jet]]) :parameter finalState: final state label string for the branch (e.g. 'MET' or 'HSCP') :parameter intermediateState: list containing intermediate state labels (e.g. ['gluino','C1+']) :parameter model: The model (Model object) to be used when converting particle labels to particle objects (only used if info, finalState or intermediateState != None). """ Branch.__init__(self) self.mass = InclusiveList() self.totalwidth = InclusiveList() self.evenParticles = InclusiveList() self.oddParticles = [] #Get labels of intermediate states if intermediateState: if not isinstance(intermediateState, list): raise SModelSError( "Intermediate state (``%s'') should be a list)" % intermediateState) bsmLabels = intermediateState[:] else: bsmLabels = [] if finalState: bsmLabels.append(finalState) else: bsmLabels.append('anyOdd') for bsmLabel in bsmLabels: bsmParticle = model.getParticlesWith(label=bsmLabel) if not bsmParticle: raise SModelSError( "BSM particle ``%s'' has not been defined in model %s" % (bsmLabel, model)) elif len(bsmParticle) != 1: raise SModelSError( "Ambiguous definition of label ``%s'' in model %s" % (bsmLabel, model)) else: self.oddParticles.append(bsmParticle[0]) #If intintermediateState is defined, the number of vertexParticles #(odd particles) must be used for comparison. Otherwise accept any number. if intermediateState: self.vertnumb = len(intermediateState) else: self.vertnumb = InclusiveValue() self.vertparts = InclusiveList()
def makePlots(smodelsFolder, slhaFolder, outputFolder, parameters, npoints, verbosity, indexfile="index.html"): """ Main interface for the interactive-plots. :parameter smodelsFolder: Path to the folder containing the SModelS python output :parameter slhaFolder: Path to the folder containing the SLHA files corresponding to the SModelS output :parameter parameters: Path to the parameter file setting the options for the interactive plots :parameter npoints: Number of points used to produce the plot. If -1, all points will be used. :parameter verbosity: Verbosity of the output (debug,info,warning,error) :parameter indexfile: name of the starting web page (index.html) :return: True if the plot creation was successfull """ try: import plotly except ImportError: raise SModelSError( "Plotly is not installed. To use this tool, please install plotly") try: import pandas except ImportError: raise SModelSError( "Pandas is not installed. To use this tool, please install pandas") setLogLevel(verbosity) #Basic checks: smodelsFolder = smodelsFolder slhaFolder = slhaFolder parFile = parameters dataHolder = DataHolder(smodelsFolder, slhaFolder, parFile, indexfile) loadData = dataHolder.loadData(npoints) if not loadData: raise SModelSError("Error loading data from folders:\n %s\n %s" % (smodelsFolder, slhaFolder)) dataHolder.makePlots(outputFolder) return outputFolder
def removeInclusives(massArray, shapeArray): """ Remove all entries corresponding to '*' in shapeArray. If shapeArray contains entries = '*', the corresponding entries in value will be removed from the output. :param massArray: Array to be formatted (e.g. [[200.,100.],[200.,100.]] or [[200.,'*'],'*'],0.4]) :param shapeArray: Array with format info (e.g. ['*',[float,float]]) :return: formatted array (e.g. [[200.,100.]] or [[200.]],0.4]) """ if shapeArray == '*': return None elif isinstance(massArray, list): if len(shapeArray) != len(massArray): raise SModelSError("Input value and data shape mismatch (%s,%s)" % (len(shapeArray), len(massArray))) else: return [ removeInclusives(xi, shapeArray[i]) for i, xi in enumerate(massArray) if not removeInclusives(xi, shapeArray[i]) is None ] else: return massArray
def _evalConditions(cluster): """ Evaluate the conditions (if any) inside an element cluster. :parameter cluster: cluster of elements (ElementCluster object) :returns: list of condition values (floats) if analysis type == upper limit. None, otherwise. """ conditionVals = {} for txname in cluster.txnames: if not txname.condition or txname.condition == "not yet assigned": continue #Make sure conditions is always a list if isinstance(txname.condition,str): conditions = [txname.condition] elif isinstance(txname.condition,list): conditions = txname.condition else: logger.error("Conditions should be a list or a string") raise SModelSError() # Loop over conditions for cond in conditions: exprvalue = _evalExpression(cond,cluster) if isinstance(exprvalue,crossSection.XSection): conditionVals[cond] = exprvalue.value else: conditionVals[cond] = exprvalue if not conditionVals: return None else: return conditionVals
def xsecToBlock(xsec, inPDGs=(2212, 2212), comment=None, xsecUnit=pb): """ Generate a string for a XSECTION block in the SLHA format from a XSection object. :param inPDGs: defines the PDGs of the incoming states (default = 2212,2212) :param comment: is added at the end of the header as a comment :param xsecUnit: unit of cross sections to be written (default is pb). Must be a Unum unit. """ if type(xsec) != type(crossSection.XSection()): logger.error("Wrong input") raise SModelSError() # Sqrt(s) in GeV header = "XSECTION " + str(xsec.info.sqrts / GeV) for pdg in inPDGs: # PDGs of incoming states header += " " + str(pdg) # Number of outgoing states header += " " + str(len(xsec.pid)) for pid in xsec.pid: # PDGs of outgoing states header += " " + str(pid) if comment: header += " # " + str(comment) # Comment entry = " 0 " + str(xsec.info.order) + " 0 0 0 0 " + \ str("%16.8E" % (xsec.value / xsecUnit) ) + " SModelS " + installation.version() return "\n" + header + "\n" + entry
def __add__(self, other): """ Adds two branches. Should only be used if the branches have the same topologies. The odd and even particles are combined. """ if self.getInfo() != other.getInfo(): raise SModelSError("Can not add branches with distinct topologies") newBranch = self.__class__() #Combine odd particles for iptc, ptc in enumerate(self.oddParticles): newBranch.oddParticles.append(ptc + other.oddParticles[iptc]) #Combine even particles (if they are the same nothing changes) for iv, vertex in enumerate(self.evenParticles): vertexParticles = [] for iptc, ptc in enumerate(vertex): vertexParticles.append(ptc + other.evenParticles[iv][iptc]) vertexParticles = ParticleList(vertexParticles) newBranch.evenParticles.append(vertexParticles) if not self.maxWeight is None and not other.maxWeight is None: newBranch.maxWeight = self.maxWeight + other.maxWeight return newBranch
def setFinalState(self, finalState=None): """ If finalState = None, define the branch final state according to the PID of the last R-odd particle appearing in the cascade decay. Else set the final state to the finalState given :parameter finalState: String defining the final state """ if finalState: if finalState == '*': finalState = InclusiveStr() if not finalState in list(finalStates.keys()): raise SModelSError( "Final state %s has not been defined. Add it to particles.py." % finalState) else: self.finalState = finalState #If PIDs have been defined, use it: elif self.PIDs: fStates = set() for pidList in self.PIDs: fStates.add(getFinalStateLabel(pidList[-1])) if len(fStates) != 1: logger.error("Error obtaining the final state for branch %s" % self) raise SModelSError else: self.finalState = list(fStates)[0] #Else do nothing else: self.finalState = None
def groupElements(elements,dataset): """ Group elements into groups where the average element identical to all the elements in group. The groups contain all elements which share the same mass,width and upper limit and can be replaced by their average element when building clusters. :parameter elements: list of all elements to be grouped :parameter dataset: Dataset object to be used when computing distances in upper limit space :returns: a list of AverageElement objects which represents a group of elements with same mass, width and upper limit. """ # First make sure all elements contain their upper limits for el in elements: if not hasattr(el,'._upperLimit'): el._upperLimit = dataset.getUpperLimitFor(el,txnames=el.txname) if el._upperLimit is None: raise SModelSError("Trying to cluster element outside the grid.") #Group elements if they have the same UL #and give the same average element (same mass and same width) avgElements = [] for iA,elA in enumerate(elements): avgEl = AverageElement([elA]) avgEl._upperLimit = elA._upperLimit for iB,elB in enumerate(elements): if iB <= iA: continue if elA._upperLimit != elB._upperLimit: continue if avgEl != elB: continue avgEl.elements.append(elB) avgEl.weight += elB.weight if not avgEl in avgElements: avgElements.append(avgEl) #Make sure each element belongs to a average element: for el in elements: nclusters = sum([avgEl.contains(el) for avgEl in avgElements]) if nclusters != 1: raise SModelSError("Error computing average elements. Element %s belongs to %i average elements." %(str(el),nclusters)) return avgElements
def findIllegalDecay(self, findIllegal): """ Find decays for which the sum of daughter masses excels the mother mass :parameter findIllegal: True if check should be run :returns: status flag and message """ if not findIllegal: return 0, "Did not check for illegal decays" st = 1 badDecay = "Illegal decay for PIDs " for particle, block in self.slha.decays.items(): if particle in SMpdgs: continue if not particle in self.slha.blocks["MASS"].keys(): continue mMom = abs(self.slha.blocks["MASS"][particle]) for dcy in block.decays: mDau = 0. for ptc in dcy.ids: ptc = abs(ptc) if ptc in SMpdgs: smParticle = self.model.getParticlesWith(pdg=ptc) if not smParticle: raise SModelSError( "Particle with PDG = %i could not be found." % ptc) elif len(smParticle) != 1: raise SModelSError( "Multiple particles defined with PDG = %i in model" % ptc) else: smParticle = smParticle[0] mDau += smParticle.mass / GeV elif ptc in self.slha.blocks["MASS"].keys(): mDau += abs(self.slha.blocks["MASS"][ptc]) else: return -2, "Unknown PID %s in decay of %s" % ( str(ptc), str(particle) + ". Add " + str(ptc) + " to smodels/particle.py") if mDau > mMom: st = -1 if not str(particle) in badDecay: badDecay += str(particle) + " " if st == 1: badDecay = "No illegal decay blocks" return st, badDecay
def clusterElements(elements, maxDist, dataset): """ Cluster the original elements according to their distance in upper limit space. :parameter elements: list of elements (Element objects) :parameter dataset: Dataset object to be used when computing distances in upper limit space :parameter maxDist: maximum distance for clustering two elements :returns: list of clusters (ElementCluster objects) """ if len(elements) == 0: return [] if any(not isinstance(el,Element) for el in elements): raise SModelSError("Asked to cluster non Element objects") if not isinstance(dataset,(DataSet,CombinedDataSet)): raise SModelSError("A dataset object must be defined for clustering") txnames = list(set([el.txname for el in elements])) if dataset.getType() == 'upperLimit' and len(txnames) != 1 : logger.error("Clustering elements with different Txnames for an UL result.") raise SModelSError() #Make sure only unique elements are clustered together (avoids double counting weights) #Sort element, so the ones with highest contribution (weight*eff) come first: elementList = sorted(elements, key = lambda el: el.weight.getMaxXsec()*el.eff, reverse=True) #Remove duplicated elements: elementsUnique = [] for el in elementList: #Skip the element if it is related to any another element in the list if any(el.isRelatedTo(elB) for elB in elementsUnique): continue elementsUnique.append(el) if dataset.getType() == 'upperLimit': #Group according to upper limit values clusters = doCluster(elementsUnique, dataset, maxDist) elif dataset.getType() == 'efficiencyMap': #Group all elements together distanceMatrix = np.zeros((len(elementsUnique),len(elementsUnique))) cluster = ElementCluster(dataset=dataset,distanceMatrix=distanceMatrix) for iel,el in enumerate(elementsUnique): el._index = iel cluster.elements = elementsUnique clusters = [cluster] for cluster in clusters: cluster.txnames = txnames return clusters
def __getitem__(self, index): if len(self) <= index: txt = "Index in XSectionList out of bounds: idx(%d)>=length(%d). " \ "(Are there cross sections given in the input?)" % \ ( index, len(self) ) logger.error(txt) raise SModelSError(txt) return self.xSections[index]
def __init__(self, info=None, finalState=None): """ Initializes the branch. If info is defined, tries to generate the branch using it. :parameter info: string describing the branch in bracket notation (e.g. [[e+],[jet]]) :parameter finalState: final state label string for the branch (e.g. 'MET' or 'HSCP') """ from smodels.particlesLoader import rEven self.masses = [] self.particles = [] self.PIDs = [] self.maxWeight = None self.vertnumb = None self.vertparts = None self.stable = False self.finalState = None if type(info) == type(str()): branch = elementsInStr(info) if not branch or len(branch) > 1: logger.error("Wrong input string " + info) raise SModelSError() else: branch = branch[0] vertices = elementsInStr(branch[1:-1]) for vertex in vertices: ptcs = vertex[1:-1].split(',') # Syntax check: for i, ptc in enumerate(ptcs): if ptc == "*": ptc = InclusiveStr() ptcs[i] = ptc if not ptc in rEven.values() \ and not ptc in list(ptcDic.keys()): raise SModelSError("Unknown particle. Add " + ptc + " to smodels/particle.py") self.particles.append(ptcs) self.vertnumb = len(self.particles) self.vertparts = [len(v) for v in self.particles] self.setFinalState(finalState)
def decompose(lhefile, inputXsecs=None, nevts=None, doCompress=False, doInvisible=False, minmassgap=-1. * GeV): """ Perform LHE-based decomposition. :param lhefile: LHE file with e.g. pythia events :param inputXsecs: xSectionList object with cross sections for the mothers appearing in the LHE file. If None, use information from file. :param nevts: (maximum) number of events used in the decomposition. If None, all events from file are processed. :param doCompress: mass compression option (True/False) :param doInvisible: invisible compression option (True/False) :param minmassgap: minimum mass gap for mass compression (only used if doCompress=True) :returns: list of topologies (TopologyList object) """ if doCompress and minmassgap < 0. * GeV: logger.error( "Asked for compression without specifying minmassgap. Please set minmassgap." ) raise SModelSError() reader = lheReader.LheReader(lhefile, nevts) smsTopList = topology.TopologyList() # Get cross section from file (= event weight, assuming a common weight for # all events) if not inputXsecs: xSectionList = crossSection.getXsecFromLHEFile(lhefile, addEvents=False) else: xSectionList = inputXsecs # Loop over events and decompose for event in reader: momPDG = tuple(sorted(event.getMom())) # Get mother PDGs eventweight = xSectionList.getXsecsFor(momPDG) # Get event element newElement = elementFromEvent(event, eventweight) if not newElement: continue allElements = [newElement] # Perform compression if doCompress or doInvisible: allElements += newElement.compressElement(doCompress, doInvisible, minmassgap) for el in allElements: el.sortBranches() smsTopList.addElement(el) smsTopList._setElementIds() return smsTopList
def _getDictionariesFromSLHA(slhafile): """ Create mass and BR dictionaries from an SLHA file. Ignore decay blocks with R-parity violating or unknown decays """ from smodels.particlesLoader import rEven, rOdd res = pyslha.readSLHAFile(slhafile) # Get mass and branching ratios for all particles brDic = {} writeIgnoreMessage(res.decays.keys(), rEven, rOdd) for pid in res.decays.keys(): if not pid in rOdd: continue brs = [] for decay in res.decays[pid].decays: nEven = nOdd = 0. for pidd in decay.ids: if pidd in rOdd: nOdd += 1 elif pidd in rEven: nEven += 1 else: logger.warning( "Particle %i not defined in particles.py,decay %i -> [%s] will be ignored" % (pidd, pid, decay.ids)) break if nOdd + nEven == len(decay.ids) and nOdd == 1: brs.append(decay) else: logger.info("Ignoring decay: %i -> [%s]", pid, decay.ids) brsConj = copy.deepcopy(brs) for br in brsConj: br.ids = [-x for x in br.ids] brDic[pid] = brs brDic[-pid] = brsConj # Get mass list for all particles massDic = dict(res.blocks['MASS'].items()) for pid in list(massDic.keys())[:]: massDic[pid] = round(abs(massDic[pid]), 1) * GeV if not -pid in massDic: massDic[-pid] = massDic[pid] #Include proxy for displaced decays if 0 in massDic or 0 in brDic: logger.error( "PDG = 0 is reserved for displaced decays and it can not be used for other particles. Please redefine the input model PDG assignments." ) raise SModelSError() else: dispPid = 0 massDic[dispPid] = 0. * GeV dispDec = pyslha.Decay(br=1., ids=[], nda=0) brDic[dispPid] = [dispDec] return brDic, massDic
def elementFromEvent(event, weight=None): """ Creates an element from a LHE event and the corresponding event weight. :param event: LHE event :param weight: event weight. Must be a XSectionList object (usually with a single entry) or None if not specified. :returns: element """ if not event.particles: logger.error("Empty event") return None brDic, massDic = _getDictionariesFromEvent(event) # Create branch list finalBranchList = [] from smodels.particlesLoader import rOdd, rEven for ip, particle in enumerate(event.particles): keys = list ( rEven.keys() ) + \ list ( rOdd.keys() ) if not particle.pdg in keys: logger.warning( "Particle %i not defined in particles.py, events containing this particle will be ignored" % (particle.pdg)) return None # Particle came from initial state (primary mother) if 1 in particle.moms: mombranch = branch.Branch() mombranch.PIDs = [[particle.pdg]] if weight: mombranch.maxWeight = weight.getMaxXsec() else: mombranch.maxWeight = 0. * fb # Get simple BR and Mass dictionaries for the corresponding branch branchBR = brDic[ip] branchMass = massDic[ip] mombranch.masses = [branchMass[mombranch.PIDs[0][0]]] # Generate final branches (after all R-odd particles have decayed) finalBranchList += branch.decayBranches([mombranch], branchBR, branchMass, sigcut=0. * fb) if len(finalBranchList) != 2: logger.error( str(len(finalBranchList)) + " branches found in event; " "Possible R-parity violation") raise SModelSError() # Create element from event newElement = element.Element(finalBranchList) if weight: newElement.weight = copy.deepcopy(weight) return newElement
def addCommentToFile(self, comment, slhaFile): """ add the optional comment to file """ if comment in [None, ""]: return if not os.path.isfile(slhaFile): logger.error("SLHA file %s not found." % slhaFile) raise SModelSError() outfile = open(slhaFile, 'a') outfile.write("# %s\n" % comment) outfile.close()
def checkConsistency(self): """ Check if the all the elements in elementList are consistent with the topology (same number of vertices and final states) :returns: True if all the elements are consistent. Print error message and exits otherwise. """ for element in self.elementList: info = element.getEinfo() if self.vertnumb != info["vertnumb"]: logger.error("Inconsistent topology.") raise SModelSError() if self.vertparts != info["vertparts"]: logger.error("Inconsistent topology.") raise SModelSError() logger.info("Consistent topology.") return True
def get_slha_file(smodelsDict): """ Returns the file name of the SLHA file corresponding to the output in smodelsDict """ slhaFile = get_entry(smodelsDict, 'OutputStatus', 'input file') if not slhaFile: raise SModelSError() return os.path.basename(slhaFile)
def add(self, newxsec): """ Append a XSection object to the list. """ if type(newxsec) != type(XSection()): logger.error("Input object must be a XSection() object") raise SModelSError() else: self.xSections.append(newxsec.copy())