Overview and Tutorial

This module implements QCD evolution equations for the coupling, quark masses, and other quantities, like matrix elements of local operators, that have an anomalous dimension. The module also provides tools for manipulating and evolving perturbation series. The coupling and quark-mass values used in the examples are from lattice QCD (arXiv:1408.4169).

QCD Running Coupling

A QCD coupling is created by specifying its value alpha0 at some scale mu0, together with the number of nf of quark flavors included in the vacuum polarization: for example,

>>> import qcdevol as qcd
>>> al = qcd.Alpha_s(alpha0=0.2128, mu0=5., nf=4)
>>> al(5)
0.2128

creates a 4-flavor coupling whose value at mu=5 is 0.2128. The value of the coupling at another scale mu is given by al(mu), where mu can be an array:

>>> al(1)
0.46677059443925056
>>> al([1., 5., 1000.])
array([0.46677059, 0.2128    , 0.08190909])

The initial value alpha0 can be specified with an uncertainty using the gvar module:

>>> import gvar as gv
>>> al = qcd.Alpha_s(alpha0=gv.gvar('0.2128(25)'), mu0=5., nf=4)
>>> al([1., 5., 1000.])
array([0.467(15), 0.2128(25), 0.08191(34)], dtype=object)

Here alpha0 is a Gaussian random variable (type gvar.GVar) representing 0.2128±0.0025. Such variables can be manipulated in arithmetic statements just like numbers. Uncertainties (and correlations) are propagated automatically; see the documentation for gvar for further information.

Heavy-quark flavors can be added or subtracted to create new couplings: for example,

>>> mb = gv.gvar('4.164(23)')           # mb(mb, nf=5)
>>> al5 = al.add_quark(m=mb, mu=mb)     # add b to vac. polarization ==> nf=5
>>> al5(1000.)
0.08692(39)
>>> mc = gv.gvar('0.9851(63)')          # mc(3., nf=4)
>>> al3 = al.del_quark(m=mc, mu=3)      # delete c from vac. polarization ==> nf=3
>>> al3(1000.)
0.07640(29)

creates a 5-flavor coupling al5 by adding a b-quark whose 5-flavor mass is mb at mu=mb, and a 3-flavor coupling by deleting a c-quark whose 4-flavor mass is mc at mu=3. Note that del_quark undoes add_quark and vice versa:

>>> new_al = al5.del_quark(m=mb, mu=mb)
>>> new_al(1000.)                       # compare al(1000) above
0.08191(34)
>>> new_al = al3.add_quark(m=mc, mu=3)
>>> new_al(1000.)                       # compare al(1000) above
0.08191(34)

The couplings here are all in the standard \overline{\mathrm{MS}} scheme. Other definitions/schemes for the coupling are supported (see below).

Running Masses

A running mass in the \overline{\mathrm{MS}} scheme is defined by the value m0 of the mass at some scale mu0, in addition to the running coupling constant: for example,

>>> import qcdevol as qcd
>>> al = qcd.Alpha_s(alpha0=0.2128, mu0=5., nf=4)
>>> mc = qcd.M_msb(m0=0.9851, mu0=3., alpha=al)
>>> mc(3)
0.9851
>>> mc(1000)
0.5377724915576455
>>> mc([1., 5., 1000.])
array([1.43226913, 0.89022211, 0.53777249])

defines a c-quark mass in a theory with nf=4 light-quark flavors (u,d,s,c), where the value at scale mu is given by mc(mu).

The scale can be replaced by a string containing an arithmetic expression involving the mass (represented by 'm'). For example, mc('m') chooses a scale such that mu=mc(mu):

>>> mc('m')                 # mu = mc(mu)
1.2737719860674563
>>> mc('3*m')               # mu = 3 * mc(mu)
0.9878145926947802
>>> mc(3 * mc('3*m'))       # mu = 3 * mc(mu)
0.9878145927281744

Again, uncertainties can be introduced by making m0 a Gaussian random variable using the gvar module:

>>> import gvar as gv
>>> al = qcd.Alpha_s(alpha0=gv.gvar('0.2128(25)'), mu0=5., nf=4)
>>> mc = qcd.M_msb(m0=gv.gvar('0.9851(63)'), mu0=3, alpha=al)
>>> mc(1000.)
0.5378(48)

where now the uncertainty in mc(1000) comes from both the coupling and the initial mass.

The c-quark masses above are for a theory with nf=4 flavors, which is useful at scales below the b-quark mass. At higher scales, however, the b quark should be included in the vacuum polarization. A new running mass with nf=5 flavors can be created using M_msb.add_quark():

