Forward model

Here, we go through an example of simulating the full forward model, from the prior definition to the expected number of events in different bins of the detector response.

using QCDNUM, PartonDensity
using Plots, Printf, NaNMath, Parameters, Random, Distributions

Define input PDFs

We can use DirichletPDFParams or ValencePDFParams, as long as we do so according to the PDF parametrisation and priors docs.

random_seed = 42

weights = [3.0, 1.0, 5.0, 5.0, 1.0, 1.0, 1.0, 0.5, 0.5]
θ = rand(MersenneTwister(random_seed), Dirichlet(weights))
pdf_params = DirichletPDFParams(K_u=4.0, K_d=6.0, λ_g1=0.7, λ_g2=-0.4,
    K_g=6.0, λ_q=-0.5, K_q=5.0, θ=θ);

Plot the input PDFs

plot_input_pdfs(pdf_params)

Sanity check that sum = 1

int_xtotx(pdf_params) ≈ 1
true

Define QCDNUM grids, weights and settings

grid = QCDNUM.GridParams(x_min=[1.0e-3], x_weights=[1], nx=100,
    qq_bounds=[1.0e2, 3.0e4], qq_weights=[1.0, 1.0], nq=50, spline_interp=3)

qcdnum_params = QCDNUM.EvolutionParams(order=2, α_S=0.118, q0=100.0, grid_params=grid,
    n_fixed_flav=5, iqc=1, iqb=1, iqt=1, weight_type=1);

Initialise

QCDNUM.init()
  +---------------------------------------------------------------------+
  |                                                                     |
  |    If you use QCDNUM, please refer to:                              |
  |                                                                     |
  |    M. Botje, Comput. Phys. Commun. 182(2011)490, arXiV:1005.1481    |
  |                                                                     |
  +---------------------------------------------------------------------+

Evolve the PDFs using QCDNUM

Define input PDF function

  • See QCDNUM docs under evolfg

There are functions available to generate the necessary input PDF function in the correct format for QCDNUM.jl (see get_input_pdf_func()), along with the mapping between this input function and quark species (see input_pdf_map).

Get function and PDF input map to fully describe the QCDNUM.InputPDF

