SND@LHC Software
Loading...
Searching...
No Matches
rpvsusy.py
Go to the documentation of this file.
1"""
2# ==================================================================
3# Python module
4#
5# This module provides methods to compute the lifetime and
6# branching ratio of SUSY RPV neutralino given its mass and couplings as
7# input parameters.
8#
9# Created: 30/04/2016 Konstantinos A. Petridis (konstantinos.petridis@cern.ch)
10#
11# Sample usage:
12# ipython -i rpvsusy.py
13# In [1]: b = RPVSYSY(1.,[1, 1],1e3,1,True)
14# HNLbranchings instance initialized with couplings:
15# \lambda_{production} = 1GeV
16# \lambda_{decay} = 1GeV
17# universal sfermion mass = 1e3GeV
18#
19# benchmark scenario:
20# 1 (values between 1 and 5)
21# and mass:
22# m = 1.0 GeV
23# In [2]: b.computeNLifetime()
24# Out[2]: 0.0219634078804
25# In [3]: b.findBranchingRatio('N -> K+ mu-')
26# Out[3]: 0.11826749348890987
27#
28# ==================================================================
29"""
30
31from __future__ import division
32from __future__ import print_function
33import re
34import math
35import ROOT
36import shipunit as u
37
38# Load PDG database
39pdg = ROOT.TDatabasePDG.Instance()
40
41def PDGname(particle):
42 """
43 Trim particle name for use with the PDG database
44 """
45 if 'down' in particle: return 'd'
46 if 'up' in particle: return 'u'
47 if 'strange' in particle: return 's'
48 if 'charm' in particle: return 'c'
49 if 'bottom' in particle: return 'b'
50 if 'beauty' in particle: return 'b'
51 if 'top' in particle: return 't'
52 if '1' in particle:
53 particle = particle.replace('1',"'")
54 if (not (('-' in particle) or ('+' in particle) or ('0' in particle)
55 or ('nu_' in particle) or ('eta' in particle) or ('phi' in particle) )
56 and (particle not in ['d','u','s','c','b','t'])):
57 particle += '+'
58 return particle
59
60def PDGcode(particle):
61 """
62 Read particle ID from PDG database
63 """
64 particle = PDGname(particle)
65 tPart = pdg.GetParticle(particle)
66 return int(tPart.PdgCode())
67
68def mass(particle):
69 """
70 Read particle mass from PDG database
71 """
72 particle = PDGname(particle)
73 tPart = pdg.GetParticle(particle)
74 return tPart.Mass()
75
76def width(particle):
77 """
78 Read particle width from PDG database
79 """
80 particle = PDGname(particle)
81 tPart = pdg.GetParticle(particle)
82 return tPart.Width()
83
84def lifetime(particle):
85 """
86 Read particle lifetime from PDG database
87 """
88 particle = PDGname(particle)
89 tPart = pdg.GetParticle(particle)
90 if particle=="D0" or particle=="D0bar" :
91 return 4.1e-13
92 elif particle=="D+" or particle=="D-":
93 return 1.0e-12
94 elif particle=="D_s+" or particle=="D_s-":
95 return 5.0e-13
96 elif particle=="B0" or particle=="B0bar":
97 return 1.5e-12
98 elif particle=="B+" or particle=="B-":
99 return 1.6e-12
100
101 return tPart.Lifetime()
102
103
104class constants():
105 """
106 Store some constants useful for HNL physics
107 """
108 def __init__(self):
109 self.decayConstant = {'K+':0.156*u.GeV,
110 'K-':0.156*u.GeV,
111 'K_L0':0.156/math.sqrt(2)*u.GeV,
112 'K_S0':0.156/math.sqrt(2)*u.GeV,
113 'K*0':0.230*u.GeV,
114 'K*0_bar':0.230*u.GeV,
115 'K*+':0.230*u.GeV,
116 'K*-':0.230*u.GeV,
117 'phi':0.230*u.GeV,
118 'eta':-0.142*u.GeV,
119 "eta\'":0.038*u.GeV,
120 'D+':0.205*u.GeV,'D*+':0.205*u.GeV,
121 'D-':0.205*u.GeV,'D*-':0.205*u.GeV,
122 'D_s+':0.259*u.GeV,'D*_s+':0.259*u.GeV,
123 'D_s-':0.259*u.GeV,'D*_s-':0.259*u.GeV,
124 'B+':0.191*u.GeV,'B-':0.191*u.GeV,
125 'B0':0.191*u.GeV,'B_s0':0.228*u.GeV,
126 'B0_bar':0.191*u.GeV,'B_s0_bar':0.228*u.GeV}
127
128 self.GF = 1.166379e-05/(u.GeV*u.GeV) # Fermi's constant (GeV^-2)
129 self.MW = 80.385*u.GeV
130 self.gW2 = self.GF/math.sqrt(2)*8*self.MW*self.MW # SU(2)L gauge coupling squared
131 self.s2thetaw = 0.23126 # square sine of the Weinberg angle
132 self.t2thetaw = self.s2thetaw/(1-self.s2thetaw) # square tan of the Weinberg angle
133 self.heV = 6.58211928*pow(10.,-16) # no units or it messes up!!
134 self.hGeV = self.heV * pow(10.,-9) # no units or it messes up!!
135 # defined in Eq (30)--(32) of [1511.07436], but without
136 # the coupling over sfermion mass, that will come later
137 self.GST2 = {'slepton' : self.gW2*self.t2thetaw*9./8.,
138 'sneutrino' : self.gW2*self.t2thetaw*9./8.,
139 'tlepton' : self.gW2*self.t2thetaw*1./32.,
140 'tneutrino' : self.gW2*self.t2thetaw*1./32.}
141
142
143
144# Load some useful constants
146
148 """
149 Lifetime and total and partial decay widths of an RPV neutralino
150 """
151 def __init__(self, mass, couplings, sfmass, benchmark,debug=False):
152 """
153 Initialize with mass and couplings of the RPV neutralino
154
155 Inputs:
156 mass (GeV)
157 which benchmark scenario (1,2,3,4,5)
158 couplings (list of [\lambda production, \lambda decay])
159 sfermion mass to normalise the couplings by (GeV)
160 """
161 self.MN = mass*u.GeV
162 self.U = couplings
163 self.U2 = [ui*ui for ui in self.U]
164 self.sfmass = sfmass
165 self.bench = benchmark
166 self.decays = {1:['N -> K+ mu-','N -> K*+ mu-','N -> K*0 nu_mu','N -> K_L0 nu_mu','N -> K_S0 nu_mu'],
167 2:['N -> K+ mu-','N -> K*+ mu-','N -> K*0 nu_mu','N -> K_L0 nu_mu','N -> K_S0 nu_mu',
168 'N -> eta nu_mu','N -> eta\' nu_mu','N -> phi nu_mu'],
169 3:['N -> K+ mu-','N -> K*+ mu-','N -> K*0 nu_mu','N -> K_L0 nu_mu','N -> K_S0 nu_mu'],
170 4:['N -> D+ mu-','N -> D*+ mu-','N -> K*0 nu_mu','N -> K_L0 nu_mu','N -> K_S0 nu_mu'],
171 5:['N -> K+ tau-','N -> K*+ tau-','N -> K*0 nu_tau','N -> K_L0 nu_tau','N -> K_S0 nu_tau']}
172
173 self.prods = {1:['D+ -> N mu+'],
174 2:['D_s+ -> N mu+'],
175 3:['B0 -> N nu_mu'],
176 4:['B0 -> N nu_mu'],
177 5:['B0 -> N nu_tau','B+ -> N tau+']}
178
179
180 if debug:
181 print("RPVSUSYbranchings instance initialized with couplings:")
182 print("\t benchmark scenario = %d"%self.bench)
183 print("\t decay coupling = %s"%self.U[0])
184 print("\t production coupling = %s"%self.U[1])
185 print("\t sfermion mass = %s"%self.sfmass)
186 print("\t total prod coupling = %s"%(self.U[0]/self.sfmass**2))
187 print("\t total decay coupling = %s"%(self.U[1]/self.sfmass**2))
188 print("and mass:")
189 print("\tm = %s GeV"%(self.MN))
190
191 def Get_Prod_Modes(self):
192 return self.prods[self.bench]
193
194 def Get_Dec_Modes(self):
195 return self.decays[self.bench]
196
197 def AddChannelsToPythia(self,P8Gen,verbose=True):
198 decays_tmp = self.decays[self.bench]
199 for decay in decays_tmp:
200 decay_cpy = decay
201 decay_cpy = decay_cpy.replace("N -> ","")
202 particles = decay_cpy.split()
203 codes = [PDGcode(p) for p in particles]
204 print(decay)
205 bf = self.findDecayBranchingRatio(decay)
206 if any("K+" in s for s in particles) or\
207 any("K-" in s for s in particles) or\
208 any("K*+" in s for s in particles) or\
209 any("K*-" in s for s in particles) or\
210 any("K*0" in s for s in particles) or\
211 any("K*0_bar" in s for s in particles):
212 bf = bf/2.
213 P8Gen.SetParameters("9900015:addChannel = 1 {:.12} 0 {}"\
214 .format(bf, ' '.join(str(code) for code in codes)))
215 P8Gen.SetParameters("9900015:addChannel = 1 {:.12} 0 {}"\
216 .format(bf, ' '.join(str(-code) for code in codes)))
217 else:
218 P8Gen.SetParameters("9900015:addChannel = 1 {:.12} 0 {}"\
219 .format(bf, ' '.join(str(code) for code in codes)))
220 if verbose:
221 print("debug readdecay table",particles,codes,bf)
222
223
224
225 def Width_H_L(self, H, L):
226 """
227 Returns the RPV neutralino decay width into neutral meson and lepton
228
229 Inputs:
230 - H is a string (name of the meson)
231 - L is a string (name of the lepton)
232 """
233 if self.MN < (mass(H)+mass(L)):
234 return 0.
235
236 phsp=math.sqrt(self.MN**4+mass(H)**4+mass(L)**4-\
237 2*self.MN**2*mass(H)**2-2*self.MN**2*mass(L)**2-\
238 2*mass(H)**2*mass(L)**2)
239 tmp_width=0
240 if 'nu_mu' in L or 'nu_e' in L or 'nu_tau' in L:
241 tmp_width = phsp/(self.sfmass**4*128*u.pi*self.MN**3)*\
242 c.GST2['sneutrino']*c.decayConstant[H]**2*\
243 (self.MN**2+mass(L)**2-mass(H)**2)
244 else:
245 tmp_width = phsp/(self.sfmass**4*128*u.pi*self.MN**3)*\
246 c.GST2['slepton']*c.decayConstant[H]**2*\
247 (self.MN**2+mass(L)**2-mass(H)**2)
248
249 if 'K*' in H or 'D*' in H or "phi" in H:
250 if 'nu_mu' in L or 'nu_e' in L or 'nu_tau' in L:
251 tmp_width = phsp/(self.sfmass**4*2*u.pi*self.MN**3)*\
252 c.GST2['tneutrino']*c.decayConstant[H]**2*\
253 (2*(self.MN**2-mass(L)**2)**2-mass(H)**2*(mass(H)**2+self.MN**2+mass(L)**2))
254 else:
255 tmp_width = phsp/(self.sfmass**4*2*u.pi*self.MN**3)*\
256 c.GST2['tlepton']*c.decayConstant[H]**2*\
257 (2*(self.MN**2-mass(L)**2)**2-mass(H)**2*(mass(H)**2+self.MN**2+mass(L)**2))
258 width=self.U2[1]*tmp_width
259 # contributions both from decay and production couplings
260 if self.bench==1 and ('K_' in H or 'K*' in H):
261 width=(self.U2[0]+self.U2[1])*tmp_width
262 # contributions only from production coupling
263 if self.bench==2 and ('eta' in H or 'phi' in H):
264 width=self.U2[0]*tmp_width
265
266 # Majorana case (charge conjugate channels)
267 width = 2.*width
268 return width
269
270
271 def Width_N_L(self, H, L):
272 """
273 Returns the Meson decay width to RPV neutralino and lepton
274 Inputs:
275 - H is a string (name of the meson)
276 - L is a string (name of the lepton)
277 """
278
279 if mass(H) < (self.MN+mass(L)):
280 return 0.
281 phsp=math.sqrt(self.MN**4+mass(H)**4+mass(L)**4-\
282 2*self.MN**2*mass(H)**2-2*self.MN**2*mass(L)**2-\
283 2*mass(H)**2*mass(L)**2)
284
285 tmp_width=0
286 if 'nu_mu' in L or 'nu_e' in L or 'nu_tau' in L:
287 tmp_width = phsp/(self.sfmass**4*64*u.pi*mass(H)**3)*\
288 c.GST2['sneutrino']*c.decayConstant[H]**2*\
289 (mass(H)**2-self.MN**2-mass(L)**2)
290 else:
291 tmp_width = phsp/(self.sfmass**4*64*u.pi*mass(H)**3)*\
292 c.GST2['slepton']*c.decayConstant[H]**2*\
293 (mass(H)**2-self.MN**2-mass(L)**2)
294
295 if 'K*' in H or 'D*' in H or "phi" in H:
296 if 'nu_mu' in L or 'nu_e' in L or 'nu_tau' in L:
297 tmp_width = phsp/(self.sfmass**4*3*u.pi*mass(H)**3)*\
298 c.GST2['tneutrino']*c.decayConstant[H]**2*\
299 (mass(H)**2*(mass(H)**2+self.MN**2+mass(L)**2-2*(self.MN**2-mass(L)**2)**2))
300 else:
301 tmp_width = phsp/(self.sfmass**4*3*u.pi*mass(H)**3)*\
302 c.GST2['tlepton']*c.decayConstant[H]**2*\
303 (mass(H)**2*(mass(H)**2+self.MN**2+mass(L)**2-2*(self.MN**2-mass(L)**2)**2))
304
305 width=self.U2[0]*tmp_width
306
307 return width
308
309
310
311 def NdecayWidth(self):
312 """
313 Returns the total SUSYRPV neutralino decay width
314 """
315 declist = self.decays[self.bench]
316 hadlist = [re.search('->\ (.+?)\ ',dec).group(1) for dec in declist]
317 leplist = [dlist[1].strip() for dlist in [re.findall(r"\ \w+",dec) for dec in declist]]
318 print(leplist,hadlist)
319 totalwidth = sum([self.Width_H_L(hadlist[i],leplist[i]) for i in range(0,len(hadlist))])
320 return totalwidth
321
322 def NprodWidth(self):
323 """
324 Returns the total SUSYRPV neutralino production width
325 """
326 declist = self.prods[self.bench]
327 hadlist = [re.search('(.+?)\ ->',dec).group(1) for dec in declist]
328 leplist = [dlist[1].strip() for dlist in [re.findall(r"\ \w+",dec) for dec in declist]]
329 totalwidth = sum([self.Width_N_L(hadlist[i],leplist[i]) for i in range(0,len(hadlist))])
330 return totalwidth
331
332
333 def findDecayBranchingRatio(self, decayString):
334 """
335 Returns the branching ratio of the selected SUSY RPV neutralino decay channel
336
337 Inputs:
338 - decayString is a string describing the decay, in the form 'N -> stuff1 ... stuffN'
339 """
340 had = re.search('->\ (.+?)\ ',decayString).group(1)
341 decaysplit = decayString.split(' ')
342 for split in decaysplit:
343 if split.find('mu')>-1 or split.find('e')>-1 or split.find('tau')>-1:
344 lep = split
345 if had == 'pi+' or had == 'pi-' or had == 'pi0':
346 print("findBranchingRatio() ERROR: Pions in final "\
347 "state have not been implemented, please choose "\
348 "a different decay mode of out...\n")
349 print(self.decays)
350 return -999
351
352 corrdecstring = 'N -> %s %s'%(had,lep)
353 listdecs = self.decays[self.bench]
354 gooddec = False
355 print("findBranchingRation() INFO: "\
356 "You have chosen the decay: '",\
357 corrdecstring)
358 for dec in listdecs:
359 if corrdecstring in dec:
360 gooddec = True
361 if gooddec is False:
362 print("findBranchingRation() ERROR: Badly "\
363 "formulated decay string, please choose "\
364 "one of the following\n")
365 print(self.decays)
366 return -999
367
368 br = 0.
369 totalwidth = self.NdecayWidth()
370 if totalwidth > 0.0:
371 br = self.Width_H_L(had,lep)/totalwidth
372 return br
373
374
375 def findProdBranchingRatio(self, decayString):
376 """
377 Returns the branching ratio of the selected SUSY RPV neutralino product channel
378
379 Inputs:
380 - decayString is a string describing the decay, in the form 'H -> N ... stuffN'
381 """
382 had = re.search('(.+?)\ ->',decayString).group(1)
383 decaysplit = decayString.split(' ')
384 for split in decaysplit:
385 if split.find('mu')>-1 or split.find('e')>-1 or split.find('tau')>-1:
386 lep = split
387 if had == 'pi+' or had == 'pi-' or had == 'pi0':
388 print("findProdBranchingRatio() ERROR: Pions in final "\
389 "state have not been implemented, please choose "\
390 "a different decay mode of out...\n")
391 print(self.decays)
392 return -999
393
394 corrdecstring = '%s -> N %s'%(had,lep)
395 listdecs = self.prods[self.bench]
396 gooddec = False
397 print("findProdBranchingRation() INFO: "\
398 "You have chosen the decay: '",\
399 corrdecstring)
400 for dec in listdecs:
401 if corrdecstring in dec:
402 gooddec = True
403 if gooddec is False:
404 print("findProdBranchingRation() ERROR: Badly "\
405 "formulated decay string, please choose "\
406 "one of the following\n")
407 print(self.decays)
408 return -999
409
410 br = self.Width_N_L(had,lep)/(self.Width_N_L(had,lep)+c.hGeV/lifetime(had))
411 return br
412
414 """
415 SUSY RPV neutralino physics according to the nuMSM
416 """
417 def __init__(self, mass, couplings, sfmass, benchmark, debug=False):
418 """
419 Initialize with mass and couplings of the HNL
420
421 Inputs:
422 mass (GeV)
423 couplings (list of [\lambda_{production}^{2}, \lambda_{decay}^{2}])
424 mass of universal sfermion
425 which benchmark (integer between 1 and 5 inclusive)
426 """
427 RPVSUSYbranchings.__init__(self, mass, couplings, sfmass, benchmark, debug)
428 def computeNLifetime(self,system="SI"):
429 """
430 Compute the RPV neutralino lifetime
431
432 Inputs:
433 - system: choose between default (i.e. SI, result in s) or FairShip (result in ns)
434 """
435 decwidth = self.NdecayWidth()
436 if decwidth == 0.0:
437 return 0.0
438 self.NLifetime = c.hGeV / decwidth
439 if system == "FairShip": self.NLifetime *= 1.e9
440 return self.NLifetime
441
computeNLifetime(self, system="SI")
Definition rpvsusy.py:428
__init__(self, mass, couplings, sfmass, benchmark, debug=False)
Definition rpvsusy.py:417
__init__(self, mass, couplings, sfmass, benchmark, debug=False)
Definition rpvsusy.py:151
AddChannelsToPythia(self, P8Gen, verbose=True)
Definition rpvsusy.py:197
findDecayBranchingRatio(self, decayString)
Definition rpvsusy.py:333
findProdBranchingRatio(self, decayString)
Definition rpvsusy.py:375
lifetime(particle)
Definition rpvsusy.py:84
width(particle)
Definition rpvsusy.py:76
PDGcode(particle)
Definition rpvsusy.py:60