2# ==================================================================
5# This module provides methods to compute the lifetime and
6# branching ratio of HNLs given its mass and couplings as
9# Created: 30/11/2014 Elena Graverini (elena.graverini@cern.ch)
11# Updated: 07/10/2017 Kyrylo Bondarenko (bondarenko@lorentz.leidenuniv.nl)
15# In [1]: b = HNL(1.,[1.e-8, 2.e-8, 1.e-9],True)
16# HNLbranchings instance initialized with couplings:
22# In [2]: b.computeNLifetime()
23# Out[2]: 4.777721453160521e-05
24# In [3]: b.findBranchingRatio('N -> pi mu')
25# Out[3]: 0.11826749348890987
27# ==================================================================
29from __future__
import print_function
30from __future__
import division
36pdg = ROOT.TDatabasePDG.Instance()
40 Change particle name for use with the PDG database
43 particle = particle.replace(
'1',
"'")
44 if particle ==
'nu':
return 'nu_e'
49 Read particle mass from PDG database
51 particle = PDGname(particle)
52 tPart = pdg.GetParticle(particle)
57 Read particle lifetime from PDG database
59 particle = PDGname(particle)
60 tPart = pdg.GetParticle(particle)
61 return tPart.Lifetime()
65 CKM matrix, from http://pdg.lbl.gov/2017/reviews/rpp2016-rev-ckm-matrix.pdf
80 Store some constants useful for HNL physics
85 'eta':1.2*0.130*u.GeV,
94 self.
GF = 1.166379e-05/(u.GeV*u.GeV)
96 self.
heV = 6.58211928*pow(10.,-16)
104 Lifetime and total and partial decay widths of an HNL
108 Initialize with mass and couplings of the HNL
112 couplings (list of [U2e, U2mu, U2tau])
115 self.
U = [math.sqrt(ui)
for ui
in self.
U2]
119 'rho+':self.
CKM.Vud**2.,
120 'D_s+':self.
CKM.Vcs**2.,
121 'D*_s+':self.
CKM.Vcs**2.,
122 'K+':self.
CKM.Vus**2.,
126 (1,1):self.
CKM.Vud**2., (1,2):self.
CKM.Vus**2., (1,3):self.
CKM.Vub**2.,
127 (2,1):self.
CKM.Vcd**2., (2,2):self.
CKM.Vcs**2., (2,3):self.
CKM.Vcb**2.,
128 (3,1):self.
CKM.Vtd**2., (3,2):self.
CKM.Vts**2., (3,3):self.
CKM.Vtb**2.}
140 'N -> mu- mu+ nu_mu',
141 'N -> mu- mu+ nu_tau',
160 'N -> e- tau+ nu_tau',
162 'N -> mu- tau+ nu_tau',
163 'N -> tau- mu+ nu_mu',
170 'N -> eta_c nu_tau' ]
175 print(
"HNLbranchings instance initialized with couplings:")
176 print(
"\tU2e = %s"%self.
U2[0])
177 print(
"\tU2mu = %s"%self.
U2[1])
178 print(
"\tU2tau = %s"%self.
U2[2])
180 print(
"\tm = %s GeV"%(self.
MN))
184 Useful function for decay kinematics. Returns 0 for kinematically forbidden region
186 l = a**2 + b**2 + c**2 - 2*a*b - 2*b*c - 2*a*c
194 Returns 3-loops QCD correction to HNL decay width into quarks
196 alpha_s = ROOT.TGraph( os.path.expandvars(
'$FAIRSHIP/python/alpha_s.dat') )
197 a_s = alpha_s.Eval(self.
MN)
198 qcd_corr = a_s / math.pi
199 qcd_corr += 5.2 * (a_s / math.pi)**2.
200 qcd_corr += 26.4 * (a_s / math.pi)**3.
205 Returns the HNL decay width into three neutrinos
207 width = (c.GF**2.)*(self.
MN**5.)*sum(self.
U2)/(192.*(u.pi**3.))
213 Returns the HNL tree level decay width into the fermion-antifermion pair and a neutrino (decay through Z boson or Z and W)
216 - alpha is a flavour of neutrino (1,2 or 3), determine the mixing angle U_alpha
217 - beta is a flavour of the fermions: (1, 2 or 3) for charge leptons or (4, 5, 6, 7, 8, 9) for quarks
219 if (alpha
not in [1,2,3])
or (beta
not in [1,2,3,4,5,6,7,8,9]):
220 print(
'Width_nu_f_fbar ERROR: unknown channel alpha =',alpha,
' beta =',beta)
222 l = [
None,
'e-',
'mu-',
'tau-',
'u',
'd',
's',
'c',
'b',
't']
223 x = mass(l[beta]) / self.
MN
226 width = (c.GF**2.)*(self.
MN**5.)*self.
U2[alpha-1]/(192.*(math.pi**3.))
229 logContent = (1. - 3.*x**2. - (1.-x**2.)*math.sqrt(1. - 4.*x**2.) ) / ( (x**2.)*(1 + math.sqrt(1. - 4.*x**2.)) )
231 logContent = x**4 + 4.*x**6 + 14.*x**8
233 L = math.log( logContent )
237 C1 = 0.25*(1. + 4.*c.s2thetaw + 8.*c.s2thetaw**2.)
238 C2 = 0.5*c.s2thetaw*(2.*c.s2thetaw +1.)
240 C1 = 0.25*(1. - 4.*c.s2thetaw + 8.*c.s2thetaw**2.)
241 C2 = 0.5*c.s2thetaw*(2.*c.s2thetaw -1.)
244 if (beta
in [4,7,9]):
245 C1 = 0.25*(1. - 8./3.*c.s2thetaw + 32./9.*c.s2thetaw**2.)
246 C2 = 1./3.*c.s2thetaw*(4./3.*c.s2thetaw - 1.)
247 if (beta
in [5,6,8]):
248 C1 = 0.25*(1. - 4./3.*c.s2thetaw + 8./9.*c.s2thetaw**2.)
249 C2 = 1./6.*c.s2thetaw*(2./3.*c.s2thetaw - 1.)
250 width = width*NZ*( C1*( (1.-14.*x**2. -2.*x**4. -12.*x**6.)*math.sqrt(1-4.*x**2) +12.*x**4. *(-1.+x**4.)*L )
251 + 4.*C2*( x**2. *(2.+10.*x**2. -12.*x**4.) * math.sqrt(1.-4.*x**2) + 6.*x**4. *(1.-2.*x**2+2.*x**4)*L ) )
257 Function to integrate needed for numerical integration using ROOT. Needed for 3-body decays trough W boson.
258 First argument is a list of 1 number, argument. Second is the list of 3 numbers xi = mi/MN
267 res *= (x - xl2 - xd2)
268 res *= (1. + xu2 - x)
273 def I(self,x1,x2,x3):
275 Numerical integral needed for 3-body decays trough W boson.
279 func = ROOT.TF1(
'func',theFunction,0,1,3)
280 func.SetParameters(x1,x2,x3)
283 wf1 = ROOT.Math.WrappedTF1(func)
284 ig = ROOT.Math.GaussIntegrator()
286 ig.SetRelTolerance(0.001)
287 res = 12. * ig.Integral(xmin,xmax)
292 Returns the HNL decay width into two different flavour leptons and a neutrino (decay through W boson)
295 - alpha is a flavour of the first lepton (1,2 or 3), determine the mixing angle U_alpha
296 - beta is a flavour of the second lepton and neutrino (1, 2 or 3)
298 if (alpha
not in [1,2,3])
or (beta
not in [1,2,3]):
299 print(
'Width_l1_l2_nu2 ERROR: unknown channel alpha =',alpha,
' beta =',beta)
303 l = [
None,
'e-',
'mu-',
'tau-']
304 x1 = mass(l[alpha])/self.
MN
305 x2 = mass(l[beta])/self.
MN
308 width = (c.GF**2.)*(self.
MN**5.)*self.
U2[alpha-1]/(192.*(math.pi**3.))
309 width = width*self.
I(x1,x2,0)
315 Returns the HNL tree level decay width into a charged lepton, up quark and down quark (decay through W boson)
318 - alpha is a flavour of the charged lepton (1,2 or 3), determine the mixing angle U_alpha
319 - beta is the up quark generation (1, 2, 3 for u, c, t)
320 - gamma is the down quark generation (1, 2, 3 for d, s, b)
322 if (alpha
not in [1,2,3])
or (beta
not in [1,2,3])
or (gamma
not in [1,2,3]):
323 print(
'Width_l_u_d ERROR: unknown channel alpha =',alpha,
' beta =',beta,
' gamma =',gamma)
325 l = [
None,
'e-',
'mu-',
'tau-']
326 u = [
None,
'u',
'c',
't']
327 d = [
None,
'd',
's',
'b']
328 xl = mass(l[alpha])/self.
MN
329 xu = mass(u[beta])/self.
MN
330 xd = mass(d[gamma])/self.
MN
333 width = (c.GF**2.)*(self.
MN**5.)*self.
U2[alpha-1]/(192.*(math.pi**3.))
335 width *= self.
I(xl,xu,xd)
341 Returns the HNL decay width into neutral meson and neutrino
344 - H is a string (name of the meson)
345 - alpha is a flavour of the nu (1, 2 or 3), determine the mixing angle U_alpha
347 if (H
not in [
'pi0',
'eta',
'rho0',
'omega',
'eta1',
'phi',
'eta_c'])
or (alpha
not in [1,2,3]):
348 print(
'Width_H0_nu ERROR: unknown channel H0 =',H,
' alpha =',alpha)
353 width = (c.GF**2.)*(c.decayConstant[H]**2.)*(self.
MN**3.)*self.
U2[alpha-1]/(32.*math.pi)
354 if H
in [
'pi0',
'eta',
'eta1',
'eta_c']:
355 width *= (1 - x**2.)**2.
356 if H
in [
'rho0',
'omega',
'phi']:
357 width *= (1. + 2*x**2.)*(1. - x**2.)**2.
359 width *= (1. - 2.*c.s2thetaw)**2.
361 width *= (16.*c.s2thetaw**2.)/9.
363 width *= (1. - 4./3.*c.s2thetaw)**2.
369 Returns the HNL decay width into charged meson and lepton
372 - H is a string (name of the positively charged meson)
373 - alpha is the lepton flavour (1, 2 or 3), determine the mixing angle U_alpha
375 if (H
not in [
'pi+',
'rho+',
'D_s+',
'D*_s+'])
or (alpha
not in [1,2,3]):
376 print(
'Width_H_l ERROR: unknown channel H =',H,
' alpha =',alpha)
378 l = [
None,
'e-',
'mu-',
'tau-']
379 xl = mass(l[alpha])/self.
MN
383 width = (c.GF**2.)*(c.decayConstant[H]**2.)*self.
CKMelemSq[H]*(self.
MN**3.)*self.
U2[alpha-1]/(16.*math.pi)
385 if H
in [
'pi+',
'D_s+']:
386 width *= ( (1. - xl**2.)**2. - xh**2. *(1. + xl**2.) )
387 if H
in [
'rho+',
'D*_s+']:
388 width *= ( (1. - xl**2.)**2. + xh**2.*(1. + xl**2.) - 2.*xh**4. )
394 Returns the total HNL leptonic decay width with charged leptons in final state
405 Returns the total HNL decay width into a neutral meson and a neutrino
407 mesons = [
'pi0',
'eta',
'rho0',
'omega',
'eta1',
'phi',
'eta_c']
416 Returns the total HNL decay width into a charged meson and a charged lepton
418 mesons = [
'pi+',
'rho+',
'D_s+',
'D*_s+']
427 Returns the total HNL decay width with quarks and neutrino in final state. Uses 3-loops alpha_s correction
433 for q
in [4,5,6,7,8,9]:
440 Returns the total HNL decay width with quarks and charged lepton in final state. Uses 3-loops alpha_s correction
454 Returns the total HNL decay width
459 totalWidth = leptonWidth + max([mesonWidth, quarkWidth])
464 Returns the branching ratio of the selected HNL decay channel
467 - decayString is a string describing the decay, in the form 'N -> stuff1 ... stuffN'
472 if (decayString
not in self.
decays)
and (decayString
not in [
'N -> hadrons',
'N -> charged hadrons']):
473 print(
'findBranchingRatio ERROR: unknown decay %s'%decayString)
476 if decayString ==
'N -> nu nu nu' or decayString ==
'N -> 3nu': br = self.
Width_3nu() / totalWidth
477 if decayString ==
'N -> e- e+ nu_e': br = self.
Width_nu_f_fbar(1,1) / totalWidth
478 if decayString ==
'N -> e- e+ nu_mu': br = self.
Width_nu_f_fbar(2,1) / totalWidth
479 if decayString ==
'N -> e- e+ nu_tau': br = self.
Width_nu_f_fbar(3,1) / totalWidth
480 if decayString ==
'N -> e- mu+ nu_mu': br = self.
Width_l1_l2_nu2(1,2) / totalWidth
481 if decayString ==
'N -> mu- e+ nu_e': br = self.
Width_l1_l2_nu2(2,1) / totalWidth
482 if decayString ==
'N -> pi0 nu_e': br = self.
Width_H0_nu(
'pi0',1) / totalWidth
483 if decayString ==
'N -> pi0 nu_mu': br = self.
Width_H0_nu(
'pi0',2) / totalWidth
484 if decayString ==
'N -> pi0 nu_tau': br = self.
Width_H0_nu(
'pi0',3) / totalWidth
485 if decayString ==
'N -> pi+ e-': br = self.
Width_H_l(
'pi+',1) / totalWidth
486 if decayString ==
'N -> mu- mu+ nu_e': br = self.
Width_nu_f_fbar(1,2) / totalWidth
487 if decayString ==
'N -> mu- mu+ nu_mu': br = self.
Width_nu_f_fbar(2,2) / totalWidth
488 if decayString ==
'N -> mu- mu+ nu_tau': br = self.
Width_nu_f_fbar(3,2) / totalWidth
489 if decayString ==
'N -> pi+ mu-': br = self.
Width_H_l(
'pi+',2) / totalWidth
490 if decayString ==
'N -> eta nu_e': br = self.
Width_H0_nu(
'eta',1) / totalWidth
491 if decayString ==
'N -> eta nu_mu': br = self.
Width_H0_nu(
'eta',2) / totalWidth
492 if decayString ==
'N -> eta nu_tau': br = self.
Width_H0_nu(
'eta',3) / totalWidth
493 if decayString ==
'N -> rho0 nu_e': br = self.
Width_H0_nu(
'rho0',1) / totalWidth
494 if decayString ==
'N -> rho0 nu_mu': br = self.
Width_H0_nu(
'rho0',2) / totalWidth
495 if decayString ==
'N -> rho0 nu_tau': br = self.
Width_H0_nu(
'rho0',3) / totalWidth
496 if decayString ==
'N -> rho+ e-': br = self.
Width_H_l(
'rho+',1) / totalWidth
497 if decayString ==
'N -> omega nu_e': br = self.
Width_H0_nu(
'omega',1) / totalWidth
498 if decayString ==
'N -> omega nu_mu': br = self.
Width_H0_nu(
'omega',2) / totalWidth
499 if decayString ==
'N -> omega nu_tau': br = self.
Width_H0_nu(
'omega',3) / totalWidth
500 if decayString ==
'N -> rho+ mu-': br = self.
Width_H_l(
'rho+',2) / totalWidth
501 if decayString ==
'N -> eta1 nu_e': br = self.
Width_H0_nu(
'eta1',1) / totalWidth
502 if decayString ==
'N -> eta1 nu_mu': br = self.
Width_H0_nu(
'eta1',2) / totalWidth
503 if decayString ==
'N -> eta1 nu_tau': br = self.
Width_H0_nu(
'eta1',3) / totalWidth
504 if decayString ==
'N -> phi nu_e': br = self.
Width_H0_nu(
'phi',1) / totalWidth
505 if decayString ==
'N -> phi nu_mu': br = self.
Width_H0_nu(
'phi',2) / totalWidth
506 if decayString ==
'N -> phi nu_tau': br = self.
Width_H0_nu(
'phi',3) / totalWidth
507 if decayString ==
'N -> e- tau+ nu_tau': br = self.
Width_l1_l2_nu2(1,3) / totalWidth
508 if decayString ==
'N -> tau- e+ nu_e': br = self.
Width_l1_l2_nu2(3,1) / totalWidth
509 if decayString ==
'N -> mu- tau+ nu_tau': br = self.
Width_l1_l2_nu2(2,3) / totalWidth
510 if decayString ==
'N -> tau- mu+ nu_mu': br = self.
Width_l1_l2_nu2(3,2) / totalWidth
511 if decayString ==
'N -> D_s+ e-': br = self.
Width_H_l(
'D_s+',1) / totalWidth
512 if decayString ==
'N -> D_s+ mu-': br = self.
Width_H_l(
'D_s+',2) / totalWidth
513 if decayString ==
'N -> D*_s+ e-': br = self.
Width_H_l(
'D*_s+',1) / totalWidth
514 if decayString ==
'N -> D*_s+ mu-': br = self.
Width_H_l(
'D*_s+',2) / totalWidth
515 if decayString ==
'N -> eta_c nu_e': br = self.
Width_H0_nu(
'eta_c',1) / totalWidth
516 if decayString ==
'N -> eta_c nu_mu': br = self.
Width_H0_nu(
'eta_c',2) / totalWidth
517 if decayString ==
'N -> eta_c nu_tau': br = self.
Width_H0_nu(
'eta_c',3) / totalWidth
519 if decayString ==
'N -> hadrons':
522 br = max([mesonWidth, quarkWidth]) / totalWidth
523 if decayString ==
'N -> charged hadrons':
529 Returns a dictionary of kinematically allowed decay channels
532 - decayString is a string describing the decay, in the form 'N -> stuff1 ... stuffN'
535 allowedDecays = {
'N -> nu nu nu':
'yes'}
536 if m > 2.*mass(
'e-'):
537 allowedDecays.update({
'N -> e- e+ nu_e':
'yes'})
538 allowedDecays.update({
'N -> e- e+ nu_mu':
'yes'})
539 allowedDecays.update({
'N -> e- e+ nu_tau':
'yes'})
540 if m > mass(
'e-') + mass(
'mu-'):
541 allowedDecays.update({
'N -> e- mu+ nu_mu':
'yes'})
542 allowedDecays.update({
'N -> mu- e+ nu_e':
'yes'})
544 allowedDecays.update({
'N -> pi0 nu_e':
'yes'})
545 allowedDecays.update({
'N -> pi0 nu_mu':
'yes'})
546 allowedDecays.update({
'N -> pi0 nu_tau':
'yes'})
547 if m > mass(
'pi+') + mass(
'e-'):
548 allowedDecays.update({
'N -> pi+ e-':
'yes'})
549 if m > 2.*mass(
'mu-'):
550 allowedDecays.update({
'N -> mu- mu+ nu_e':
'yes'})
551 allowedDecays.update({
'N -> mu- mu+ nu_mu':
'yes'})
552 allowedDecays.update({
'N -> mu- mu+ nu_tau':
'yes'})
553 if m > mass(
'pi+') + mass(
'mu-'):
554 allowedDecays.update({
'N -> pi+ mu-':
'yes'})
556 allowedDecays.update({
'N -> eta nu_e':
'yes'})
557 allowedDecays.update({
'N -> eta nu_mu':
'yes'})
558 allowedDecays.update({
'N -> eta nu_tau':
'yes'})
560 allowedDecays.update({
'N -> rho0 nu_e':
'yes'})
561 allowedDecays.update({
'N -> rho0 nu_mu':
'yes'})
562 allowedDecays.update({
'N -> rho0 nu_tau':
'yes'})
563 if m > mass(
'rho+') + mass(
'e-'):
564 allowedDecays.update({
'N -> rho+ e-':
'yes'})
565 if m > mass(
'omega'):
566 allowedDecays.update({
'N -> omega nu_e':
'yes'})
567 allowedDecays.update({
'N -> omega nu_mu':
'yes'})
568 allowedDecays.update({
'N -> omega nu_tau':
'yes'})
569 if m > mass(
'rho+') + mass(
'mu-'):
570 allowedDecays.update({
'N -> rho+ mu-':
'yes'})
572 allowedDecays.update({
'N -> eta1 nu_e':
'yes'})
573 allowedDecays.update({
'N -> eta1 nu_mu':
'yes'})
574 allowedDecays.update({
'N -> eta1 nu_tau':
'yes'})
576 allowedDecays.update({
'N -> phi nu_e':
'yes'})
577 allowedDecays.update({
'N -> phi nu_mu':
'yes'})
578 allowedDecays.update({
'N -> phi nu_tau':
'yes'})
579 if m > mass(
'e-') + mass(
'tau-'):
580 allowedDecays.update({
'N -> e- tau+ nu_tau':
'yes'})
581 allowedDecays.update({
'N -> tau- e+ nu_e':
'yes'})
582 if m > mass(
'mu-') + mass(
'tau-'):
583 allowedDecays.update({
'N -> mu- tau+ nu_tau':
'yes'})
584 allowedDecays.update({
'N -> tau- mu+ nu_mu':
'yes'})
585 if m > mass(
'D_s+') + mass(
'e-'):
586 allowedDecays.update({
'N -> D_s+ e-':
'yes'})
587 if m > mass(
'D_s+') + mass(
'mu-'):
588 allowedDecays.update({
'N -> D_s+ mu-':
'yes'})
589 if m > mass(
'D*_s+') + mass(
'e-'):
590 allowedDecays.update({
'N -> D*_s+ e-':
'yes'})
591 if m > mass(
'D*_s+') + mass(
'mu-'):
592 allowedDecays.update({
'N -> D*_s+ mu-':
'yes'})
593 if m > mass(
'eta_c'):
594 allowedDecays.update({
'N -> eta_c nu_e':
'yes'})
595 allowedDecays.update({
'N -> eta_c nu_mu':
'yes'})
596 allowedDecays.update({
'N -> eta_c nu_tau':
'yes'})
599 if decay
not in allowedDecays:
600 allowedDecays.update({decay:
'no'})
608 HNL physics according to the nuMSM
612 Initialize with mass and couplings of the HNL
616 couplings (list of [U2e, U2mu, U2tau])
618 HNLbranchings.__init__(self, mass, couplings, debug)
621 Compute the HNL lifetime
624 - system: choose between default (i.e. SI, result in s) or FairShip (result in ns)
627 if system ==
"FairShip": self.
NLifetime *= 1.e9
__init__(self, mass, couplings, debug=False)
computeNLifetime(self, system="SI")
sqrt_lambda(self, a, b, c)
Width_charged_mesons(self)
Width_l1_l2_nu2(self, alpha, beta)
findBranchingRatio(self, decayString)
Width_quarks_lepton(self)
Width_l_u_d(self, alpha, beta, gamma)
Width_neutral_mesons(self)
Width_quarks_neutrino(self)
Width_H0_nu(self, H, alpha)
Width_H_l(self, H, alpha)
Width_charged_leptons(self)
__init__(self, mass, couplings, debug=False)
Width_nu_f_fbar(self, alpha, beta)