>>> mb = gv.gvar('4.164(23)')           # mb(mb, nf=5)
>>> mc5 = mc.add_quark(m=mb, mu=mb)     # add b to vac. polarization (nf=5)
>>> mc5(1000)
0.5286(48)

Again M_msb.del_quark() undoes M_msb.add_quark(), and vice versa:

>>> mc5.del_quark(m=mb, mu=mb)(1000)    # compare mc(1000) above
0.5387(48)

Operator Z Factors

The \mu dependence of a local operator defined in the \overline{\mathrm{MS}} scheme is canceled by multiplying by a Z(\mu) factor where

\mu^2\frac{d\ln{Z(\mu)}}{d\mu^2} = - \alpha_s(\mu)\sum_{n=0}^{N_\gamma} \gamma_n\, \alpha_s^n(\mu)

where the \gamma_n on right-hand side specify the operator’s anomalous dimension. Such functions are represented in qcdevol by objects of type OPZ: for example,

>>> import qcdevol as qcd
>>> al = qcd.Alpha_s(alpha0=0.2128, mu0=5., nf=4)
>>> z = qcd.OPZ(z0=2.5, mu0=10., alpha=al, gamma=[1., 0.5, 0.25])
>>> z(10)
2.5
>>> z(1000)
0.7974943209599848

defines a Z factor z(mu) where the array gamma specifies the coefficients \gamma_n in the evolution equation.

Coupling Schemes

By default, qcdevol uses the standard \overline{\mathrm{MS}} scheme to define couplings, but other definitions are supported and it is easy to switch between schemes. For example,

>>> import qcdevol as qcd
>>> alv = qcd.Alpha_s(alpha0=0.2618042866, mu0=5, nf=4, scheme='v')
>>> alv(5)
0.26180428659999827
>>> alv(5, 'msb')
0.21280000003610486

defines a coupling in the V scheme but alv(5, 'msb') gives the value of the corresponding \overline{\mathrm{MS}} coupling at mu=5. The V scheme is defined in terms of the static-quark potential and is particularly useful when the renormalization scale is set using the BLM criterion (…ref…).

The coupling \alpha_s in another scheme is specified in terms of \alpha_\mathrm{\overline{MS}}:

\alpha_s(\mu) \equiv
\alpha_\mathrm{\overline{MS}}(r\mu)\Big(1 + \alpha_\mathrm{\overline{MS}}(r\mu)
\sum_{n=0}^{N_s} c_n\, \alpha_\mathrm{\overline{MS}}^{n}(r\mu)
\Big)

Schemes are implemented using a dictionary qcdevol.SCHEMES where SCHEMES[scheme](nf) is a function call that returns a tuple containing the c_n coefficients and the rescaling parameter r for nf flavors:

>>> qcd.SCHEMES['msb'](4)           # MS-bar scheme with nf=4
(array([], dtype=float64), 1.0)
>>> qcd.SCHEMES['v'](4)             # V scheme with nf=4
(array([-0.63661977,  0.98060332]), 0.4345982085070782)

From this output above, the nf=4 V coupling is given by:

\alpha_V(\mu) \equiv \alpha_\mathrm{\overline{MS}}(0.435 \mu)
- 0.637 \alpha_\mathrm{\overline{MS}}^2(0.435 \mu)
+ 0.981 \alpha_\mathrm{\overline{MS}}^3(0.435 \mu)

Additional schemes can be added to the dictionary.

QED Effects

Leading-order QED corrections from the beta function and quark mass gamma function can be included in couplings and quark masses:

>>> import qcdevol as qcd
>>> al = qcd.Alpha_s(alpha0=0.2128, mu0=5., nf=4, alpha_qed=1/130)
>>> m = qcd.M_msb(m0=4.513, mu0=3., alpha=al, Q=1/3)

Here alpha_qed is the QED coupling and Q is the QED charge of the quark in units of the proton mass (the sign is irrelevant). The running of the QED coupling is higher order and so is neglected here; alpha_qed should be set to a value of the running QED coupling appropriate to the scales of interest.

Power Series

qcdevol has a couple of tools for manipulating perturbation series. These series are represented by objects of type gvar.powerseries.PowerSeries where, for example,

>>> from gvar.powerseries import PowerSeries
>>> ps = PowerSeries([1, 0.5, 0.25], order=5)
>>> ps.c
array([1.  , 0.5 , 0.25, 0.  , 0.  , 0.  ])
>>> ps(.1)                  # series evaluated at x=0.1
1.0525
>>> (1 / ps).c              # coefficients of expansion of 1/ps
array([ 1.    , -0.5   ,  0.    ,  0.125 , -0.0625,  0.    ])

