bachelor_thesis/prog/python/qqgg/tangled/pdf.py

189 lines
5.9 KiB
Python
Raw Normal View History

2020-04-18 20:00:18 +02:00
"""
Implementation of the analytical cross section for q q_bar ->
γγ in the lab frame.
Author: Valentin Boettcher <hiro@protagon.space>
"""
import numpy as np
import monte_carlo
import lhapdf
2020-04-19 17:34:00 +02:00
from numba import jit, vectorize, float64
2020-04-18 20:00:18 +02:00
2020-04-19 17:34:00 +02:00
@vectorize([float64(float64, float64, float64, float64)], nopython=True)
2020-04-18 20:00:18 +02:00
def energy_factor(e_proton, charge, x_1, x_2):
"""Calculates the factor common to all other values in this module.
:param e_proton: proton energy per beam
:param charge: charge of the quark
:param x_1: momentum fraction of the first quark
:param x_2: momentum fraction of the second quark
"""
return charge ** 4 / (137.036 * e_proton) ** 2 / (24 * x_1 * x_2)
2020-04-28 15:18:35 +02:00
def momenta(e_proton, x_1, x_2, cosθ, φ=None):
2020-04-18 20:00:18 +02:00
"""Given the Energy of the incoming protons `e_proton` and the
momentum fractions `x_1` and `x_2` as well as the cosine of the
azimuth angle of the first photon the 4-momenta of all particles
are calculated.
"""
x_1 = np.asarray(x_1)
x_2 = np.asarray(x_2)
cosθ = np.asarray(cosθ)
2020-04-27 09:19:56 +02:00
2020-04-28 15:18:35 +02:00
if φ is None:
φ = 0
cosφ = np.ones_like(cosθ)
sinφ = 0
else:
if φ == "rand":
φ = np.random.uniform(0, 2 * np.pi, cosθ.shape)
else:
φ = np.asarray(φ)
sinφ = np.sin(φ)
cosφ = np.cos(φ)
assert (
x_1.shape == x_2.shape == cosθ.shape
), "Invalid shapes for the event parameters."
2020-04-28 15:18:35 +02:00
sinθ = np.sqrt(1 - cosθ ** 2)
2020-04-27 09:19:56 +02:00
ones = np.ones_like(cosθ)
zeros = np.zeros_like(cosθ)
q_1 = e_proton * x_1 * np.array([ones, zeros, zeros, ones,])
2020-04-28 15:18:35 +02:00
q_2 = e_proton * x_2 * np.array([ones, zeros, zeros, -ones,])
g_3 = (
2020-04-28 15:18:35 +02:00
2
* e_proton
* x_1
* x_2
/ (x_1 + x_2 - (x_1 - x_2) * cosθ)
* np.array([1 * np.ones_like(cosθ), sinθ * sinφ, cosφ * sinθ, cosθ])
2020-04-18 20:00:18 +02:00
)
g_4 = q_1 + q_2 - g_3
q_1 = q_1.reshape(4, cosθ.size).T
q_2 = q_2.reshape(4, cosθ.size).T
g_3 = g_3.reshape(4, cosθ.size).T
g_4 = g_4.reshape(4, cosθ.size).T
return np.array([q_1, q_2, g_3, g_4])
2020-04-18 20:00:18 +02:00
2020-04-19 17:34:00 +02:00
@vectorize([float64(float64, float64, float64, float64, float64)], nopython=True)
def diff_xs_η(e_proton, charge, η, x_1, x_2):
"""Calculates the differential cross section as a function of the
cosine of the pseudo rapidity η of one photon in units of 1/GeV².
Here =dηdφ
:param e_proton: proton energy per beam [GeV]
:param charge: charge of the quark
:param x_1: momentum fraction of the first quark
:param x_2: momentum fraction of the second quark
:param η: pseudo rapidity
:return: the differential cross section [GeV^{-2}]
"""
2020-04-27 09:19:56 +02:00
rap = np.arctanh((x_1 - x_2) / (x_1 + x_2))
f = energy_factor(e_proton, charge, x_1, x_2)
2020-04-27 09:19:56 +02:00
return f * ((np.tanh(η - rap)) ** 2 + 1)
2020-04-18 20:00:18 +02:00
2020-04-19 17:34:00 +02:00
@vectorize([float64(float64, float64, float64)], nopython=True)
def averaged_tchanel_q2(e_proton, x_1, x_2):
return 2 * x_1 * x_2 * e_proton ** 2
2020-04-26 18:07:08 +02:00
def cut_pT_from_eta(greater_than=0):
def cut(e_proton, η, x1, x2):
cosθ = np.cos(η_to_θ(η))
_, _, p1, p2 = momenta(e_proton, x1, x2, cosθ)
return (
np.sqrt((p1[0][1:3] ** 2).sum()) > greater_than
and np.sqrt((p2[0][1:3] ** 2).sum()) > greater_than
)
return cut
2020-04-19 17:34:00 +02:00
from numba.extending import get_cython_function_address
2020-04-26 18:07:08 +02:00
def get_xs_distribution_with_pdf(xs, q, e_hadron, quarks=None, pdf=None, cut=None):
2020-04-18 20:00:18 +02:00
"""Creates a function that takes an event (type np.ndarray) of the
2020-04-28 15:18:35 +02:00
form [angle_arg, impulse fractions of quarks in hadron 1, impulse
fractions of quarks in hadron 2] and returns the differential
cross section for such an event. I would have used an object as
argument, wasn't for the sampling function that needs a vector
2020-04-28 15:18:35 +02:00
valued function. Angle_Arg can actually be any angular-like parameter
as long as the xs has the corresponding parameter.
2020-04-18 20:00:18 +02:00
2020-04-28 15:18:35 +02:00
:param xs: cross section function with signature (energy hadron, angle_arg, x_1, x_2)
2020-04-18 20:00:18 +02:00
:param q2: the momentum transfer Q^2 as a function with the signature
2020-04-19 17:34:00 +02:00
(e_hadron, x_1, x_2)
2020-04-18 20:00:18 +02:00
:param quarks: the constituent quarks np.ndarray of the form [[id, charge], ...],
the default is a proton
:param pdf: the PDF to use, the default is "NNPDF31_lo_as_0118"
2020-04-28 15:18:35 +02:00
:param cut: cut function with signature (energy hadron, angle_arg, x_1,
2020-04-26 18:07:08 +02:00
x_2) to return 0, when the event does not fit the cut
2020-04-18 20:00:18 +02:00
:returns: differential cross section summed over flavors and weighted with the pdfs
:rtype: function
"""
pdf = pdf or lhapdf.mkPDF("NNPDF31_lo_as_0118", 0)
2020-04-28 15:18:35 +02:00
quarks = quarks or np.array(
[[5, -1 / 3], [4, 2 / 3], [3, -1 / 3], [2, 2 / 3], [1, -1 / 3]]
) # proton
2020-04-18 20:00:18 +02:00
supported_quarks = pdf.flavors()
for flavor in quarks[:, 0]:
assert flavor in supported_quarks, (
"The PDF doesn't support the quark flavor " + flavor
)
2020-04-19 17:34:00 +02:00
xfxQ2 = pdf.xfxQ2
# @jit(float64(float64[4])) Unfortunately that does not work as yet!
2020-04-18 20:00:18 +02:00
def distribution(event: np.ndarray) -> float:
2020-04-26 18:07:08 +02:00
if cut and not cut(e_hadron, *event):
return 0
2020-04-28 15:18:35 +02:00
angle_arg, x_1, x_2 = event
2020-04-18 20:00:18 +02:00
2020-04-19 17:34:00 +02:00
q2_value = q(e_hadron, x_1, x_2)
2020-04-18 20:00:18 +02:00
result = 0
for quark, charge in quarks:
2020-04-28 15:18:35 +02:00
xs_value = xs(e_hadron, charge, angle_arg, x_1, x_2)
2020-04-18 20:00:18 +02:00
result += (
(xfxQ2(quark, x_1, q2_value) + xfxQ2(-quark, x_1, q2_value))
2020-04-28 15:18:35 +02:00
/ x_1
* (xfxQ2(-quark, x_2, q2_value) + xfxQ2(quark, x_2, q2_value))
/ x_2
2020-04-18 20:00:18 +02:00
* xs_value
)
return result
return distribution, (pdf.xMin, pdf.xMax)
2020-04-24 15:01:39 +02:00
def sample_momenta(num_samples, dist, interval, e_hadron, upper_bound=None, **kwargs):
res, eff = monte_carlo.sample_unweighted_array(
2020-04-24 15:01:39 +02:00
num_samples,
dist,
interval,
upper_bound=upper_bound,
report_efficiency=True,
**kwargs
)
cosθ, x_1, x_2 = res.T
return momenta(e_hadron, x_1[None, :], x_2[None, :], cosθ[None, :]), eff