def _getPromptDecays(slhafile, brDic, l_inner=1. * mm, gb_inner=1.3, l_outer=10. * m, gb_outer=1.43): """ Using the widths in the slhafile, reweights the BR dictionary with the fraction of prompt decays and add the fraction of "long-lived decays". The fraction of prompt decays and "long-lived decays" are defined as: F_prompt = 1 - exp(-width*l_inner/gb_inner) F_long = exp(-width*l_outer/gb_outer) where l_inner is the inner radius of the detector, l_outer is the outer radius and gb_x is the estimate for the kinematical factor gamma*beta for each case. We use gb_outer = 10 and gb_inner= 0.5 :param widthDic: Dictionary with the widths of the particles :param l_inner: Radius of the inner tracker :param gb_inner: Effective gamma*beta factor to be used for prompt decays :param l_outer: Radius of the outer detector :param gb_outer: Effective gamma*beta factor to be used for long-lived decays :return: Dictionary = {pid : decay} """ hc = 197.327 * MeV * fm #hbar * c #Get the widths: res = pyslha.readSLHAFile(slhafile) decays = res.decays #PID for displaced decays: dispPid = 0 for pid in brDic: if pid == dispPid: continue width = abs(decays[abs(pid)].totalwidth) * GeV Fprompt = 1. - math.exp(-width * l_inner / (gb_inner * hc)) Flong = math.exp(-width * l_outer / (gb_outer * hc)) for decay in brDic[pid]: decay.br *= Fprompt #Reweight by prompt fraction #Add long-lived fraction: if Flong > 1e-50: stableFraction = pyslha.Decay(br=Flong, ids=[], nda=0) brDic[pid].append(stableFraction) if (Flong + Fprompt) > 1.: logger.error("Sum of decay fractions > 1 for " + str(pid)) return False Fdisp = 1 - Flong - Fprompt #Add displaced decay: if Fdisp > 0.001: displacedFraction = pyslha.Decay(br=Fdisp, ids=[dispPid], nda=1) brDic[pid].append(displacedFraction) return brDic
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 getDictionariesFromEvent(event): """ Create mass and BR dictionaries for each branch in an event. :param event: LHE event :returns: BR and mass dictionaries for the branches in the event """ particles = event.particles # Identify and label to which branch each particle belongs #(the branch label is the position of the primary mother) masses = {} decays = {} for ip, particle in enumerate(particles): if particle.status == -1: #Ignore incoming particles continue if not particle.pdg in masses: masses[particle.pdg] = [particle.mass] decays[particle.pdg] = [] #Get event decay: decay = pyslha.Decay(0, 0, [], particle.pdg) #Collect all daughters: for ipn,newparticle in enumerate(particles): if ipn == ip: continue if not ip+1 in newparticle.moms: #newparticle is not a daughter of particle continue # Check if particle has a single parent # (as it should) newparticle.moms = [mom for mom in newparticle.moms if mom != 0] if len(newparticle.moms) != 1: raise SModelSError("More than one parent particle found") decay.nda += 1 decay.br = 1. decay.ids.append(newparticle.pdg) if decay.ids: decay.ids = sorted(decay.ids) decays[particle.pdg].append(decay) return masses,decays
def testEventDictionaries(self): filename = "./testFiles/lhe/simplyGluino.lhe" reader = lheReader.LheReader(filename) events = [event for event in reader] reader.close() massDict, decayDict = lheReader.getDictionariesFromEvent(events[0]) self.assertEqual(massDict, { -1: [0.33], 1000021: [675.0], 1000022: [200.0], 1: [0.33] }) gluinoDec = [ pyslha.Decay(br=1., nda=3, ids=[-1, 1, 1000022], parentid=1000021) ] * 2 self.assertTrue(compareDecays(gluinoDec, decayDict[1000021])) self.assertEqual(decayDict[1000022], [])
def testDictionaries(self): filename = "./testFiles/lhe/simplyGluino.lhe" massDict, decayDict = lheReader.getDictionariesFrom(filename) self.assertEqual(massDict, { 1000021: 675.0, 1000022: 200.0, 1: 0.33, 2: 0.33 }) gluinoDecs = [ pyslha.Decay(br=0.3, nda=3, ids=[-1, 1, 1000022], parentid=1000021), pyslha.Decay(br=0.7, nda=3, ids=[-2, 2, 1000022], parentid=1000021) ] self.assertEqual(len(decayDict[1000021].decays), len(gluinoDecs)) self.assertTrue(compareDecays(gluinoDecs, decayDict[1000021].decays)) re = pyslha.readSLHAFile("./testFiles/slha/gluino_squarks.slha") filename = "./testFiles/lhe/gluino_squarks.lhe" massDict, decayDict = lheReader.getDictionariesFrom(filename) for pdg in massDict: if pdg < 100000: continue self.assertAlmostEqual(re.blocks['MASS'][pdg], massDict[pdg]) #Expected answer: decayRes = {} decayRes[1000024] = [ pyslha.Decay(br=1.0000, ids=[1000022, 24], parentid=1000024, nda=2) ] decayRes[1000023] = [ pyslha.Decay(br=0.8571, ids=[1000022, 25], parentid=1000023, nda=2), pyslha.Decay(br=0.1429, ids=[1000022, 23], parentid=1000023, nda=2) ] decayRes[1000001] = [ pyslha.Decay(br=1.0000, ids=[1000021, 1], parentid=1000001, nda=2) ] decayRes[1000002] = [ pyslha.Decay(br=0.5000, ids=[1000024, 1], parentid=1000002, nda=2), pyslha.Decay(br=0.2500, ids=[1000021, 2], parentid=1000002, nda=2), pyslha.Decay(br=0.2500, ids=[1000023, 2], parentid=1000002, nda=2) ] decayRes[2000002] = [ pyslha.Decay(br=1.0000, ids=[1000021, 2], parentid=2000002, nda=2) ] decayRes[1000021] = [ pyslha.Decay(br=0.2500, ids=[-1000024, 4, -3], parentid=1000021, nda=3), pyslha.Decay(br=0.3750, ids=[1000024, -2, 1], parentid=1000021, nda=3), pyslha.Decay(br=0.1250, ids=[1000024, -4, 3], parentid=1000021, nda=3), pyslha.Decay(br=0.1250, ids=[1000023, -2, 2], parentid=1000021, nda=3), pyslha.Decay(br=0.1250, ids=[1000023, -6, 6], parentid=1000021, nda=3) ] for pdg in decayRes: self.assertTrue(compareDecays(decayDict[pdg].decays, decayRes[pdg])) self.assertEqual(decayDict[pdg].totalwidth, float('inf')) for pdg in decayDict: if not decayDict[pdg].decays: self.assertEqual(decayDict[pdg].totalwidth, 0.)
def getDictionariesFrom(lheFile,nevts=None): """ Reads all events in the LHE file and create mass and BR dictionaries for each branch in an event. :param lheFile: LHE file :param nevts: (maximum) number of events used in the decomposition. If None, all events from file are processed. :returns: BR and mass dictionaries for the particles appearing in the event """ reader = LheReader(lheFile, nevts) # Loop over events and decompose massDict = {} decaysDict = {} for event in reader: eventMass,eventDecays = getDictionariesFromEvent(event) for pdg,mass in eventMass.items(): if not pdg in massDict: massDict[pdg] = mass else: massDict[pdg] += mass for pdg,decays in eventDecays.items(): if not pdg in decaysDict: decaysDict[pdg] = decays else: decaysDict[pdg] += decays #Use averaged mass over all events: for pdg,masses in massDict.items(): massDict[pdg] = sum(masses)/len(masses) if not pdg in decaysDict: decaysDict[pdg] = [] #Make sure all particles appear in decaysDict #Compute the decay dictionaries: for pdg,eventDecays in list(decaysDict.items()): daughterIDs = [] for eventDec in eventDecays: pids = sorted(eventDec.ids) if not pids in daughterIDs: daughterIDs.append(pids) n = len(eventDecays) #Number of times the particle appears in all events combinedDecays = [] for daughterID in daughterIDs: decay = pyslha.Decay(br=0.,nda=len(daughterID),ids=daughterID,parentid=pdg) for eventDecay in eventDecays: if sorted(eventDecay.ids) != daughterID: continue decay.br += 1./float(n) #Count this decay combinedDecays.append(decay) decaysDict[pdg] = combinedDecays #Stable particles will appear with an empty list #Remove anti-particle decays and masses: for pdg in list(massDict.keys())[:]: if -abs(pdg) in massDict and abs(pdg) in massDict: massDict.pop(-abs(pdg)) for pdg in list(decaysDict.keys())[:]: if -abs(pdg) in decaysDict and abs(pdg) in decaysDict: decaysDict.pop(-abs(pdg)) #Add widths to decaysDict using the pyslha.Particle class: for pdg,decays in decaysDict.items(): decaysDict[pdg] = pyslha.Particle(pid=pdg) if abs(pdg) in massDict: decaysDict[pdg].mass = massDict[abs(pdg)] if decays: decaysDict[pdg].totalwidth = float('inf') else: decaysDict[pdg].totalwidth = 0. decaysDict[pdg].decays = decays reader.close() return massDict,decaysDict
def setDecays(self,decaysDict,promptWidth,stableWidth,erasePrompt): allPDGs = list(set(self.getValuesFor('pdg'))) evenPDGs,oddPDGs = self.getEvenOddList() for particle in self.BSMparticles: if isinstance(particle,MultiParticle): continue if not hasattr(particle,'pdg') or not hasattr(particle,'Z2parity'): raise SModelSError("PDG and/or Z2-parity for particle %s has not been defined" %particle.label) pdg = particle.pdg particle.decays = [] if pdg in decaysDict: particleData = decaysDict[pdg] chargeConj = 1 elif -pdg in decaysDict: particleData = decaysDict[-pdg] chargeConj = -1 else: logger.error("Decay information for particle %i could not be found" %pdg) raise SModelSError() particle.totalwidth = abs(particleData.totalwidth)*GeV if particle.totalwidth < stableWidth: particle.totalwidth = 0.*GeV #Treat particle as stable logger.debug("Particle %s has width below the threshold and will be assumed as stable" %particle.pdg) continue if particle.totalwidth > promptWidth: particle.totalwidth = float('inf')*GeV #Treat particle as prompt logger.debug("Particle %s has width above the threshold and will be assumed as prompt." %particle.pdg) if erasePrompt and particle.Z2parity == -1: logger.debug("Erasing quantum numbers of (prompt) particle %s." %particle.pdg) for attr in erasePrompt: delattr(particle,attr) else: particle.decays.append(None) #Include possibility for particle being long-lived (non-prompt) for decay in particleData.decays: pids = decay.ids missingIDs = set(pids).difference(set(allPDGs)) if missingIDs: logger.info("Particle(s) %s is not defined within model. Decay %s will be ignored" %(missingIDs,decay)) continue oddPids = [pid for pid in decay.ids if abs(pid) in oddPDGs] evenPids = [pid for pid in decay.ids if abs(pid) in evenPDGs] if len(oddPids) != 1 or len(evenPids+oddPids) != len(decay.ids): logger.debug("Decay %i -> %s is not of the form Z2-odd -> Z2-odd + [Z2-even particles] and will be ignored" %(pdg,pids)) continue #Conjugated decays if needed #(if pid*chargeConj is not in model, assume the particle is its own anti-particle) decayIDs = [pid*chargeConj if pid*chargeConj in allPDGs else pid for pid in decay.ids] newDecay = pyslha.Decay(br=decay.br,nda=decay.nda,parentid=decay.parentid,ids=decayIDs) #Convert PDGs to particle objects: daughters = [] for pdg in newDecay.ids: daughter = self.getParticlesWith(pdg=pdg) if not daughter: raise SModelSError("Particle with PDG = %i was not found in model. Check the model definitions." %pdg) elif len(daughter) > 1: raise SModelSError("Multiple particles defined with PDG = %i. PDG ids must be unique." %pdg) else: daughter = daughter[0] daughters.append(daughter) oddParticles = [p for p in daughters if p.Z2parity == -1] evenParticles = ParticleList([p for p in daughters if p.Z2parity == 1]) newDecay.oddParticles = oddParticles newDecay.evenParticles = evenParticles particle.decays.append(newDecay) #Check if all unstable particles have decay channels defined: for p in self.BSMparticles: if p.totalwidth < stableWidth: continue ndecays = len([dec for dec in p.decays if dec is not None]) if ndecays == 0: if p.Z2parity == -1: logger.warning("No valid decay found for %s. It will be considered stable." %p) p.totalwidth = 0.*GeV
def _getDictionariesFromEvent(event): """ Create mass and BR dictionaries for each branch in an event. :param event: LHE event :returns: BR and mass dictionaries for the branches in the event """ particles = event.particles # Identify and label to which branch each particle belongs #(the branch label is the position of the primary mother) branches = {} for ip, particle in enumerate(particles): if particle.status == -1: continue if particles[particle.moms[0]].status == -1: # If a primary mother, the branch index is its own position initMom = ip else: # If not a primary mother, check if particle has a single parent # (as it should) if particle.moms[0] != particle.moms[1] and \ min(particle.moms) != 0: logger.error("More than one parent particle found") raise SModelSError() initMom = max(particle.moms) - 1 while particles[particles[initMom].moms[0]].status != -1: # Find primary mother (labels the branch) initMom = max(particles[initMom].moms) - 1 branches[ip] = initMom # Get mass and BR dictionaries for all branches: massDic = {} brDic = {} for ibranch in branches.values(): #ibranch = position of primary mother massDic[ibranch] = {} brDic[ibranch] = {} from smodels.particlesLoader import rEven for ip, particle in enumerate(particles): if particle.pdg in rEven or particle.status == -1: # Ignore R-even particles and initial state particles continue ibranch = branches[ip] # Get particle branch massDic[ibranch][particle.pdg] = round(particle.mass, 1) * GeV # Create empty BRs brDic[ibranch][particle.pdg] = [pyslha.Decay(0., 0, [], particle.pdg)] # Get BRs from event for ip, particle in enumerate(particles): if particle.status == -1: # Ignore initial state particles continue if particles[particle.moms[0]].status == -1: # Ignore initial mothers continue ibranch = branches[ip] momPdg = particles[max(particle.moms) - 1].pdg if momPdg in rEven: # Ignore R-even decays continue # BR = 1 always for an event brDic[ibranch][momPdg][0].br = 1. brDic[ibranch][momPdg][0].nda += 1 brDic[ibranch][momPdg][0].ids.append(particle.pdg) return brDic, massDic