creates a power series ps where ps(x) is 1 + 0.5 * x + 0.25 * x**2. The coefficients in the power series are given by array ps.c. Objects of type PowerSeries can be manipulated in arithmetic expressions (e.g., 1/ps); the order parameter determines the order to which the results of such manipulations are retained (here order=5). See the gvar documentation for more information.

Coupling evolution can be encoded as a perturbative expansion using the Alpha_s.ps() method:

>>> import qcdevol as qcd
>>> al = qcd.Alpha_s(alpha0=0.2128, mu0=5., nf=4)
>>> al_mu = al.ps(mu=10, mu0=5.)
>>> al_mu.c
array([ 0.00000000e+00,  1.00000000e+00, -9.19315001e-01,  3.94494408e-01,
       -2.51122798e-02, -2.95286242e-01,  6.88557903e-01, -6.96944872e-01,
        6.62799259e-02,  9.06342876e-01, -1.81543549e+00,  2.11988983e+00,
       -1.09845078e+00, -1.42954114e+00,  4.49291959e+00, -6.15638652e+00,
        4.21269069e+00,  2.52873248e+00, -1.24163903e+01,  1.99914050e+01,
       -1.74877661e+01, -8.36210407e-01,  3.26707481e+01, -6.30713131e+01,
        6.63081007e+01, -1.80173513e+01])
>>> al_mu(al(5.))
0.17484202878404417
>>> al(10.)
0.17484202878432245

Here al_mu is the perturbative expansion of al(10) in powers of al(5.). The default order for such expansions is 25; it can be reset using parameter order (or using qcdevol.setdefaults()). Although the QCD \beta function in qcdevol includes only 5 terms, we generally want to include many more terms in expansions from Alpha_s.ps() in order to capture the leading logarithms from higher orders. (Note how the size of the coefficients grows with the order, because of these logarithms.) This is especially true when mu and mu0 are very different. As shown, the power series evaluated with argument al(5.) reproduces the value of al(10.) to high precision.

Expansions of this sort are useful for re-expressing perturbation series in terms of different couplings. For example, assume the power series ps above is the expansion of a physical quantity in terms of al(10.). We can obtain the power series for that same quantity but expressed in terms of al(5.) from ps(al_mu):

>>> ps(al(10.))
1.0950634481495156
>>> ps_5 = ps(al_mu)
>>> ps_5.c
array([  1.        ,   0.5       ,  -0.2096575 ,  -0.2624103 ,
         0.39597608,  -0.34153157,   0.24708534,   0.12658372,
        -0.68992001,   0.94619112,  -0.60932994,  -0.3442165 ,
         1.74456656,  -2.85761819,   2.4879741 ,   0.13905648,
        -4.55797342,   8.65938371,  -8.91160139,   2.00821817,
        12.10714048, -27.6086077 ,  32.89875863, -15.31519961,
       -29.39399005,  86.51115021])
>>> ps_5(al(5.))
1.0950634481493524

Such manipulations are simplified by using qcdevol.evol_ps():

>>> ps_5 = qcd.evol_ps(ps, mu=5., mu0=10., nf=4, order=25)

gives the same result as above. (Note that order=25 is specified so that results match with analysis above; absent this specification the order is taken from that of ps – i.e., order=5.)

Note that qcdevol.evol_ps() also works for expansions describing quantities with an anomalous dimension (specified by parameter gamma):

>>> zps = PowerSeries([1., .5, .125], order=25) # power series in al(1)
>>> zps(al(1))
1.2606196456987717
>>> zps_mu = qcd.evol_ps(zps, mu=2, mu0=1, nf=4, gamma=[.3, .1])
>>> zps_mu.c
array([1.00000000e+00, 8.41116917e-02, 1.33399272e-01, 2.99767072e-01,
       5.68779314e-01, 1.17136230e+00, 2.46037314e+00, 5.19810496e+00,
       1.10290278e+01, 2.35302136e+01, 5.04927453e+01, 1.08877565e+02,
       2.35723191e+02, 5.12122316e+02, 1.11602245e+03, 2.43865702e+03,
       5.34168259e+03, 1.17258547e+04, 2.57902639e+04, 5.68239130e+04,
       1.25400917e+05, 2.77142855e+05, 6.13319516e+05, 1.35894881e+06,
       3.01447117e+06, 2.01340283e+07])
>>> zps_mu(al(2))
1.059323660860776
>>> z = qcd.OPZ(z0=zps(al(1)), mu0=1, alpha=al, gamma=[.3, .1])
>>> z(2)
1.0593237119435541

Here a Z factor at scale mu0=1 is specified in terms of a power series in al(1). The power series for the Z factor at scale mu=2 in terms of al(2) is given by zps_mu. The coefficients get quite large in zps_mu but al(2)**n gets smaller faster, so the result for zps_mu(al(2)) is quite accurate (compare with z(2)). Higher orders are needed when mu/mu0 becomes large or small.

