def testElementWildcard(self): el1 = element.Element(info='[[[e+],[e-,mu+]],[[*],[e-,mu+]]]', finalState=['MET', 'HSCP']) el2 = element.Element(info='[[[e+],[e-,mu+]],[[jet],[e-,mu+]]]', finalState=['*', 'HSCP']) el3 = element.Element(info='[[[e+],[e-,mu+]],[[jet],[e-,mu+]]]', finalState=['HSCP', 'HSCP']) el4 = element.Element(info='[[*],[[jet],[e-,mu+]]]', finalState=['MET', 'HSCP']) el5 = element.Element(info='[[[e+],[e-,mu+]],[[jet],[e-,mu+]]]', finalState=['MET', 'HSCP']) el6 = element.Element(info='[[*],[*]]', finalState=['MET', 'HSCP']) el7 = element.Element(info='[[[e+],[e-,mu+]],[[jet,jet],[e-,mu+]]]', finalState=['MET', 'HSCP']) self.assertTrue(el1 == el2) self.assertTrue(el2 == el3) self.assertTrue(el3 != el4) self.assertTrue(el2 == el4) self.assertTrue(el1 == el4) self.assertTrue(el1 != el3) self.assertTrue(el6 == el1) self.assertTrue(el6 == el2) self.assertTrue(el5 == el1) self.assertTrue(el5 != el3) self.assertTrue(el7 != el1) self.assertTrue(el1 != el7) self.assertTrue(str(el1) == "[[[e+],[e-,mu+]],[[*],[e-,mu+]]]") self.assertTrue(str(el6) == "[[*],[*]]")
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 testInclusiveTxName(self): f = './database/13TeV/CMS/CMS-PAS-EXO-16-036-eff/c000/THSCPM2.txt' gInfo = infoObj.Info('./database/13TeV/CMS/CMS-PAS-EXO-16-036-eff/globalInfo.txt') gInfo.addInfo('dataId','c000') tx = TxName(f,gInfo,gInfo,finalStates) el = element.Element(info="[[],[[e+]]]",finalState = ['HSCP','MET'], model=finalStates) newEl = tx.hasElementAs(el) #newEl should be equal to el, but with opposite branch ordering self.assertFalse(newEl is None) bsmParticles = [[str(bsm) for bsm in br] for br in newEl.oddParticles] self.assertTrue(bsmParticles == [['anyOdd','MET'],['HSCP']])
def _evalExpression(stringExpr,cluster): """ Auxiliary method to evaluate a string expression using the weights of the elements in the cluster. Replaces the elements in stringExpr (in bracket notation) by their weights and evaluate the expression. e.g. computes the total weight of string expressions such as "[[[e^+]],[[e^-]]]+[[[mu^+]],[[mu^-]]]" or ratios of weights of string expressions such as "[[[e^+]],[[e^-]]]/[[[mu^+]],[[mu^-]]]" and so on... :parameter stringExpr: string containing the expression to be evaluated :parameter cluster: cluster of elements (ElementCluster object) :returns: xsection for the expression. Can be a XSection object, a float or not numerical (None,string,...) """ #Get model for final state particles (database particles): model = cluster.dataset.globalInfo._databaseParticles #Get txname final state: if not hasattr(cluster.txnames[0],'finalState'): finalState = ['MET','MET'] else: finalState = cluster.txnames[0].finalState if not hasattr(cluster.txnames[0],'intermediateState'): intermediateState = None else: intermediateState = cluster.txnames[0].intermediateState #Get cross section info from cluster (to generate zero cross section values): infoList = cluster.elements[0].weight.getInfo() #Get weights for elements appearing in stringExpr weightsDict = {} evalExpr = stringExpr.replace("'","").replace(" ","") for i,elStr in enumerate(elementsInStr(evalExpr)): el = element.Element(elStr,intermediateState=intermediateState, finalState=finalState,model=model) weightsDict['w%i'%i] = crossSection.XSectionList(infoList) for el1 in cluster.elements: if el1 == el: weightsDict['w%i'%i] += el1.weight evalExpr = evalExpr.replace(elStr,'w%i'%i) weightsDict.update({"Cgtr" : cGtr, "cGtr" : cGtr, "cSim" : cSim, "Csim" : cSim}) exprvalue = eval(evalExpr, weightsDict) if type(exprvalue) == type(crossSection.XSectionList()): if len(exprvalue) != 1: logger.error("Evaluation of expression "+evalExpr+" returned multiple values.") return exprvalue[0] #Return XSection object return exprvalue
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 = [] for ip, particle in enumerate(event.particles): # Particle came from initial state (primary mother) if 1 in particle.moms: mombranch = branch.Branch() mombranch.momID = particle.pdg mombranch.daughterID = particle.pdg if weight: mombranch.maxWeight = weight.getMaxXsec() # Get simple BR and Mass dictionaries for the corresponding branch branchBR = brDic[ip] branchMass = massDic[ip] mombranch.masses = [branchMass[mombranch.momID]] # 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") import sys sys.exit() # Create element from event newElement = element.Element(finalBranchList) if weight: newElement.weight = copy.deepcopy(weight) return newElement
def testTxNameWildCard(self): f = './database/13TeV/CMS/CMS-PAS-EXO-16-036-eff/c000/THSCPM2.txt' gInfo = infoObj.Info( './database/13TeV/CMS/CMS-PAS-EXO-16-036-eff/globalInfo.txt') gInfo.addInfo('dataId', 'c000') tx = TxName(f, gInfo, gInfo) el = element.Element(info="[[],[[e+]]]", finalState=['HSCP', 'MET']) el.setMasses([[1.25E+02 * GeV], [4.40E+02 * GeV, 1.00E+00 * GeV]]) newEl = tx.hasElementAs( el ) #newEl should be equal to el, but with opposite branch ordering self.assertFalse(newEl is None) self.assertTrue(newEl.getMasses() == [[4.40E+02 * GeV, 1.00E+00 * GeV], [1.25E+02 * GeV]]) res = tx.getEfficiencyFor(newEl.getMasses()) self.assertAlmostEqual(res, 0.090999)
def _evalExpression(stringExpr,cluster,analysis): """ Auxiliary method to evaluate a string expression using the weights of the elements in the cluster. Replaces the elements in stringExpr (in bracket notation) by their weights and evaluate the expression. e.g. computes the total weight of string expressions such as "[[[e^+]],[[e^-]]]+[[[mu^+]],[[mu^-]]]" or ratios of weights of string expressions such as "[[[e^+]],[[e^-]]]/[[[mu^+]],[[mu^-]]]" and so on... :parameter stringExpr: string containing the expression to be evaluated :parameter cluster: cluster of elements (ElementCluster object) :parameter analysis: analysis (ULanalysis object). Just used to print the error message :returns: value for the expression. Can be a XSectionList object, a float or not numerical (None,string,...) """ #Generate elements appearing in the string expression with zero cross-sections: elements = [] for elStr in elementsInStr(stringExpr): el = element.Element(elStr) elements.append(el) #Replace elements in strings by their weights and add weights from cluster to the elements list: expr = stringExpr[:].replace("'","").replace(" ","") for iel, el in enumerate(elements): expr = expr.replace(str(el), "elements["+ str(iel) +"].weight") for el1 in cluster.elements: if el1.particlesMatch(el): el.weight.combineWith(el1.weight) el.combineMotherElements(el1) ## keep track of all mothers if expr.find("Cgtr") >= 0 or expr.find("Csim") >= 0: expr = expr.replace("Cgtr", "cGtr") expr = expr.replace("Csim", "cSim") logger.warning(analysis.label + " using deprecated functions " "'Cgtr'/'Csim'. Auto-replacing with 'cGtr'/'cSim'.") exprvalue = eval(expr) if type(exprvalue) == type(crossSection.XSectionList()): if len(exprvalue) != 1: logger.error("Evaluation of expression "+expr+" returned multiple values.") return exprvalue else: return exprvalue
def _getElementsEffs(constraint, conditions): """ Generate a dictionary of elements with their simple efficiencies as values. Efficiencies are = 1. if the element appears in the constraint or conditions. """ # Get element strings appearing in constraint elStrings = elementsInStr(constraint) if not elStrings: return False if conditions: for cond in conditions: elStrings += elementsInStr(cond) elementsEff = {} if not elStrings: return False elStrings = set(elStrings) for elstr in elStrings: el = element.Element(elstr) elementsEff[el] = 1. return elementsEff
def _evalExpression(stringExpr,cluster): """ Auxiliary method to evaluate a string expression using the weights of the elements in the cluster. Replaces the elements in stringExpr (in bracket notation) by their weights and evaluate the expression. e.g. computes the total weight of string expressions such as "[[[e^+]],[[e^-]]]+[[[mu^+]],[[mu^-]]]" or ratios of weights of string expressions such as "[[[e^+]],[[e^-]]]/[[[mu^+]],[[mu^-]]]" and so on... :parameter stringExpr: string containing the expression to be evaluated :parameter cluster: cluster of elements (ElementCluster object) :returns: xsection for the expression. Can be a XSection object, a float or not numerical (None,string,...) """ #Get cross section info from cluster (to generate zero cross section values): infoList = cluster.elements[0].weight.getInfo() #Generate elements appearing in the string expression with zero cross sections: elements = [] for elStr in elementsInStr(stringExpr): el = element.Element(elStr) el.weight = crossSection.XSectionList(infoList) elements.append(el) #Replace elements in strings by their weights and add weights from cluster to the elements list: expr = stringExpr[:].replace("'","").replace(" ","") for iel, el in enumerate(elements): expr = expr.replace(str(el), "elements["+ str(iel) +"].weight") for el1 in cluster.elements: if el1.particlesMatch(el): el.weight.combineWith(el1.weight) el.combineMotherElements(el1) ## keep track of all mothers if expr.find("Cgtr") >= 0 or expr.find("Csim") >= 0: expr = expr.replace("Cgtr", "cGtr") expr = expr.replace("Csim", "cSim") exprvalue = eval(expr) if type(exprvalue) == type(crossSection.XSectionList()): if len(exprvalue) != 1: logger.error("Evaluation of expression "+expr+" returned multiple values.") return exprvalue[0] #Return XSection object return exprvalue
def _evalExpression(stringExpr,cluster): """ Auxiliary method to evaluate a string expression using the weights of the elements in the cluster. Replaces the elements in stringExpr (in bracket notation) by their weights and evaluate the expression. e.g. computes the total weight of string expressions such as "[[[e^+]],[[e^-]]]+[[[mu^+]],[[mu^-]]]" or ratios of weights of string expressions such as "[[[e^+]],[[e^-]]]/[[[mu^+]],[[mu^-]]]" and so on... :parameter stringExpr: string containing the expression to be evaluated :parameter cluster: cluster of elements (ElementCluster object) :returns: xsection for the expression. Can be a XSection object, a float or not numerical (None,string,...) """ #Get cross section info from cluster (to generate zero cross section values): infoList = cluster.elements[0].weight.getInfo() #Get final state info finalStates = cluster.elements[0].getFinalStates() #Get weights for elements appearing in stringExpr weightsDict = {} evalExpr = stringExpr.replace("'","").replace(" ","") for i,elStr in enumerate(elementsInStr(evalExpr)): el = element.Element(elStr) el.setFinalState(finalStates) weightsDict['w%i'%i] = crossSection.XSectionList(infoList) for el1 in cluster.elements: if el1.particlesMatch(el): weightsDict['w%i'%i].combineWith(el1.weight) el.combineMotherElements(el1) evalExpr = evalExpr.replace(elStr,'w%i'%i) weightsDict.update({"Cgtr" : cGtr, "cGtr" : cGtr, "cSim" : cSim, "Csim" : cSim}) exprvalue = eval(evalExpr, weightsDict) if type(exprvalue) == type(crossSection.XSectionList()): if len(exprvalue) != 1: logger.error("Evaluation of expression "+evalExpr+" returned multiple values.") return exprvalue[0] #Return XSection object return exprvalue
def testInclusiveElement(self): el1 = element.Element(info='[[[e+],[e-,mu+]],[[*],[e-,mu+]]]',finalState = ['MET','HSCP'], model=finalStates) el2 = element.Element(info='[[[e+],[e-,mu+]],[[jet],[e-,mu+]]]',finalState = ['anyOdd','HSCP'], model=finalStates) el3 = element.Element(info='[[[e+],[e-,mu+]],[[jet],[e-,mu+]]]',finalState = ['HSCP','HSCP'], model=finalStates) el4 = element.Element(info='[[*],[[jet],[e-,mu+]]]',finalState = ['MET','HSCP'], model=finalStates) el5 = element.Element(info='[[[e+],[e-,mu+]],[[jet],[e-,mu+]]]',finalState = ['MET','HSCP'], model=finalStates) el6 = element.Element(info='[[*],[*]]',finalState = ['MET','HSCP'], model=finalStates) el7 = element.Element(info='[[[e+],[e-,mu+]],[[jet,jet],[e-,mu+]]]',finalState = ['MET','HSCP'], model=finalStates) self.assertTrue(el1 == el2) self.assertTrue(el2 == el3) self.assertTrue(el3 != el4) self.assertTrue(el2 == el4) self.assertTrue(el1 == el4) self.assertTrue(el1 != el3) self.assertTrue(el6 == el1) self.assertTrue(el6 == el2) self.assertTrue(el5 == el1) self.assertTrue(el5 != el3) self.assertTrue(el7 != el1) self.assertTrue(el1 != el7) self.assertTrue(str(el1) == "[[[e+],[e-,mu+]],[[*],[e-,mu+]]]") self.assertTrue(str(el6) == "[[*],[*]]") el10 = element.Element(info="[[[q,q],[e-,nu]],[[e-,nu]]]", finalState = ['MET','MET'], intermediateState=[['gluino','C1+'],['C1+']], model=finalStates) el11 = element.Element(info="[['*'],['*']]",finalState = ['MET','MET'], intermediateState=[['C1+'],['C1+']], model=finalStates) self.assertFalse(el10 == el11) el12 = element.Element(info="[['*'],['*']]",finalState = ['MET','MET'], intermediateState=[['gluino','C1+'],['C1+']], model=finalStates) self.assertTrue(el10 == el12) el13 = element.Element(info="[[[q,q],[e-,nu]],[]]", finalState = ['MET','MET'], intermediateState=[['gluino','C1+'],[]], model=finalStates) el14 = element.Element(info="[['*'],['*']]",finalState = ['MET','MET'], intermediateState=[['gluino','C1+'],['C1+']], model=finalStates) self.assertFalse(el13 == el14) el15 = element.Element(info="[['*'],['*']]",finalState = ['MET','MET'], intermediateState=[['gluino','C1+'],[]], model=finalStates) self.assertTrue(el13 == el15)
def decompose(slhafile, sigcut=.1 * fb, doCompress=False, doInvisible=False, minmassgap=-1. * GeV, useXSecs=None): """ Perform SLHA-based decomposition. :param sigcut: minimum sigma*BR to be generated, by default sigcut = 0.1 fb :param doCompress: turn mass compression on/off :param doInvisible: turn invisible compression on/off :param minmassgap: maximum value (in GeV) for considering two R-odd particles degenerate (only revelant for doCompress=True ) :param useXSecs: optionally a dictionary with cross sections for pair production, by default reading the cross sections from the SLHA file. :returns: list of topologies (TopologyList object) """ t1 = time.time() if doCompress and minmassgap / GeV < 0.: logger.error( "Asked for compression without specifying minmassgap. Please set minmassgap." ) raise SModelSError() if type(sigcut) == type(1.): sigcut = sigcut * fb try: f = pyslha.readSLHAFile(slhafile) except pyslha.ParseError as e: logger.error("The file %s cannot be parsed as an SLHA file: %s" % (slhafile, e)) raise SModelSError() # Get cross section from file xSectionList = crossSection.getXsecFromSLHAFile(slhafile, useXSecs) # Get BRs and masses from file brDic, massDic = _getDictionariesFromSLHA(slhafile) # Only use the highest order cross sections for each process xSectionList.removeLowerOrder() # Order xsections by PDGs to improve performance xSectionList.order() # Get maximum cross sections (weights) for single particles (irrespective # of sqrtS) maxWeight = {} for pid in xSectionList.getPIDs(): maxWeight[pid] = xSectionList.getXsecsFor(pid).getMaxXsec() # Generate dictionary, where keys are the PIDs and values # are the list of cross sections for the PID pair (for performance) xSectionListDict = {} for pids in xSectionList.getPIDpairs(): xSectionListDict[pids] = xSectionList.getXsecsFor(pids) # Create 1-particle branches with all possible mothers branchList = [] for pid in maxWeight: branchList.append(Branch()) branchList[-1].PIDs = [[pid]] if not pid in massDic: logger.error( "pid %d does not appear in masses dictionary %s in slhafile %s" % (pid, massDic, slhafile)) branchList[-1].masses = [massDic[pid]] branchList[-1].maxWeight = maxWeight[pid] # Generate final branches (after all R-odd particles have decayed) finalBranchList = decayBranches(branchList, brDic, massDic, sigcut) # Generate dictionary, where keys are the PIDs and values are the list of branches for the PID (for performance) branchListDict = {} for branch in finalBranchList: if len(branch.PIDs) != 1: logger.error("During decomposition the branches should \ not have multiple PID lists!") return False if branch.PIDs[0][0] in branchListDict: branchListDict[branch.PIDs[0][0]].append(branch) else: branchListDict[branch.PIDs[0][0]] = [branch] for pid in xSectionList.getPIDs(): if not pid in branchListDict: branchListDict[pid] = [] #Sort the branch lists by max weight to improve performance: for pid in branchListDict: branchListDict[pid] = sorted(branchListDict[pid], key=lambda br: br.maxWeight, reverse=True) smsTopList = topology.TopologyList() # Combine pairs of branches into elements according to production # cross section list for pids in xSectionList.getPIDpairs(): weightList = xSectionListDict[pids] minBR = (sigcut / weightList.getMaxXsec()).asNumber() if minBR > 1.: continue for branch1 in branchListDict[pids[0]]: BR1 = branch1.maxWeight / maxWeight[ pids[0]] #Branching ratio for first branch if BR1 < minBR: break #Stop loop if BR1 is already too low for branch2 in branchListDict[pids[1]]: BR2 = branch2.maxWeight / maxWeight[ pids[1]] #Branching ratio for second branch if BR2 < minBR: break #Stop loop if BR2 is already too low finalBR = BR1 * BR2 if type(finalBR) == type(1. * fb): finalBR = finalBR.asNumber() if finalBR < minBR: continue # Skip elements with xsec below sigcut if len(branch1.PIDs) != 1 or len(branch2.PIDs) != 1: logger.error("During decomposition the branches should \ not have multiple PID lists!") return False newElement = element.Element([branch1, branch2]) newElement.weight = weightList * finalBR allElements = [newElement] # Perform compression if doCompress or doInvisible: allElements += newElement.compressElement( doCompress, doInvisible, minmassgap) for el in allElements: el.sortBranches( ) #Make sure elements are sorted BEFORE adding them smsTopList.addElement(el) smsTopList._setElementIds() logger.debug("slhaDecomposer done in %.2f s." % (time.time() - t1)) return smsTopList
# Combine pairs of branches into elements according to production # cross-section list for pids in xSectionList.getPIDpairs(): weightList = xSectionListDict[pids] minBR = (sigcut / weightList.getMaxXsec()).asNumber() if minBR > 1.: continue for branch1 in branchListDict[pids[0]]: for branch2 in branchListDict[pids[1]]: finalBR = branch1.maxWeight * branch2.maxWeight / \ (maxWeight[pids[0]] * maxWeight[pids[1]]) if type(finalBR) == type(1. * fb): finalBR = finalBR.asNumber() if finalBR < minBR: continue # Skip elements with xsec below sigcut newElement = element.Element([branch1, branch2]) newElement.weight = weightList * finalBR allElements = [newElement] # Perform compression if doCompress or doInvisible: allElements += newElement.compressElement( doCompress, doInvisible, minmassgap) for el in allElements: top = topology.Topology(el) smsTopList.addList([top]) logger.debug("slhaDecomposer done in " + str(time.time() - t1) + " s.") return smsTopList def _getDictionariesFromSLHA(slhafile):
def decompose(model, sigmacut=0.1 * fb, doCompress=True, doInvisible=True, minmassgap=0 * GeV): """ Perform decomposition using the information stored in model. :param sigmacut: minimum sigma*BR to be generated, by default sigmacut = 0.1 fb :param doCompress: turn mass compression on/off :param doInvisible: turn invisible compression on/off :param minmassgap: maximum value (in GeV) for considering two R-odd particles degenerate (only revelant for doCompress=True ) :returns: list of topologies (TopologyList object) """ t1 = time.time() xSectionList = model.xsections pdgList = model.getValuesFor('pdg') if doCompress and minmassgap / GeV < 0.: logger.error( "Asked for compression without specifying minmassgap. Please set minmassgap." ) raise SModelSError() if isinstance(sigmacut, (float, int)): sigmacut = sigmacut * fb sigmacut = sigmacut.asNumber(fb) xSectionList.removeLowerOrder() # Order xsections by PDGs to improve performance xSectionList.order() # Get maximum cross sections (weights) for single particles (irrespective # of sqrtS) maxWeight = {} for pid in xSectionList.getPIDs(): maxWeight[pid] = xSectionList.getXsecsFor(pid).getMaxXsec().asNumber( fb) # Generate dictionary, where keys are the PIDs and values # are the list of cross sections for the PID pair (for performance) xSectionListDict = {} for pids in xSectionList.getPIDpairs(): xSectionListDict[pids] = xSectionList.getXsecsFor(pids) # Create 1-particle branches with all possible mothers branchList = [] for pid in maxWeight: branchList.append(Branch()) bsmParticle = model.getParticlesWith(pdg=pid) if not bsmParticle: raise SModelSError("Particle for pdg %i has not been defined.") if len(bsmParticle) != 1: raise SModelSError( "Particle with pdg %i has multiple definitions.") branchList[-1].oddParticles = [bsmParticle[0]] if not pid in pdgList: logger.error("PDG %i has not been defined" % int(pid)) branchList[-1].maxWeight = maxWeight[pid] # Generate final branches (after all R-odd particles have decayed) finalBranchList = decayBranches(branchList, sigmacut) # Generate dictionary, where keys are the PIDs and values are the list of branches for the PID (for performance) branchListDict = {} for branch in finalBranchList: if branch.oddParticles[0].pdg in branchListDict: branchListDict[branch.oddParticles[0].pdg].append(branch) else: branchListDict[branch.oddParticles[0].pdg] = [branch] for pid in xSectionList.getPIDs(): if not pid in branchListDict: branchListDict[pid] = [] #Sort the branch lists by max weight to improve performance: for pid in branchListDict: branchListDict[pid] = sorted(branchListDict[pid], key=lambda br: br.maxWeight, reverse=True) smsTopList = topology.TopologyList() # Combine pairs of branches into elements according to production # cross section list for pids in xSectionList.getPIDpairs(): weightList = xSectionListDict[pids] maxxsec = weightList.getMaxXsec().asNumber(fb) if maxxsec == 0.: ## protection continue minBR = sigmacut / maxxsec if minBR > 1.: continue for branch1 in branchListDict[pids[0]]: BR1 = branch1.maxWeight / maxWeight[ pids[0]] #Branching ratio for first branch if BR1 < minBR: break #Stop loop if BR1 is already too low for branch2 in branchListDict[pids[1]]: BR2 = branch2.maxWeight / maxWeight[ pids[1]] #Branching ratio for second branch if BR2 < minBR: break #Stop loop if BR2 is already too low finalBR = BR1 * BR2 if finalBR < minBR: continue # Skip elements with xsec below sigmacut newElement = element.Element([branch1, branch2]) newElement.weight = weightList * finalBR newElement.sortBranches( ) #Make sure elements are sorted BEFORE adding them smsTopList.addElement(newElement) smsTopList.compressElements(doCompress, doInvisible, minmassgap) smsTopList._setElementIds() logger.debug("decomposer done in %.2f s." % (time.time() - t1)) return smsTopList