my_func = get_input_pdf_func(pdf_params)
input_pdf = QCDNUM.InputPDF(func=my_func, map=input_pdf_map)
QCDNUM.InputPDF
  func: _input_pdfs (function of type PartonDensity.var"#_input_pdfs#22"{DirichletPDFParams{Float64, Vector{Float64}}})
  cfunc: Base.CFunction
  map: Array{Float64}((156,)) [0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0  …  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

Evolve the PDF over the specified grid

ϵ = QCDNUM.evolve(input_pdf, qcdnum_params)
0.0025676779122516535

For splines

nw = QCDNUM.zmfillw()
splint_params = QCDNUM.SPLINTParams();
quark_coeffs = QuarkCoefficients();

 ZMFILLW: start weight calculations   4  38   0   0
 ZMFILLW: calculations completed

Define initial spline

QCDNUM.ssp_spinit(splint_params.nuser)

ia = QCDNUM.isp_s2make(splint_params.nsteps_x, splint_params.nsteps_q);
xnd = QCDNUM.ssp_unodes(ia, splint_params.nnodes_x, 0);
qnd = QCDNUM.ssp_vnodes(ia, splint_params.nnodes_q, 0);
┌ Warning: SPLINT is already initialised, skipping call to QCDNUM.ssp_spinit()
└ @ QCDNUM ~/.julia/packages/QCDNUM/gx4Mo/src/splint/initialisation.jl:36

Check nodes and erase

QCDNUM.ssp_nprint(ia);
QCDNUM.ssp_erase(ia);

  N   IX     XNODE    NQMA      IQ     QNODE    NXMI
  1    1  0.10000E-02    0       1  0.10000E+03    0
  2    5  0.13183E-02    0       2  0.11234E+03    0
  3   10  0.18621E-02    0      12  0.35982E+03    0
  4   15  0.26303E-02    0      22  0.11525E+04    0
  5   20  0.37154E-02    0      32  0.36911E+04    0
  6   25  0.52481E-02    0      42  0.11822E+05    0
  7   30  0.74131E-02    0      50  0.30000E+05    0
  8   35  0.10471E-01    0       -       -         -
  9   40  0.14791E-01    0       -       -         -
 10   45  0.20893E-01    0       -       -         -
 11   50  0.29512E-01    0       -       -         -
 12   55  0.41687E-01    0       -       -         -
 13   60  0.58884E-01    0       -       -         -
 14   65  0.83176E-01    0       -       -         -
 15   70  0.11749E+00    0       -       -         -
 16   75  0.16596E+00    0       -       -         -
 17   80  0.23442E+00    0       -       -         -
 18   85  0.33113E+00    0       -       -         -
 19   90  0.46774E+00    0       -       -         -
 20   95  0.66069E+00    0       -       -         -
 21  100  0.93325E+00    0       -       -         -
 22  101  0.10000E+01    0       -       -         -

Set nodes and fill spline with structure function

iaFLup = QCDNUM.isp_s2user(xnd, splint_params.nnodes_x, qnd, splint_params.nnodes_q);
QCDNUM.ssp_s2f123(iaFLup, qcdnum_params.output_pdf_loc, quark_coeffs.proup, 1, 0.0);

iaF2up = QCDNUM.isp_s2user(xnd, splint_params.nnodes_x, qnd, splint_params.nnodes_q);
QCDNUM.ssp_s2f123(iaF2up, qcdnum_params.output_pdf_loc, quark_coeffs.proup, 2, 0.0);

iaF3up = QCDNUM.isp_s2user(xnd, splint_params.nnodes_x, qnd, splint_params.nnodes_q);
QCDNUM.ssp_s2f123(iaF3up, qcdnum_params.output_pdf_loc, quark_coeffs.valup, 3, 0.0);

iaFLdn = QCDNUM.isp_s2user(xnd, splint_params.nnodes_x, qnd, splint_params.nnodes_q);
QCDNUM.ssp_s2f123(iaFLdn, qcdnum_params.output_pdf_loc, quark_coeffs.prodn, 1, 0.0);

iaF2dn = QCDNUM.isp_s2user(xnd, splint_params.nnodes_x, qnd, splint_params.nnodes_q);
QCDNUM.ssp_s2f123(iaF2dn, qcdnum_params.output_pdf_loc, quark_coeffs.prodn, 2, 0.0);

iaF3dn = QCDNUM.isp_s2user(xnd, splint_params.nnodes_x, qnd, splint_params.nnodes_q);
QCDNUM.ssp_s2f123(iaF3dn, qcdnum_params.output_pdf_loc, quark_coeffs.valdn, 3, 0.0);

store spline addresses

QCDNUM.ssp_uwrite(splint_params.spline_addresses.F2up, Float64(iaF2up));
QCDNUM.ssp_uwrite(splint_params.spline_addresses.F2dn, Float64(iaF2dn));
QCDNUM.ssp_uwrite(splint_params.spline_addresses.F3up, Float64(iaF3up));
QCDNUM.ssp_uwrite(splint_params.spline_addresses.F3dn, Float64(iaF3dn));
QCDNUM.ssp_uwrite(splint_params.spline_addresses.FLup, Float64(iaFLup));
QCDNUM.ssp_uwrite(splint_params.spline_addresses.FLdn, Float64(iaFLdn));

my_funcp = get_input_xsec_func(1) # charge = 1
input_xsecp = @cfunction(my_funcp, Float64, (Ref{Int32}, Ref{Int32}, Ref{UInt8}))

my_funcm = get_input_xsec_func(-1) # charge = -1
input_xsecm = @cfunction(my_funcm, Float64, (Ref{Int32}, Ref{Int32}, Ref{UInt8}))
Ptr{Nothing} @0x00007f86cd1f7cd0

plot

g = qcdnum_params.grid_params
xsec_on_grid = zeros(g.nx, g.nq);

for ix = 1:g.nx
    for iq = 1:g.nq
        xsec_on_grid[ix, iq] = _fun_xsec_i(1, ix, iq) # charge = 1
    end
end

qcdnum_x_grid = QCDNUM.gxcopy(g.nx)
qcdnum_qq_grid = QCDNUM.gqcopy(g.nq)
p1 = heatmap(qcdnum_x_grid, qcdnum_qq_grid, NaNMath.log10.(xsec_on_grid[:, :]'))
plot(p1, xlabel="x", ylabel="q2",
    xaxis=:log, yaxis=:log)
plot(qcdnum_x_grid, NaNMath.log10.(xsec_on_grid[:, 4]),
    label="Q2=141 (input scale)", lw=3)
plot!(qcdnum_x_grid, NaNMath.log10.(xsec_on_grid[:, 22]), label="Q2=1152", lw=3)
plot!(qcdnum_x_grid, NaNMath.log10.(xsec_on_grid[:, 35]), label="Q2=5233", lw=3)
plot!(qcdnum_x_grid, NaNMath.log10.(xsec_on_grid[:, 41]), label="Q2=10523", lw=3)
plot!(xaxis=:log, legend=:bottomleft, xlabel="x",
    ylabel="log10(cross section spline input)", ylims=(-7, 5))
iaF_eP = QCDNUM.isp_s2make(1, 2);
QCDNUM.ssp_uwrite(splint_params.spline_addresses.F_eP, Float64(iaF_eP));
QCDNUM.ssp_s2fill(iaF_eP, input_xsecp, splint_params.rscut);

iaF_eM = QCDNUM.isp_s2make(1, 2);
QCDNUM.ssp_uwrite(splint_params.spline_addresses.F_eM, Float64(iaF_eM));
QCDNUM.ssp_s2fill(iaF_eM, input_xsecm, splint_params.rscut);

plot spline

spline = zeros(g.nx, g.nq);

for ix = 1:g.nx
    for iq = 1:g.nq
        spline[ix, iq] = QCDNUM.dsp_funs2(iaF_eP, qcdnum_x_grid[ix],
            qcdnum_qq_grid[iq], 1)
    end
end

p1 = heatmap(qcdnum_x_grid, qcdnum_qq_grid, NaNMath.log10.(spline[:, :]'))
plot(p1, xlabel="x", ylabel="q2",
    xaxis=:log, yaxis=:log)

Integrate over the cross section spline and find expected events numbers

Here, we neglect any possible contribution from systematic errors

nbins = size(xbins_M_begin)[1]
IntXsec_eP = zeros(nbins);
IntXsec_eM = zeros(nbins);
for i in 1:nbins
    IntXsec_eP[i] = QCDNUM.dsp_ints2(iaF_eP, xbins_M_begin[i], xbins_M_end[i],
        q2bins_M_begin[i], q2bins_M_end[i], 318.0, 4)
    IntXsec_eM[i] = QCDNUM.dsp_ints2(iaF_eM, xbins_M_begin[i], xbins_M_end[i],
        q2bins_M_begin[i], q2bins_M_end[i], 318.0, 4)
end

1 for e-p and 0 for e+p

ePp = 0;
eMp = 1;

TM_eP = get_TM_elements(ePp);
TM_eM = get_TM_elements(eMp);

K_eP = get_K_elements(ePp);
K_eM = get_K_elements(eMp);

nbins_out = size(TM_eP)[2];

counts_pred_eP = zeros(nbins_out);
counts_pred_eM = zeros(nbins_out);

for j in 1:nbins_out

    for i in 1:nbins

        counts_pred_eP[j] += TM_eP[i, j] * (1.0 / K_eP[i]) * IntXsec_eP[i]
        counts_pred_eM[j] += TM_eM[i, j] * (1.0 / K_eM[i]) * IntXsec_eM[i]

    end

end

counts_pred_eM
153-element Vector{Float64}:
 782.0747989157136
 543.341827637819
 407.6562006131687
 343.27554129757675
 221.58522672609953
 194.89598293573863
 140.65397196560687
 278.93744351583194
 853.6716286836335
 696.8839416798988
   ⋮
  13.826820303419813
   7.897518762639923
   4.452160806595315
   2.3743347283583263
   1.413392410229477
   0.9056333285465725
   0.09650371692146183
  52.806123532812435
   2.065405204640709

This page was generated using Literate.jl.