More About Uncertainties

As discussed above, parameters alpha0 in Alpha_s and m0 in M_msb can be specified with uncertainties by replacing numbers with Gaussian random variables (objects with a mean and standard deviation of type gvar.GVar). In fact almost any parameter in the classes and functions discussed above can be a gvar.GVar, and the associated uncertainties and correlations are propagated through the various methods. This allows for a comprehensive analysis of the impact of such uncertainties on results. (It also means these objects can be used in fit functions for nonlinear least squares fitting using the lsqfit module.)

The coupling, for example, typically has uncertainties due to the initial values (alpha0). One might also worry about errors associated due to the fact that only the first five terms of the beta function are included by qcdevol. The impact of such errors on the coupling value and a quark mass at, say, the Z mass is easily measured by running the following code:

import numpy as np
import gvar as gv
import qcdevol as qcd

# uncertainty due to initial values
alpha0 = gv.gvar('0.2128(25)')
print('alpha0 =', alpha0)
m0 = gv.gvar('0.9851(63)')
print('m0 =', m0)

# uncertainty due to running of alpha_qed
alpha_qed = 1. / gv.gvar('130(5)')
print('alpha_qed =', alpha_qed)

# uncertainty in mass of Z
Mz = gv.gvar('91.1876(21)')
print('Mz =', Mz, '\n')

# uncertainty due to higher-order term in beta function
bmsb = qcd.BETA_MSB(nf=4, alpha_qed=alpha_qed, terr=gv.gvar('0(1)'))
print('extended beta_msb =', bmsb, '\n')

# uncertainty due to higher-order terms in gamma function
gmsb = qcd.GAMMA_MSB(nf=4, alpha_qed=alpha_qed, Q=1/3, terr=gv.gvar('0(1)'))
print('extended gamma_msb =', gmsb, '\n')

# create alpha with alph0 and extended beta_msb
al = qcd.Alpha_s(alpha0=alpha0, mu0=5., nf=4, alpha_qed=alpha_qed, beta_msb=bmsb)
print('al(Mz) =', al(Mz))

# create quark mass with m0 and extended gamma
m = qcd.M_msb(m0=m0, mu0=3., alpha=al, Q=1/3, gamma_msb=gmsb)
print('m(Mz) =', m(Mz), '\n')

# create error budget for al(Mz)
inputs = dict(
    alpha0=alpha0, m0=m0, beta=bmsb[-1], gamma=gmsb[-1],
    Mz=Mz, alpha_qed=alpha_qed
    )
outputs = {'al(Mz)':al(Mz), 'm(Mz)':m(Mz)}
print(gv.fmt_errorbudget(inputs=inputs, outputs=outputs, ndecimal=4))

In addition to the uncertainty in alpha0, we include uncertainty in the Z mass, and we add a extra term 0.00±0.38 to the beta function (of order the root-mean-square of the other coefficients), beyond the ones normally used by qcdevol (given by qcdevol.BETA_MSB(4)). Similarly we add an extra term 0.00±0.33 to the quark’s gamma function. Running the code gives the following ouput:

alpha0 = 0.2128(25)
m0 = 0.9851(63)
alpha_qed = 0.00769(30)
Mz = 91.1876(21)

extended beta_msb = [0.6631455962162306 0.3249639(42) 0.2047328(15) 0.32222297343221057
 0.1860792072936385 0.00(38)]

extended gamma_msb = [0.31833154(83) 0.3702317(49) 0.3208073114884162 0.2802914321897117
 0.3646484366907343 0.00(33)]

al(Mz) = 0.11270(66)
m(Mz) = 0.6325(52)

Partial % Errors:
              al(Mz)     m(Mz)
------------------------------
   alpha0:    0.5870    0.5245
       m0:    0.0000    0.6395
     beta:    0.0027    0.0006
    gamma:    0.0000    0.0094
       Mz:    0.0004    0.0002
alpha_qed:    0.0000    0.0056
------------------------------
    total:    0.5870    0.8272

This shows that al(Mz) has an uncertainty of 0.5870%, with 0.5870% coming from alpha0, 0.0027% from the extended beta function, 0.0004% from the Z mass, and less than 0.0001% from the QED coupling. (The separate errors are added in quadrature to obtain the total error.) The uncertainty in the quark mass m(Mz) is dominated by the uncertainties in initial values alpha0 and m0. The QED coupling and the extended gamma function are next in importance, but negligible. Here uncertainties in the initial values are the only uncertainties that matter for both the coupling and mass.