6. X-rays, cosmic muons and gaseous detectors    Theory

As we have seen in the previous two chapters, solar axions should produce photons in the soft X-ray energy range. In experiments like CAST, gaseous detectors – like the one used in this thesis – are therefore a common and suitable choice for the detection of the X-rays focused by the X-ray optics. In order to give a guiding reference for the relevant physics associated with the detection of particles at CAST, we will now cover each aspect that we make use of in explanations or calculations. The focus will be on aspects either not commonly discussed (for example depth graded multilayers for X-ray telescopes) or essential for explanation (gas diffusion).

We start with a section on particle interactions with matter, sec. 6.1, where we discuss X-ray interactions as well as charged particles in gases. Next in sec. 6.2 follows a short section about cosmic radiation, in particular the expected muon flux as it serves as a dominant source of background in experiments like CAST. Finally, in sec. 6.3 we cover concepts of gaseous detector physics that we depend on.

6.1. Particle interactions with matter

On the one hand we will discuss how X-rays interact with matter. Both in terms of solids as well as gases, focused on their attenuation, because this is required to describe signal attenuation due to – for example – a detector window of a gaseous detector or the absorption of X-rays in the detector gas. In addition, X-ray reflectivity will be discussed as it is of interest for the behavior of X-ray telescopes. On the other hand the interaction of highly energetic charged particles with matter will be discussed and its relation to cosmic radiation as a source of background. Finally, X-ray fluorescence will be covered as well. As a source of background in an axion helioscope experiment it is indistinguishable 1 from axion-induced X-rays.

For a detailed overview of the interaction of X-rays with matter, see the X-ray data booklet (Williams 2001).

6.1.1. X-rays in solid matter & gases

Lambert-Beer's law (Bouguer 1729; Lambert 1760; Beer 1852)

\begin{equation} \label{eq:theory:beer_lambert_law} I(z) = I_0 e^{-μz}, \end{equation}

gives the intensity of radiation \(I(z)\) after traversing through a medium with constant attenuation \(μ\) of length \(z\), given a starting intensity of \(I_0\). Directly related is of course the absorption length \(l_{\text{abs}} = 1/μ\) (or mean free path), which is a useful property when considering typical absorption depths.

This law is of vital importance for the behavior of X-rays traversing through matter, which is needed to compute the efficiency of a gaseous detector with an entrance window. In addition, it is also related to the mean free path of X-rays in a gas, which is an important parameter in gaseous detectors to understand the absorption efficiency of X-rays at different energies and the resulting expected diffusion.

In the context of X-rays the factor \(μ\) is typically rewritten via the 'mass attenuation coefficient' \(μ_m = μ · ρ\) with \(ρ\) the density of the material, commonly in \(\si{g.cm^{-3}}\). \(μ_m\) is then defined by

\[ μ_m = \frac{N_A}{M} σ_A, \]

where \(N_A\) is Avogadro's number, \(M\) the molar mass of the medium in units of \(\si{g\per\mol}\) and \(σ_A\) is the photoabsorption cross section in units of \(\si{cm^2}\). Thus, the mass attenuation coefficient is usually given in \(\si{cm^2.g^{-1}}\) such that \(μ = μ_m · ρ\) is of inverse length as expected. This directly yields the definition of the absorption length,

\[ l_{\text{abs}} = \frac{1}{μ}. \]

Further, the photoabsorption cross section can be described via the atomic scattering factor \(f₂\)

\[ σ_A = 2 r_e λ f₂, \]

where \(r_e\) is the classical electron radius and \(λ\) the wavelength of the X-ray. \(f₂\) is the imaginary part of the forward scattering factor \(f\)

\[ f = f₁ - i f₂ \]

which itself is the simplification of the general atomic scattering factor that describes the atom specific part of the scattering cross section.

This way of expressing it has the nice property of relying on a well tabulated parameter \(f₂\). Together with \(f₁\) these tabulated values can be used to compute everything from the refractive index at a specific X-ray energy of a compound to the attenuation coefficient and even reflectivity of a multi-layer substrate. It generalizes from single element to compounds easily by

\[ μ_m = \frac{N_A}{M_c} \sum_i n_i σ_{A,i}, \]

with \(M_c\) the molar weight of the compound and \(n_i\) the number of atoms of kind \(i\). X-ray absorption and transmission properties can be calculated from this only requiring the atomic scattering factors, which can be found tabulated for different elements, for example by NIST and Henke.

There is an online calculator for calculations of X-ray transmission found under 2 (Henke, Gullikson, and Davis 1993), as well as a library implementation developed during the course of this thesis under 3 (Schmidt and SciNim contributors 2022) for this purpose.

Fig. 1 shows an example of X-ray transmission through a \(\SI{300}{nm}\) thick layer of \ccsini as well as transmission through \(\SI{3}{cm}\) of argon at normal temperature and pressure (NTP), \(\SI{1}{atm}\), \(\SI{20}{°C}\). All information about the absorption lines and general transmission is encoded in \(f₂\).

transmission_example.svg
Figure 1: Figure 8: X-ray transmission through a \SI{300}{nm} thick layer of \ccsini and \SI{3}{cm} of argon calculated with Schmidt_xrayAttenuation_2022. Calculation of the transmission based on tabulated scattering form factors.
6.1.1.1. Absorption length of Argon    extended

Fig. 3 also shows the absorption length for argon at NTP.

absorption_length_example.svg
Figure 3: Figure 9: Absorption length of \SI{3}{cm} of argon calculated with Schmidt_xrayAttenuation_2022. Calculation based on tabulated scattering form factors.
6.1.1.2. Generation of \ccsini transmission figure    extended

Let's compute an example transmission plot using the Lambert-Beer law as presented above based on xrayAttenuation now, on the one hand for \ccsini as well as argon (common detector gas).

TODO: update ginger to use -output-directory to put the plot in the right path & turn it into a TikZ plot.

import std / strutils
import xrayAttenuation, ggplotnim
# generate a compound of silicon and nitrogen with correct number of atoms
let Si₃N₄ = compound((Si, 3), (N, 4))
# instantiate an Argon instance
let ar = Argon.init()
# compute the density using ideal gas law at 1 atm
let ρ_Ar = density(1013.25.mbar.to(Pascal), 293.15.K, ar.molarMass)

# define energies in which to compute the transmission
# (we don't start at 0, as at 0 energy the parameters are not well defined)
let energies = linspace(1e-2, 10.0, 1000)
proc compTrans[T: AnyCompound](el: T, ρ: g•cm⁻³, length: Meter): DataFrame =
  result = toDf({ "Energy [keV]" : energies })
    .mutate(f{float: "μ" ~ el.attenuationCoefficient(idx("Energy [keV]").keV).float},
            f{float: "Trans" ~ transmission(`μ`.cm²•g⁻¹, ρ, length).float},
            f{float: "l_abs" ~ absorptionLength(el, ρ, idx("Energy [keV]").keV).to(cm).float},
            f{"Compound" <- el.name})
var df = newDataFrame()
# compute transmission for Si₃N₄ (known density and desired length)
df.add Si₃N₄.compTrans(3.44.g•cm⁻³, 300.nm.to(Meter))
# and for argon 
df.add ar.compTrans(ρ_Ar, 3.cm.to(Meter))
# create a plot for the transmissions
echo df
let dS = pretty(300.nm, 3, short = true)
let dA = pretty(3.cm, 1, short = true)
let si = r"$\mathrm{Si}₃\mathrm{N}₄$"
ggplot(df, aes("Energy [keV]", "Trans", color = "Compound")) +
  geom_line() +
  xlab(r"Energy [$\si{keV}$]") + ylab("Transmission") +
  ggtitle("Transmission examples of $# $# and $# Argon at NTP" % [dS, si, dA]) +
  themeLatex(fWidth = 0.9, width = 600, baseTheme = singlePlot) +
  ggsave("/home/basti/phd/Figs/theory/transmission_example.pdf",
         useTex = true, standalone = true, width = 600, height = 360)

let dff = df.filter(f{float -> bool: classify(`l_abs`) != fcInf},
                    f{`Compound` == "Argon"})
echo dff
ggplot(dff, aes("Energy [keV]", "l_abs")) +
  geom_line() +
  xlab(r"Energy [$\si{keV}$]") + ylab(r"Absorption length [$\si{cm}$]") +
  ggtitle("Absorption length of $# Argon at NTP" % [dA]) +
  themeLatex(fWidth = 0.9, width = 600, baseTheme = singlePlot) +  
  ggsave("/home/basti/phd/Figs/theory/absorption_length_example.pdf",
         useTex = true, standalone = true, width = 600, height = 360)
  

The absorption length of pure argon at NTP is seen in fig. 3.

absorption_length_example.svg
Figure 3: Figure 10: Absorption length of argon at NTP. The absorption edge and long absorption length around the Kα line is clearly visible showing the reason for the escape peak in the \cefe spectrum.

6.1.2. X-ray reflectivity & scattering

The same atomic scattering factors \(f₁\) and \(f₂\) introduced in section 6.1.1 for the attenuation can also be used to compute the reflectivity of X-rays under shallow angles. A great overview of the relevant physics for X-ray reflectivity is found in (Windt 1998), which introduces the IMD program for the simulation of multilayer coatings for X-rays.

By defining the combined scattering factor

\[ f(E) = f₁(E) + i f₂(E) \]

at energy \(E\), the refractive index \(n\) of a medium can be computed using

\[ n(E) = 1 - r_e \frac{λ²}{2π} \sum_i n_{ai} f_i(E) \]

where \(n_{ai}\) is the number density of the \(i\text{-th}\) compound of the medium. By expressing the refractive index for X-rays in this fashion, reflectivity can be expressed using the Fresnel equations, just like for visible light. The reflectivity for s-polarization is calculated by

\begin{equation} \label{eq:theory:fresnell_reflectance_s} r^s_{ik} = \frac{n_i · \cos(θ_i) - n_k · \cos(θ_k)}{n_i · \cos(θ_i) + n_k · \cos(θ_k)} \end{equation}

while for p-polarization it is done via

\begin{equation} \label{eq:theory:fresnell_reflectance_p} r^p_{ik} = \frac{n_i · \cos(θ_k) - n_k · \cos(θ_i)}{n_i · \cos(θ_k) + n_k · \cos(θ_i)}. \end{equation}

Here \(θ_i\), \(θ_k\) are the incident and refracted angles and \(n_i\), \(n_k\) the refractive indices on the incident and outgoing side \(i\) and \(k\), respectively.

The total reflected energy, the reflectance \(R\), is expressed as

\[ R = \frac{1}{2}\left( \left|r^s\right|² + \left|r^p\right|²\right) \]

for unpolarized light. This can be generalized to multiple layers of material on a substrate and including a surface roughness. Combined, these provide the essence for a realistic computation of the efficiency of an X-ray telescope mirror shell.

This is also implemented in 3 (Schmidt and SciNim contributors 2022) and 2 (Henke, Gullikson, and Davis 1993) also provides an online calculator for such reflectivities.

Depth graded multilayers

One particular kind of surface, the depth graded multilayer, is used in certain kinds of modern X-ray telescopes, for example the LLNL telescope at CAST following the NuSTAR design. In such a multilayer, repeating layers of a low atomic number \(Z\) material and a high \(Z\) material are stacked at decreasing thicknesses.

A depth-graded multilayer is described by the equation: \[ d_i = \frac{a}{(b + i)^c} \] where \(d_i\) is the depth of layer \(i\) (out of \(N\) layers), \[ a = d_{\text{min}} (b + N)^c \] and \[ b = \frac{1 - N k}{k - 1} \] with \[ k = \left(\frac{d_{\text{min}}}{d_{\text{max}}}\right)^{\frac{1}{c}} \] where \(d_{\text{min}}\) and \(d_{\text{max}}\) are the thickness of the bottom and top most layers, respectively.

For example for the the LLNL telescope a Pt/C depth graded multilayer is used, in which a top layer of carbon is stacked on top of a platinum layer. Between 2 to 5 repetitions of decreasing thickness are stacked with a ratio of \(\SIrange{40}{45}{\%}\) carbon to platinum in thickness. More details on this will be discussed in appendix 37, as it is of vital importance to calculate the axion image required for the limit calculation correctly.

The reflectivity for a depth-graded multilayer is computed recursively from the bottom of the stack to the top layer using

\[ r_i = \frac{r_{ik} + r_k \exp(2 i β_i)}{1 + r_{ik} r_k \exp(2 i β_i)} \]

with

\[ β_i = 2π · d_i · \frac{\cos(θ_i)}{λ}, \]

where \(θ_i\) as seen from the normal axis and with the wavelength of the incoming X-rays \(λ\). The \(r_{ik}\) values are computed following equations \eqref{eq:theory:fresnell_reflectance_s} and \eqref{eq:theory:fresnell_reflectance_p}. Such multilayers work by summing the reflecting contributions from the different layer transitions. Different thicknesses of the different multilayers mean X-rays at different energies and angles are best reflected from different layers. Thus, a much improved overall reflectivity over a wider energy and angle range can be achieved compared to a normal single layer on a substrate (e.g. a gold coating as used for the XMM-Newton or ABRIXAS optics).

6.1.3. Bethe-Bloch equation

Another relevant aspect for gaseous detectors is the energy deposition of charged particles. In particular for experiments that sit near Earth's surface, a major source of background is due to cosmic radiation, with cosmic muons making up more than \(\SI{95}{\%}\) (Zyla and others 2020) of radiation (aside from neutrinos) at the surface, see sec. 6.2.

The energy loss of such muons can be calculated with the Bethe-Bloch equation, which describes the average energy loss per distance for a charged particle with charge \(z\) in a homogeneous medium with charge carriers \(Z\). (Zyla and others 2020) 4

\begin{equation} \label{eq:theory:bethe_bloch_eq} \left⟨ -\frac{\mathrm{d}E}{\mathrm{d}x} \right⟩ = K z² \frac{Z}{A} \frac{1}{β²} \left[ \frac{1}{2} \ln\frac{2m_e c² β² γ² W_{\text{max}}}{I²} - β² - \frac{δ(βγ)}{2} \right] \end{equation}

where the different variables are as follows:

  • \(K = 4π N_A r_e² m_e c² = \SI{0.307075}{MeV.mol^{-1}.cm^2}\)
  • \(W_{\text{max}}\): maximum possible energy transfer to an electron in a single interaction
  • \(I\): mean excitation energy of the absorber material in \si{\eV}
  • \(δ(βγ)\): density-effect correction to energy loss
  • and \(r_e = \frac{e²}{4π ε_0 m_e c²}\) the classical electron radius, \(N_A\) Avogadro's number, \(m_e\) electron mass, \(c\) speed of light in vacuum, \(z\) charge number of incident particle, \(Z\) atomic number of absorber material, \(A\) atomic mass of absorber material, \(β = v/c\) speed of incident particle, \(γ\) Lorentz factor

This interaction behavior of muons leads to a specific, expected energy loss per distance. Commonly they are 'minimum ionizing particles' (MIPs), as their energies lies between \(\SIrange{0.1}{100}{GeV}\), the large 'valley' in which the Bethe equation is applicable 5. For argon gas at normal conditions this is shown in fig. 4.

As the Bethe formula was derived from quantum mechanical perturbation theory, higher order corrections can be computed. For our purposes here the leading order is enough. It is important to keep in mind that the Bethe-Bloch equation gives the mean energy per distance. When considering short distances as typically encountered in particle detectors, this mean is skewed by rare interactions that deposit large amounts of energy (towards \(W_{\text{max}}\)). The energy deposition along short distances is typically described by a Landau-Vavilov distribution (similar, but different from a normal Landau distribution) (Zyla and others 2020; Bichsel 2006). The most probable energy loss is often a more appropriate number to look at. It can be expressed as

\begin{equation} \label{eq:theory:most_probable_loss} Δ_p = ξ \left[ \ln{ \frac{2 m_e c² β² γ²}{I}} + \ln{\frac{ξ}{I}} + j - β² - δ(βγ) \right], \end{equation}

where \(ξ\) is

\[ ξ = \frac{1}{2} K z² \left⟨ \frac{Z}{A} \right⟩ \frac{x}{β²} \, \si{MeV}, \]

for a detector in which the material column the particle travels through is expressed as \(x = d · ρ\) of a distance \(d\) in \(\si{g.cm^{-2}}\). \(j = \num{0.200}\) is an empirical constant (Zyla and others 2020; Bichsel 1988). Further, \(⟨Z / A⟩\) is simply the average \(Z/A\) for a material compound \(⟨Z/A⟩ = \sum_i w_i Z_i / A_i\).

The large difference typically encountered between the most probable and the mean value for the energy loss in particle detectors, makes studying the expected signals a complicated topic. For a detailed description relevant for thin gaseous detectors, see especially (Bichsel 2006).

Fig. 4 shows the comparison of the most probable energy loss via equation \eqref{eq:theory:most_probable_loss} and the mean energy loss via the Bethe-Bloch equation \eqref{eq:theory:bethe_bloch_eq} for muons of different energies traversing \(\SI{3}{cm}\) of argon gas.

ar_energy_loss_cast.svg
Figure 4: Figure 11: Mean energy loss via Bethe-Bloch (purple) equation of muons in \SI{3}{\cm} of argon at conditions in use in GridPix detector at CAST. \SI{1050}{mbar} of chamber pressure at room temperature. Note that the mean is skewed by events that transfer a large amount of energy, but are very rare! As such care must be taken interpreting the numbers. Green shows the most probable energy loss, based on the peak of the Landau-Vavilov distribution underlying the Bethe-Bloch mean value.
6.1.3.1. Bethe equation for muons traversing \(\SI{3}{\cm}\) of argon gas    extended

We will now compute the energy loss for muons traversing the \SI{3}{\cm} of argon gas that are seen by a muon traversing orthogonally to the readout plane (i.e. such that it may look like a photon).

import math, macros, unchained, ggplotnim, sequtils, strformat, strutils
import thesisHelpers
import ggplotnim / ggplot_vegatex

let K = 4 * π * N_A * r_e^2 * m_e * c^2 # usually in: [MeV mol⁻¹ cm²]

defUnit(cm³•g⁻¹)
defUnit(J•m⁻¹)
defUnit(cm⁻³)
defUnit(g•mol⁻¹)
defUnit(MeV•g⁻¹•cm²)
defUnit(mol⁻¹)
defUnit(keV•cm⁻¹)
defUnit(g•cm⁻³)
defUnit(g•cm⁻²)

proc I[T](z: float): T =
  ## use Bloch approximation for all but Argon (better use tabulated values!)
  result = if z == 18.0: 188.0.eV.to(T) 
           else: (10.eV * z).to(T)

proc calcβ(γ: UnitLess): UnitLess =
  result = sqrt(1.0 - 1.0 / (γ^2))

proc betheBloch(z, Z: UnitLess, A: g•mol⁻¹, γ: UnitLess, M: kg): MeV•g⁻¹•cm² =
  ## result in MeV cm² g⁻¹ (normalized by density)
  ## z: charge of particle
  ## Z: charge of particles making up medium
  ## A: atomic mass of particles making up medium
  ## γ: Lorentz factor of particle
  ## M: mass of particle in MeV (or same mass as `m_e` defined as)
  let β = calcβ(γ)
  let W_max = 2 * m_e * c^2 * β^2 * γ^2 / (1 + 2 * γ * m_e / M + (m_e / M)^2)
  let lnArg = 2 * m_e * c^2 * β^2 * γ^2 * W_max / (I[Joule](Z)^2)
  result = (K * z^2 * Z / A * 1.0 / (β^2) * (
   0.5 * ln(lnArg) - β^2
  )).to(MeV•g⁻¹•cm²)

proc mostProbableLoss(z, Z: UnitLess, A: g•mol⁻¹, γ: UnitLess,
                      x: g•cm⁻²): keV =
  ## Computes the most probable value, corresponding to the peak of the Landau
  ## distribution, that gives rise to the Bethe-Bloch formula.
  ##
  ## Taken from PDG chapter 'Passage of particles through matter' equation
  ## `34.12` in 'Fluctuations in energy loss', version 2020).
  ##
  ## `x` is the "thickness". Density times length, `x = ρ * d`. The other parameters
  ## are as in `betheBloch` above.
  let β = calcβ(γ)
  let ξ = K / 2.0 * Z / A * z*z * (x / (β*β))
  const j = 0.200
  let I = I[Joule](Z)
  result = (ξ * ( ln((2 * m_e * c^2 * β^2 * γ^2).to(Joule) / I) + ln(ξ.to(Joule) / I) + j - β^2)).to(keV) # - δ*(β*γ)

proc density(p: mbar, M: g•mol⁻¹, temp: Kelvin): g•cm⁻³ =
  ## returns the density of the gas for the given pressure.
  ## The pressure is assumed in `mbar` and the temperature (in `K`).
  ## The default temperature corresponds to BabyIAXO aim.
  ## Returns the density in `g / cm^3`
  let gasConstant = 8.314.J•K⁻¹•mol⁻¹ # joule K^-1 mol^-1
  let pressure = p.to(Pa) # pressure in Pa
  result = (pressure * M / (gasConstant * temp)).to(g•cm⁻³)

proc E_to_γ(E: GeV): UnitLess =
  result = E.to(Joule) / (m_μ * c^2) + 1

type
  Element = object
    name: string
    Z: UnitLess
    M: g•mol⁻¹
    A: UnitLess # numerically same as `M`
    ρ: g•cm⁻³

proc initElement(name: string, Z: UnitLess, M: g•mol⁻¹, ρ: g•cm⁻³): Element =
  Element(name: name, Z: Z, M: M, A: M.UnitLess, ρ: ρ)

let M_Ar = 39.95.g•mol⁻¹ # molar mass. Numerically same as relative atomic mass
#let ρAr = density(1050.mbar, M_Ar, temp = 293.15.K)
let ρAr = density(1013.mbar, M_Ar, temp = 293.15.K)
let Argon = initElement("ar", 18.0.UnitLess, 39.95.g•mol⁻¹, ρAr)

proc intBethe(e: Element, d_total: cm, E0: eV, dx = 1.μm): eV =
  ## integrated energy loss of bethe formula after `d` cm of matter
  ## and returns the energy remaining
  var γ: UnitLess = E_to_γ(E0.to(GeV))
  var d: cm
  result = E0
  var totalLoss = 0.eV
  while d < d_total and result > 0.eV:
    let E_loss: MeV = betheBloch(-1, e.Z, e.M, γ, m_μ) * e.ρ * dx
    result = result - E_loss.to(eV)
    γ = E_to_γ(result.to(GeV))
    d = d + dx.to(cm)
    totalLoss = totalLoss + E_loss.to(eV)
  result = max(0.float, result.float).eV

func argonLabel(): string = "fig:theory:muon_argon_3cm_bethe_loss"

## TODO: add in the most probable value calc!  
func argonCaption(): string = 
  result = r"Mean energy loss via Bethe-Bloch (purple) equation of muons in \SI{3}{\cm} of argon at " &
    r"conditions in use in GridPix detector at CAST. \SI{1050}{mbar} of chamber pressure at room " &
    r"temperature. Note that the mean is skewed by events that transfer a large amount of energy, " &
    r"but are very rare! As such care must be taken interpreting the numbers. Green shows the most " &
    r"probable energy loss, based on the peak of the Landau-Vavilov distribution underlying the " &
    r"Bethe-Bloch mean value." &
    interactiveVega(argonLabel())

proc plotDetectorAbsorption(element: Element) =
  let E_float = logspace(-2, 2, 1000)
  let energies = E_float.mapIt(it.GeV)
  let E_loss = energies.mapIt((it.to(eV) - intBethe(element, 3.cm, it.to(eV))).to(keV).float)
  let E_lossMP = energies.mapIt(mostProbableLoss(-1, element.Z, element.M, E_to_γ(it), ρ_Ar * 3.cm).float)
  let df = seqsToDf({E_float, "Bethe-Bloch (BB)" : E_loss, "Most probable (MP)" : E_lossMP})
    .gather(["Bethe-Bloch (BB)", "Most probable (MP)"], "Type", "Value")
  ggplot(df, aes("E_float", "Value", color = "Type")) +
    geom_line() +
    #xlab(r"μ Energy [\si{\GeV}]") + ylab(r"$-\left\langle \frac{\mathrm{d}E}{\mathrm{d}x}\right\rangle$ [\si{\keV}]") +
    xlab(r"μ Energy [\si{\GeV}]") +
    ylab(r"$-\left\langle \frac{\mathrm{d}E}{\mathrm{d}x}\right\rangle$ (BB), $Δ_p$ (MP) [\si{\keV}]") +
    scale_x_log10() + scale_y_log10() +
    themeLatex(fWidth = 0.9, width = 600, baseTheme = singlePlot) +
    margin(right = 6) +
    ggtitle(r"Energy loss of Muons in \SI{3}{\cm} " & &"{element.name.capitalizeAscii} at CAST conditions") +
    ggsave(&"/home/basti/phd/Figs/muonStudies/{element.name}_energy_loss_cast.pdf", useTeX = true, standalone = true)
    #ggvegatex(&"/home/basti/phd/Figs/muonStudies/{element.name}_energy_loss_cast",
    #          onlyTikZ = false,
    #          caption = argonCaption(),
    #          label = argonLabel(),
    #          width = 600, height = 360)
plotDetectorAbsorption(Argon)

proc plotMostProbable(e: Element) =
  let E_float = logspace(-1.5, 2, 1000)
  let energies = E_float.mapIt(it.GeV)
  let E_loss = energies.mapIt(mostProbableLoss(-1, e.Z, e.M, E_to_γ(it), ρ_Ar * 3.cm))
  let df = toDf({"E_loss" : E_loss.mapIt(it.float), E_float})
  ggplot(df, aes("E_float", "E_loss")) +
    geom_line() +
    scale_x_log10() + 
    xlab("Energy [GeV]") + ylab("Most probable loss [keV]") +
    ggsave("/tmp/most_probable_loss.pdf")
plotMostProbable(Argon)

6.1.4. X-ray fluorescence

Cosmic muons in their interactions with matter can ionize atoms, leading to the possible emission of X-rays if the removed electron is part of an inner shell, mostly K (and some L) shell electrons. This leads to a form of background based on real X-rays and thus represents a kind of background that is impossible to distinguish from any kind of axion signal unless external scintillator based vetoes are used. Of course, to be relevant as a form of detector background the material must be close to the detector, as the X-rays will otherwise be absorbed. This makes the detector material, the gas itself and all material in the direction of the detectors' sensitivity a candidate for X-ray fluorescence background.

Table 1 lists the X-ray fluorescence lines for elements one commonly encounters in the context of gaseous detectors for a helioscope experiment. Table 2 lists the atomic binding energies of the same elements. This is intended as a useful reference to understand possible lines in background, potential targets for an X-ray tube for reference X-ray data and understand the absorption edges for materials.

The difference between the binding energy and the energies of the most likely fluorescence lines is the explanation for why commonly a material is more transparent for a fluorescence X-ray than energies slightly above it. This (among other effects) explains the 'escape peak' seen in many types of gaseous detectors. For example see fig. 1 for the argon transmission. The argon \(K 1s\) binding energy of \(\SI{3.2}{keV}\) is visible in the form of the sudden drop in transmission (which is of course directly proportional to the absorption length!). At the same time an X-ray produced by an ionized argon atom from the \(K 1s\) electron via the Kα line yields an X-ray of only \(\SI{2.95}{keV}\) and thus argon gas is extremely transparent for such X-rays (this is the cause of so called 'escape photons', see sec. 6.3.8).

Table 1: Excerpt of X-ray fluorescence energies of K, L and M emission lines for different elements mostly relevant for the context of this thesis in in \si{eV}. Taken from the X-ray data book williams2001x, specifically https://xdb.lbl.gov/Section1/Table_1-2.pdf.
Z Element Kα1 Kα2 Kβ1 Lα1 Lα2 Lβ1 Lβ2 Lγ1 Mα1
6 C 277                
7 N 392.4                
8 O 524.9                
13 Al 1,486.70 1,486.27 1,557.45            
14 Si 1,739.98 1,739.38 1,835.94            
18 Ar 2,957.70 2,955.63 3,190.5            
22 Ti 4,510.84 4,504.86 4,931.81 452.2 452.2 458.4      
25 Mn 5,898.75 5,887.65 6,490.45 637.4 637.4 648.8      
26 Fe 6,403.84 6,390.84 7,057.98 705.0 705.0 718.5      
28 Ni 7,478.15 7,460.89 8,264.66 851.5 851.5 868.8      
29 Cu 8,047.78 8,027.83 8,905.29 929.7 929.7 949.8      
47 Ag 22,162.92 21,990.3 24,942.4 2,984.31 2,978.21 3,150.94 3,347.81 3,519.59  
54 Xe 29,779 29,458 33,624 4,109.9    
78 Pt 66,832 65,112 75,748 9,442.3 9,361.8 11,070.7 11,250.5 12,942.0 2,050.5
79 Au 68,803.7 66,989.5 77,984 9,713.3 9,628.0 11,442.3 11,584.7 13,381.7 2,122.9
82 Pb 74,969.4 72,804.2 84,936 10,551.5 10,449.5 12,613.7 12,622.6 14,764.4 2,345.5
Table 2: Excerpt of electron binding energies of elements mostly relevant for the context of this thesis in \si{eV}. Taken from the X-ray data book williams2001x, specifically https://xdb.lbl.gov/Section1/Table_1-1.pdf.
Z Element K 1s L1 2s L2 2p1/2 L3 2p3/2 M1 3s M2 3p1/2 M3 3p3/2 M4 3d3/2 M5 3d5/2  
6 C 284.2                  
7 N 409.9 37.3                
8 O 543.1 41.6                
13 Al 1559.6 117.8 72.95 72.55            
14 Si 1839 149.7 99.82 99.42            
18 Ar 3205.9 326.3 250.6 248.4 29.3 15.9 15.7      
22 Ti 4966 560.9 460.2 453.8 58.7 32.6 32.6      
25 Mn 6539 769.1 649.9 638.7 82.3 47.2 47.2      
26 Fe 7112 844.6 719.9 706.8 91.3 52.7 52.7      
28 Ni 8333 1008.6 870.0 852.7 110.8 68.0 66.2      
29 Cu 8979 1096.7 952.3 932.7 122.5 77.3 75.1      
47 Ag 25514 3806 3524 3351 719.0 603.8 573.0 374.0 368.3  
54 Xe 34561 5453 5107 4786 1148.7 1002.1 940.6 689.0 676.4  
78 Pt 78395 13880 13273 11564 3296 3027 2645 2202 2122  
79 Au 80725 14353 13734 11919 3425 3148 2743 2291 2206  
82 Pb 88005 15861 15200 13035 3851 3554 3066 2586 2484 891.8
Z Element N1 4s N2 4p1/2 N3 4p3/2 N4 4d3/2 N5 4d5/2 N6 4f5/2 N7 4f7/2 O1 5s O2 5p1/2 O3 5p3/2
47 Ag 97.0 63.7 58.3              
54 Xe 213.2 146.7 145.5 69.5 67.5 --- --- 23.3 13.4 12.1
78 Pt 725.4 609.1 519.4 331.6 314.6 74.5 71.2 101.7 65.3 51.7
79 Au 762.1 642.7 546.3 353.2 335.1 87.6 84.0 107.2 74.2 57.2
82 Pb 761.9 643.5 434.3 412.2 141.7 136.9 147 106.4 83.3 20.7

6.2. Cosmic rays

Cosmic rays, or cosmic radiation refers to two aspects of a related phenomenon. Primary cosmic radiation is the radiation arriving at Earth from the Sun, galactic and extragalactic sources. The main contribution are highly energetic protons, but other long lived elementary particles and nuclei also contribute to a lesser extent. Cosmic rays are isotropic at most energies, because of the influence of galactic magnetic fields. Their energies range from \(\SI{1e9}{eV}\) up to \(\SI{1e21}{eV}\). It is generally assumed that particles below \(\SI{1e18}{eV}\) are of mainly galactic origin, whereas the above is dominated by extragalactic sources. The flux of the primary cosmic rays generally follows a power law distribution. Different contributions follow a generally similar power law (also see (Zyla and others 2020), chapter on cosmic rays).

When cosmic rays interact with the molecules of Earth's atmosphere, mesons are produced, mainly pions. Neutral pions generate showers of photons and electron-positron pairs. Charged pions on the other hand decay into muons and anti muon-neutrinos. Muons are produced over electrons in this case, due to chirality. As they are more massive than electrons they have a larger component of the opposite chirality to the neutrino, which is necessary for this 'forbidden' decay due to angular momentum conservation.

They are produced at an altitude of roughly \(\SI{15}{km}\). A large fraction of them reaches the surface as they are highly relativistic. Their spectrum is described by a convolution of the production energy, their energy loss due to ionization in the atmosphere and possible decay.

Muons are of interest in the context of helioscope experiments, as they present a dominant source of background, especially in gaseous detectors (directly and indirectly due to fluorescence). And because current helioscope experiments are built near Earth's surface, little attenuation of muon flux happens. Therefore, a good understanding of the expected muon flux is required.

Above \(\SI{100}{GeV}\) muon decay is negligible. At those energies the muon flux at the surface strictly follows the same power law as the primary cosmic ray flux. Following (Gaisser, Engel, and Resconi 2016), in this regime it can be described by

\begin{equation} \label{eq:theory:muon_flux_gaisser} \frac{\mathrm{d}N_μ}{\mathrm{d}E_μ \mathrm{d}Ω} \approx \frac{0.14 E_μ^{-2.7}}{\si{\centi\meter\squared \second \steradian \giga\electronvolt}} \times \left[ \frac{1}{1 + \frac{1.1 E_μ \cos ϑ}{\SI{115}{GeV}}} + \frac{0.054}{1 + \frac{1.1 E_μ \cos ϑ}{\SI{850}{GeV}}} \right] \end{equation}

where the first term in parenthesis is the pion and the second the kaon contribution.

For lower energies, (Shukla and Sankrith 2018) provide a set of fitted functions based on \eqref{eq:theory:muon_flux_gaisser} with a single power law

\[ I(E, θ) = I_0 N (E_0 + E)^{-n} \left(1 + \frac{E}{ε}\right)^{-1} D(θ)^{-(n - 1)}, \]

where \(I_0\), the intensity under zenith angle \(θ\), and \(ε\) is another fit parameter for the replacement of the separate meson masses in eq. \eqref{eq:theory:muon_flux_gaisser}. \(D(θ)\) is the path length through the atmosphere under an angle \(θ\) from the zenith. \(N\) is a normalization constant given by

\[ N = (n - 1) (E_0 + E_c)^{n-1}, \]

where \(n\) corresponds to the effective power of the cosine behavior and is the final fit parameter. \(E_0\) accounts for the energy loss due to interactions in the atmosphere and \(E_c\) is the lowest energy given in a data set.

If the Earth is assumed flat, it is \(D(θ) = 1/\cosθ\) (which is often assumed for simplicity and is a reasonable approximation as long as only angles close to \(θ = 0\) are considered). To describe a trajectory through Earth's curved atmosphere however, \(D(θ)\) can be modified to:

\[ D(θ) = \sqrt{ \left( \frac{R²}{d²} \cos² θ + 2\frac{R}{d} + 1 \right) } - \frac{R}{d}\cos θ \] where \(R\) is the Earth radius, \(d\) the vertical path length (i.e. the height at which the muon is created) and \(θ\) the zenith angle.

While this parametrization is very useful to describe the few specific datasets shown in (Shukla and Sankrith 2018) and provides a way to fit any measured muon flux at a specific location, it is limited in applicability to arbitrary locations, altitudes and angles. For that an approach that does not require a fit to a dataset is preferable, namely by utilizing a combination of the approximation by Gaisser, eq. \eqref{eq:theory:muon_flux_gaisser}, and the interaction of muons with the atmosphere. As such, we modify the equation for the intensity \(I\) to the following:

\[ I(E, θ) = I_0 (n-1) (E_θ(E, θ) + E_c)^{n-1} (E_θ(E, θ) + E)^{-n} \left[ \frac{1}{1 + \frac{1.1 E_μ \cos ϑ}{\SI{115}{GeV}}} + \frac{0.054}{1 + \frac{1.1 E_μ \cos ϑ}{\SI{850}{GeV}}} \right] D(θ)^{-(n - 1)}, \]

where we take \(n = 3\) exactly. One could put in the best fit for the general cosine behavior under zenith angles, \(n = n_{\text{fit}} + 1\), but for simplicity we just use 3 here. Take \(E_θ(E, θ)\) to be the energy of a muon left from initial energy \(E\) at generation in the upper atmosphere after transporting it through the atmosphere under the angle \(θ\). The transport must take into account the density change using the barometric height formula of the atmosphere. Transport is done using the Bethe-Bloch equation as introduced in sec. 6.1.3 assuming an atmosphere made up of nitrogen, oxygen and argon. As such we remove all parameters except an initial intensity \(I_0\), which can be set to the best fit of the integrated muon flux at the zenith angle at sea level. In the following figures we simply use \(I_0 = \SI{90}{\per\meter\squared \per\steradian \per\second}\). Figure 5 shows the expected differential muon flux using these parameters for different angles at sea level. In 5(a) the initial energy of the muons is shown before transporting through the atmosphere. For each angle the lines cut off at the energy below which the muon would likely be stopped by the atmosphere according to its energy loss per distance or reaches the surface with less than \(\SI{300}{MeV}\). On the right we see the final energy of the same muons at the surface. The lines in 5(b) are ragged, because muon decay is simulated using Monte Carlo. 6

These numbers match reasonably well with different datasets for different locations under different angles, but they should not be considered as more than a starting point for a general expectation. However, they are still useful as a reference to consider when evaluating muon fluxes under different angles at CAST.

Figure 5(a): Initial energy
Figure 5(b): Final energy
Figure 5: Differential muon flux at sea level for different zenith angles. 5(a) shows the initial energy of the muon. The cutoff corresponds to the lowest energy transported through the atmosphere; muons still arrive at the surface without decay or stopping. 5(b) shows the final muon energy at the surface, with the lowest muon $\SI{300}{MeV}$ at the surface.

6.2.1. Calculation of muon angular / energy dependence at surface    extended

The code here is directly based on code written in my notes ./../org/Doc/StatusAndProgress.html being tangled into a file /tmp/muon_flux.nim.

Aside from the figures included in the main thesis, this also produces fig. 6.

flux_at_cast_88_deg.svg
Figure 6: Figure 12: Expected muon flux under an angle of \(\SI{88}{°}\) at CAST.
import math, macros, unchained
import seqmath, ggplotnim, sequtils, strformat

let K = 4 * π * N_A * r_e^2 * m_e * c^2 # usually in: [MeV mol⁻¹ cm²]

defUnit(cm³•g⁻¹)
defUnit(J•m⁻¹)
defUnit(cm⁻³)
defUnit(g•mol⁻¹)
defUnit(MeV•g⁻¹•cm²)
defUnit(mol⁻¹)
defUnit(keV•cm⁻¹)
defUnit(g•cm⁻³)
defUnit(g•cm⁻²)

proc electronDensity(ρ: g•cm⁻³, Z, A: UnitLess): cm⁻³ =
  result = N_A * Z * ρ / (A * M_u.to(g•mol⁻¹))

proc I[T](z: float): T =
  ## use Bloch approximation for all but Argon (better use tabulated values!)
  # 188.0 eV from NIST table 
  result = if z == 18.0: 188.0.eV.to(T) 
           else: (10.eV * z).to(T)

proc calcβ(γ: UnitLess): UnitLess =
  result = sqrt(1.0 - 1.0 / (γ^2))

proc betheBloch(z, Z: UnitLess,
                   A: g•mol⁻¹,
                   γ: UnitLess,
                   M: kg): MeV•g⁻¹•cm² =
  ## result in MeV cm² g⁻¹ (normalized by density)
  ## z: charge of particle
  ## Z: charge of particles making up medium
  ## A: atomic mass of particles making up medium
  ## γ: Lorentz factor of particle
  ## M: mass of particle in MeV (or same mass as `m_e` defined as)
  let β = calcβ(γ)
  let W_max = 2 * m_e * c^2 * β^2 * γ^2 /
    (1 + 2 * γ * m_e / M + (m_e / M)^2)
  let lnArg = 2 * m_e * c^2 * β^2 * γ^2 * W_max / (I[Joule](Z)^2)
  result = (K * z^2 * Z / A * 1.0 / (β^2) * (
    0.5 * ln(lnArg) - β^2
  )).to(MeV•g⁻¹•cm²)

proc mostProbableLoss(z, Z: UnitLess, A: g•mol⁻¹, γ: UnitLess,
                      x: g•cm⁻²): keV =
  ## Computes the most probable value, corresponding to the peak of the Landau
  ## distribution, that gives rise to the Bethe-Bloch formula.
  ##
  ## Taken from PDG chapter 'Passage of particles through matter' equation
  ## `34.12` in 'Fluctuations in energy loss', version 2020).
  ##
  ## `x` is the "thickness". Density times length, `x = ρ * d`. The other parameters
  ## are as in `betheBloch` above.
  let β = calcβ(γ)
  let ξ = K / 2.0 * Z / A * z*z * (x / (β*β))
  const j = 0.200
  let I = I[Joule](Z)
  result = (ξ * ( ln((2 * m_e * c^2 * β^2 * γ^2).to(Joule) / I) + ln(ξ.to(Joule) / I) + j - β^2)).to(keV) # - δ*(β*γ)

proc density(p: mbar, M: g•mol⁻¹, temp: Kelvin): g•cm⁻³ =
  ## returns the density of the gas for the given pressure.
  ## The pressure is assumed in `mbar` and the temperature (in `K`).
  ## The default temperature corresponds to BabyIAXO aim.
  ## Returns the density in `g / cm^3`
  let gasConstant = 8.314.J•K⁻¹•mol⁻¹ # joule K^-1 mol^-1
  let pressure = p.to(Pa) # pressure in Pa
  # factor 1000 for conversion of M in g / mol to kg / mol
  result = (pressure * M / (gasConstant * temp)).to(g•cm⁻³)

proc E_to_γ(E: GeV): UnitLess =
  result = E.to(Joule) / (m_μ * c^2) + 1

proc γ_to_E(γ: UnitLess): GeV =
  result = ((γ - 1) * m_μ * c^2).to(GeV)

type
  Element = object
    Z: UnitLess
    M: g•mol⁻¹
    A: UnitLess # numerically same as `M`
    ρ: g•cm⁻³
proc initElement(Z: UnitLess, M: g•mol⁻¹, ρ: g•cm⁻³): Element =
  Element(Z: Z, M: M, A: M.UnitLess, ρ: ρ)

# molar mass. Numerically same as relative atomic mass
let M_Ar = 39.95.g•mol⁻¹
let ρAr = density(1050.mbar, M_Ar, temp = 293.15.K)
let Argon = initElement(18.0.UnitLess, 39.95.g•mol⁻¹, ρAr)

proc intBethe(e: Element, d_total: cm, E0: eV, dx = 1.μm): eV =
  ## integrated energy loss of bethe formula after `d` cm of matter
  ## and returns the energy remaining
  var γ: UnitLess = E_to_γ(E0.to(GeV))
  var d: cm
  result = E0
  var totalLoss = 0.eV
  while d < d_total and result > 0.eV:
    let E_loss: MeV = betheBloch(-1, e.Z, e.M, γ, m_μ) * e.ρ * dx
    result = result - E_loss.to(eV)
    γ = E_to_γ(result.to(GeV))
    d = d + dx.to(cm)
    totalLoss = totalLoss + E_loss.to(eV)
  result = max(0.float, result.float).eV

proc plotDetectorAbsorption() =
  let E_float = logspace(-2, 2, 1000)
  let energies = E_float.mapIt(it.GeV)
  let E_loss = energies.mapIt(
    (it.to(eV) - intBethe(Argon, 3.cm, it.to(eV))).to(keV).float
  )
  let df = toDf(E_float, E_loss)
  ggplot(df, aes("E_float", "E_loss")) +
    geom_line() +
    xlab("μ Energy [GeV]") + ylab("ΔE [keV]") +
    scale_x_log10() + scale_y_log10() +
    ggtitle("Energy loss of Muons in 3 cm Ar at CAST conditions") +
    ggsave("/home/basti/phd/Figs/muons/ar_energy_loss_cast.pdf")
plotDetectorAbsorption()

let Atmosphere = @[(0.78084, initElement(7.0.UnitLess, 14.006.g•mol⁻¹, 1.2506.g•dm⁻³.to(g•cm⁻³))), # N2
                   (0.20964, initElement(8.0.UnitLess, 15.999.g•mol⁻¹, 1.429.g•dm⁻³.to(g•cm⁻³))),  # O2
                   (0.00934, initElement(18.0.UnitLess, 39.95.g•mol⁻¹, 1.784.g•dm⁻³.to(g•cm⁻³)))]  # Ar

proc plotMuonBethe() =
  let E_float = logspace(-2, 2, 1000)
  let energies = E_float.mapIt(it.GeV)
  var dEdxs = newSeq[float]()
  for e in energies:
    var dEdx = 0.0.MeV•g⁻¹•cm²
    for elTup in Atmosphere:
      let (w, element) = elTup
      let γ = E_to_γ(e)
      dEdx += w * betheBloch(-1, element.Z, element.M, γ, m_μ)
    dEdxs.add dEdx.float
  let df = toDf(E_float, dEdxs)
  ggplot(df, aes("E_float", "dEdxs")) +
    geom_line() +
    xlab("μ Energy [GeV]") + ylab("dE/dx [MeV•g⁻¹•cm²]") +
    scale_x_log10() + scale_y_log10() +
    ggtitle("Energy loss of Muons in atmosphere") +
    ggsave("/home/basti/phd/Figs/muons/energy_loss_muons_atmosphere.pdf")  
plotMuonBethe()
#if true: quit()
import math, unchained, ggplotnim, sequtils

const R_Earth = 6371.km
func distanceAtmosphere(θ: Radian, d: KiloMeter = 36.6149.km): UnitLess =
  ## NOTE: The default value for `d` is not to be understood as a proper height. It.s an
  ## approximation based on a fit to get `R_Earth / d = 174`!
  result = sqrt((R_Earth / d * cos(θ))^2 + 2 * R_Earth / d + 1) - R_Earth / d * cos(θ)

defUnit(cm⁻²•s⁻¹•sr⁻¹)  
defUnit(m⁻²•s⁻¹•sr⁻¹)
proc muonFlux(E: GeV, θ: Radian, E₀, E_c: GeV,
              I₀: m⁻²•s⁻¹•sr⁻¹,
              ε: GeV): m⁻²•s⁻¹•sr⁻¹ =
  const n = 3.0
  let N = (n - 1) * pow((E₀ + E_c).float, n - 1)
  result = I₀ * N * pow((E₀ + E).float, -n) *
    #pow((1 + E / ε).float, -1) *
    ( ( 1.0 / (1 + 1.1 * E * cos(θ) / 115.GeV).float) + (0.054 / (1 + 1.1 * E * cos(θ) / 850.GeV).float) ) * 
    pow(distanceAtmosphere(θ), -(n - 1))

from numericalnim/integrate import simpson
proc plotE_vs_flux(θ: Radian, E₀, E_c: GeV, I₀: m⁻²•s⁻¹•sr⁻¹, ε: GeV,
                   suffix = "") =
  let energies = linspace(E_c.float, 100.0, 1000)
  let E = energies.mapIt(it.GeV)
  let flux = E.mapIt(muonFlux(it, θ, E₀, E_c, I₀, ε).float) # .to(cm⁻²•s⁻¹•sr⁻¹)
  let df = toDf(energies, flux)

  echo "Integrated flux: ", simpson(flux, energies)
  
  ggplot(df, aes("energies", "flux")) +
    geom_line() +
    xlab("Energy [GeV]") + ylab("Flux [m⁻²•s⁻¹•sr⁻¹]") +
    scale_x_log10() + scale_y_log10() +
    ggtitle(&"Flux dependency on the energy of muons at θ = {θ.to(°)}{suffix}") +
    ggsave(&"/home/basti/phd/Figs/muons/energy_vs_flux_cosmic_muons{suffix}.pdf")
plotE_vs_flux(0.Radian,
              2.5.GeV, #4.29.GeV,
              0.5.GeV, 70.7.m⁻²•s⁻¹•sr⁻¹, 854.GeV)


let E₀ = 25.0.GeV
let I₀ = 90.0.m⁻²•s⁻¹•sr⁻¹
let E_c = 1.GeV
let ε = 2000.GeV

proc plotFlux_at_CAST() =
  let energies = linspace(0.5, 100.0, 1000)
  let E = energies.mapIt(it.GeV)
  let flux = E.mapIt(muonFlux(it, 88.0.degToRad.Radian, E₀, E_c, I₀, ε).float)
  let df = toDf(energies, flux)
  ggplot(df, aes("energies", "flux")) +
    geom_line() +
    xlab("Energy [GeV]") + ylab("Flux [m⁻²•s⁻¹•sr⁻¹]") +
    scale_x_log10() + scale_y_log10() +
    ggtitle("Flux dependency on the energy at θ = 88° at CAST altitude") +
    ggsave("/home/basti/phd/Figs/muons/flux_at_cast_88_deg.pdf")
plotFlux_at_CAST()

proc computeMeanEnergyLoss() =
  let energies = linspace(0.5, 100.0, 1000)
  let E = energies.mapIt(it.GeV)
  let flux = E.mapIt(muonFlux(
    it, 88.0.degToRad.Radian, E₀, E_c, I₀, ε).float
  )
  let E_loss = E.mapIt(
    (it.to(eV) - intBethe(Argon, 3.cm, it.to(eV))).to(keV).float
  )
  let fluxSum = flux.sum
  let df = toDf(energies, E_loss, flux)
      .mutate(f{"flux" ~ `flux` / fluxSum},
              f{"AdjFlux" ~ `E_loss` * `flux`})
  echo "Mean energy loss: ", df["AdjFlux", float].sum
computeMeanEnergyLoss()

proc computeHeight(S: Meter, θ: Radian): KiloMeter =
  ## For given remaining distance distance along the path of a muon
  ## `S` (see fig. 1 in 1606.06907) computes the remaining height above
  ## ground. Formula is the result of inverting eq. 7 to `d` using quadratic
  ## formula. Positive result, because negative is negative.
  result = (-1.0 * R_Earth + sqrt(R_Earth^2 + S^2 + 2 * S * R_Earth * cos(θ)).m).to(km)

import algorithm
defUnit(K•m⁻¹)
proc barometricFormula(h: KiloMeter): g•cm⁻³ =
  let hs = @[0.0.km, 11.0.km]
  let ρs = @[1.225.kg•m⁻³, 0.36391.kg•m⁻³]
  let Ts = @[288.15.K, 216.65.K]
  let Ls = @[-1.0 * 0.0065.K•m⁻¹, 0.0.K•m⁻¹]
  let M_air = 0.0289644.kg•mol⁻¹
  let R = 8.3144598.N•m•mol⁻¹•K⁻¹
  let g_0 = 9.80665.m•s⁻²
  let idx = hs.mapIt(it.float).lowerBound(h.float) - 1
  case idx
  of 0:
    # in Troposphere, using regular barometric formula for denities
    let expArg = g_0 * M_air / (R * Ls[idx])
    result = (ρs[idx] * pow(Ts[idx] / (Ts[idx] + Ls[idx] * (h - hs[idx])), expArg)).to(g•cm⁻³)
  of 1:
    # in Tropopause, use equation valid for L_b = 0
    result = (ρs[idx] * exp(-1.0 * g_0 * M_air * (h - hs[idx]) / (R * Ts[idx]))).to(g•cm⁻³)
  else: doAssert false, "Invalid height! Outside of range!"

import random
randomize(430)
proc intBetheAtmosphere(E: GeV, θ: Radian, dx = 10.cm): eV =
  ## integrated energy loss using Bethe formula for muons generated at
  ## `15.km` under an angle of `θ` to the observer for a muon of energy
  ## `E`.
  # Main contributions in Earth's atmosphere
  const τ = 2.19618.μs # muon half life
  let elements = Atmosphere
  var γ: UnitLess = E_to_γ(E.to(GeV))
  result = E.to(eV)
  var totalLoss = 0.eV
  let h_muon = 15.km # assume creation happens in `15.km`
  let S = h_muon.to(m) * distanceAtmosphere(θ.rad, d = h_muon)
  var S_prime = S
  while S_prime > 0.m and result > 0.eV:
    let h = computeHeight(S_prime, θ)
    let ρ_at_h = barometricFormula(h)
    var E_loss = 0.0.MeV
    for eTup in elements: # compute the weighted contribution of the element fraction
      let (w, e) = eTup
      E_loss += w * betheBloch(-1, e.Z, e.M, γ, m_μ) * ρ_at_h * dx

    ## Add step for radioactive decay of muon.
    ## - given `dx` compute likelihood of decay
    ## - eigen time of muon: dx / v = dt. dτ = dt / γ
    ## - muon decay is λ = 1 / 2.2e-6s
    let β = calcβ(γ)
    # compute effective time in lab frame
    let δt = dx / (β * c)
    # compute eigen time
    let δτ = δt / γ
    # probability of a decay in this time frame
    let p = pow(1 / math.E, δτ / τ)
    # decay with likelihood `p`
    #echo "γ = ", γ, " yields ", p, " in δτ ", δτ, " for energy ", E
    if rand(1.0) < (1.0 - p):
      echo "Particle decayed after: ", S_prime
      return 0.eV
          
    result = result - E_loss.to(eV)
    S_prime = S_prime - dx
    γ = E_to_γ(result.to(GeV))
    totalLoss = totalLoss + E_loss.to(eV)
  echo "total Loss ", totalLoss.to(GeV)
  result = max(0.float, result.float).eV

block MuonLimits:
  let τ_μ = 2.1969811.μs
  # naively this means given some distance `s` the muon can
  # traverse `s = c • τ_μ` (approximating its speed by `c`) before
  # it has decayed with a 1/e chance
  # due to special relativity this is extended by γ
  let s = c * τ_μ
  echo s
  # given production in 15 km, means
  let h = 15.km
  echo h / s
  # so a reduction of (1/e)^22. So 0.
  # now it's not 15 km but under an angle `θ = 88°`.
  let R_over_d = 174.UnitLess
  let n = 3.0
  let E₀ = 25.0.GeV
  let I₀ = 90.0.m⁻²•s⁻¹•sr⁻¹
  let E_c = 1.GeV
  let ε = 2000.GeV

  # distance atmospher gives S / d, where `d` corresponds to our `h` up there
  let S = h * distanceAtmosphere(88.0.degToRad.rad)
  # so about 203 km
  # so let's say 5 * mean distance is ok, means we ned
  let S_max = S / 5.0
  # so need a `γ` such that `s` is stretched to `S_max`
  let γ = S_max / s
  echo γ
  # ouch. Something has to be wrong. γ of 61?

  # corresponds to an energy loss of what?
  let Nitrogen = initElement(7.0.UnitLess, 14.006.g•mol⁻¹, 1.2506.g•dm⁻³.to(g•cm⁻³))
  echo "================================================================================"
  echo "Energy left: ", intBethe(Nitrogen, S.to(cm), 6.GeV.to(eV), dx = 1.m.to(μm)).to(GeV)
  proc print(E: GeV, θ: Radian) =
    let left = intBetheAtmosphere(E, θ = θ).to(GeV)
    echo "E = ", E, ", θ = ", θ, ", Bethe = ", E - left
  print(6.GeV, 0.Radian)
  #print(200.GeV, 0.Radian)  
  #print(200.GeV, 88.°.to(Radian))
  #print(200.GeV, 75.°.to(Radian))

  let E_loss75 = 100.GeV - intBetheAtmosphere(100.GeV, 75.°.to(Radian)).to(GeV)
  plotE_vs_flux(75.°.to(Radian),
                E_loss75, #23.78.GeV, #25.GeV, #E_loss75,
                1.0.GeV,
                90.m⁻²•s⁻¹•sr⁻¹, #65.2.m⁻²•s⁻¹•sr⁻¹,
                2000.GeV, # 854.GeV,
                "_at_75deg")

  
  echo "S@75° = ", h * distanceAtmosphere(75.0.degToRad.rad, d = 15.0.km)
  echo "================================================================================"  
echo E_to_γ(4.GeV)
echo E_to_γ(0.GeV)

proc plotE_vs_flux_and_angles(E_c: GeV, I₀: m⁻²•s⁻¹•sr⁻¹, ε: GeV,
                              suffix = "") =
  ## Generates a plot of the muon flux vs energy for a fixed set of different
  ## angles.
  ##
  ## The energy loss is computed using a fixed 
  let energies = logspace(log10(E_c.float), 2.float, 1000)
  let angles = linspace(0.0, 80.0, 9)
  block CalcLossEachMuon:
    var df = newDataFrame()
    for angle in angles:
      let E = energies.mapIt(it.GeV)
      let θ = angle.°.to(Radian)
      var flux = newSeq[float]()
      var E_initials = newSeq[float]()
      var E_lefts = newSeq[float]()
      var lastDropped = 0.GeV
      for e in E:
        let E_left = intBetheAtmosphere(e, θ).to(GeV)
        if E_left <= 0.0.GeV:
          echo "Skipping energy : ", e, " as muon was lost in atmosphere"
          continue
        elif E_left <= E_c:
          echo "Skipping energy : ", e, " as muon has less than E_c = ", E_c, " energy left"
          lastDropped = e
          continue
        let E₀ = e - E_left
        flux.add muonFlux(e, θ, E₀, E_c, I₀, ε).float
        E_initials.add e.float        
        E_lefts.add E_left.float
      let dfLoc = toDf({E_initials, E_lefts, flux, "angle [°]" : angle})
      #  .filter(f{`E_initials` >= lastDropped.float})
      df.add dfLoc

    var theme = themeLatex(fWidth = 0.5, width = 600, baseTheme = sideBySide)
    theme.tickWidth = some(theme.tickWidth.get / 2.0)
    theme.tickLength = some(theme.tickLength.get / 2.0)    
    theme.gridLineWidth = some(theme.gridLineWidth.get / 2.0)    
    ggplot(df, aes("E_initials", "flux", color = factor("angle [°]"))) +
      geom_line() +
      xlab(r"Initial energy [\si{GeV}]") + ylab(r"Flux [\si{m^{-2}.s^{-1}.sr^{-1}}]", margin = 2.5) +
      scale_x_log10() + scale_y_log10() +
      margin(right = 3.0) + 
      ggtitle(&"Muon flux at different angles{suffix}") +
      theme + 
      ggsave(&"/home/basti/phd/Figs/muons/initial_energy_vs_flux_and_angle_cosmic_muons{suffix}.pdf",
             useTeX = true, standalone = true, width = 600, height = 450)
  
    ggplot(df, aes("E_lefts", "flux", color = factor("angle [°]"))) +
      geom_line() +
      xlab(r"Energy at surface [\si{GeV}]") + ylab(r"Flux [\si{m^{-2}.s^{-1}.sr^{-1}}]", margin = 2.5) +
      scale_x_log10() + scale_y_log10() +
      margin(right = 3.0) +       
      theme + 
      ggtitle(&"Muon flux at different angles{suffix}") +
      ggsave(&"/home/basti/phd/Figs/muons/final_energy_vs_flux_and_angle_cosmic_muons{suffix}.pdf",
             useTeX = true, standalone = true, width = 600, height = 450)              
  block StaticLoss:
    var df = newDataFrame()
    for angle in angles:
      let E = energies.mapIt(it.GeV)
      let θ = angle.°.to(Radian)
      let E₀ = 100.GeV - intBetheAtmosphere(100.GeV, 0.0.Radian).to(GeV)    
      let flux = E.mapIt(muonFlux(it, θ, E₀, E_c, I₀, ε).float)
      let dfLoc = toDf({energies, flux, "angle [°]" : angle})
      df.add dfLoc
    ggplot(df, aes("energies", "flux", color = factor("angle [°]"))) +
      geom_line() +
      xlab("Energy [GeV]") + ylab("Flux [m⁻²•s⁻¹•sr⁻¹]") +
      scale_x_log10() + scale_y_log10() +
      ggtitle(&"Differential muon flux dependency at different angles{suffix}") +
      ggsave(&"/home/basti/phd/Figs/muons/energy_vs_flux_and_angle_cosmic_muons{suffix}.pdf")


#proc plotE_vs_flux_and_angles(E_c: GeV, I₀: m⁻²•s⁻¹•sr⁻¹, ε: GeV,
#                              suffix = "") =
#  ## Generates a plot of the integrated muon flux vs angles for a fixed set of different
#  ## energies.
#  let angles = linspace(0.0, 90.0, 100)
#  var df = newDataFrame()
#  let energies = linspace(E_c.float, 100.0, 1000)
#  let E = energies.mapIt(it.GeV)
#  for angle in angles:
#    let θ = angle.°.to(Radian)
#    let E₀ = 100.GeV - intBetheAtmosphere(100.GeV, θ).to(GeV)
#    let flux = E.mapIt(muonFlux(it, θ, E₀, E_c, I₀, ε).float) 
#    let dfLoc = toDf({energies, flux, "angle [°]" : angle})
#    df.add dfLoc
#  ggplot(df, aes("energies", "flux", color = factor("angle [°]"))) +
#    geom_line() +
#    xlab("Energy [GeV]") + ylab("Flux [m⁻²•s⁻¹•sr⁻¹]") +
#    scale_x_log10() + scale_y_log10() +
#    ggtitle(&"Differential muon flux dependency at different angles{suffix}") +
#    ggsave(&"/home/basti/phd/Figs/muons/energy_vs_flux_and_angle_cosmic_muons{suffix}.pdf")

# different angles!      
block MuonBehavior:
  plotE_vs_flux_and_angles(0.3.GeV, 90.m⁻²•s⁻¹•sr⁻¹, 854.GeV)

proc unbinnedCdf(x: seq[float]): (seq[float], seq[float]) =
  ## Computes the CDF of unbinned data
  var cdf = newSeq[float](x.len)
  for i in 0 ..< x.len:
    cdf[i] = i.float / x.len.float
  result = (x.sorted, cdf)

import random, algorithm
proc sampleFlux(samples = 1_000_000): DataFrame =
  randomize(1337)
  let energies = linspace(0.1, 100.0, 100_000)
  #let energies = logspace(0, 2, 1000)
  let E = energies.mapIt(it.GeV)
  let flux = E.mapIt(muonFlux(it, 88.0.degToRad.Radian, E₀, E_c, I₀, ε).float)
  # given flux compute CDF
  let fluxCS = flux.cumSum()
  let fluxCS_sorted = flux.sorted.cumSum()
  let fluxCDF = fluxCS.mapIt(it / fluxCS[^1])
  let fluxCDF_sorted = fluxCS_sorted.mapIt(it / fluxCS_sorted[^1])

  let (data, cdf) = unbinnedCdf(flux)

  let dfX = toDf(energies, fluxCS, fluxCS_sorted, fluxCDF, fluxCDF_sorted)
  ggplot(dfX, aes("energies", "fluxCS")) +
    geom_line() +
    ggsave("/t/cumsum_test.pdf")
  ggplot(dfX, aes("energies", "fluxCDF")) +
    geom_line() +
    ggsave("/t/cdf_test.pdf")    
  ggplot(dfX, aes("energies", "fluxCS_sorted")) +
    geom_line() +
    ggsave("/t/cumsum_sorted_test.pdf")    
  ggplot(dfX, aes("energies", "fluxCDF_sorted")) +
    geom_line() +
    ggsave("/t/cdf_sorted_test.pdf")

  ggplot(toDf(data, cdf), aes("data", "cdf")) +
    geom_line() +
    ggsave("/t/unbinned_cdf.pdf")
  
  #if true: quit()
  var lossesBB = newSeq[float]()
  var lossesMP = newSeq[float]()
  var energySamples = newSeq[float]()

  let dedxmin = 1.519.MeV•cm²•g⁻¹
  echo "Loss = ", (dedxmin * Argon.ρ * 3.cm).to(keV)
  
  for i in 0 ..< samples:
    # given the fluxCDF sample different energies, which correspond to the
    # distribution expected at CAST
    let idx = fluxCdf.lowerBound(rand(1.0))
    let E_element = E[idx]
    # given this energy `E` compute the loss
    let lossBB = (E_element.to(eV) - intBethe(Argon, 3.cm, E_element.to(eV), dx = 50.μm)).to(keV).float
    lossesBB.add lossBB
    let lossMP = mostProbableLoss(-1, Argon.Z, Argon.M, E_Element.E_to_γ(), Argon.ρ * 3.cm)
    lossesMP.add lossMP.float
    #echo "Index ", i, " yields energy ", E_element, " and loss ", loss
    energySamples.add E_element.float
  let df = toDf(energySamples, lossesBB, lossesMP)
    .gather(["lossesBB", "lossesMP"], "Type", "Value")
  ggplot(df, aes("Value", fill = "Type")) +
    geom_histogram(bins = 300, hdKind = hdOutline, alpha = 0.5, position = "identity") +
    margin(top = 2) +
    xlim(5, 15) +
    ggtitle(&"Energy loss of muon flux at CAST based on MC sampling with {samples} samples") +
    ggsave("/home/basti/phd/Figs/muons/sampled_energy_loss.pdf")

  ggplot(df, aes("energySamples")) +
    geom_histogram(bins = 300) +
    margin(top = 2) +
    ggtitle(&"Sampled energies for energy loss of muon flux at CAST") +
    ggsave("/home/basti/phd/Figs/muons/sampled_energy_for_energy_loss.pdf")
  let (samples, bins) = histogram(energySamples, bins = 300)
  let dfH = toDf({"bins" : bins[0 ..< ^1], samples})
    .filter(f{`bins` > 0.0 and `samples`.float > 0.0})
  ggplot(dfH, aes("bins", "samples")) +
    geom_line() +
    scale_x_log10() + 
    margin(top = 2) +
    ggtitle(&"Sampled energies for energy loss of muon flux at CAST") +
    ggsave("/home/basti/phd/Figs/muons/sampled_energy_for_energy_loss_manual.pdf")

  ggplot(toDf(energies, flux), aes("energies", "flux")) +
    geom_line() +
    scale_x_log10() +
    ggsave("/tmp/starting_data_e_flux.pdf")

  ggplot(toDf(energies, flux), aes("energies", "flux")) +
    geom_line() +
    ggsave("/tmp/linear_starting_data_e_flux.pdf")
    

discard sampleFlux(samples = 1_000_000)
ntangle thesis.org && nim c -d:release code/muons
code/muons

6.3. Gaseous detector fundamentals

Gaseous detectors consist of a volume filled with gas, usually a noble gas with a small amount of molecular gas. For low rate, low energy experiments an entrance window allows the particles to be detected, to enter. An electric field is applied over the gas volume, strong enough to cause electron-ion pairs created by ionization of the incoming particles to drift to opposite ends of the volume (magnetic fields may be utilized in addition). At least on the side of the anode (where the electrons arrive), a readout of some form is installed to measure the time of arrival, amount of collected charge and / or the position of the electrons. Depending on the details, this in principle allows for a 3D reconstruction of the initial event in the gas volume. The details of choice of detector gas, applied electric fields, gas volume dimensions and types of readout have a very large impact on the applications a detector is useful for. In the following we will focus on the physics concepts required for gaseous detectors with few \(\si{cm}\) long drift volumes and high spatial resolution readouts.

Note: this section covers the basic fundamentals that will be later referenced in the thesis. For a much more in-depth understanding of these concepts, see references (Sauli 2014) and (Kolanoski and Wermes 2020) and to some extent the PDG (Zyla and others 2020) (in particular chapters on particle detectors and passage of particles through matter; chapter number varies by year).

6.3.1. Gas mixtures and Dalton's law

Of common interest when dealing with gas mixtures is the notion of partial pressures. In ideal gas theory, a mixture of gases at a pressure \(P\) can be considered to be the sum of the 'partial pressures' of each gas species

\[ P = \sum_i p_i, \]

initially noted by John Dalton in 1802 (Dalton 1802).

The contribution of each gas only depends on the species' mole fraction. Typically when considering gas mixtures the fractions of each gas species is given as a percentage. The percentage already refers to the mole fraction of each species. As such, the partial pressure of a single species can be expressed as:

\[ p_i = n_i P \]

where \(n_i\) is the mole fraction of species \(i\).

This is an extremely valuable property when computing different interactions of particles with gas mixtures. For example when computing the absorption of X-rays after propagating through a certain distance of a gas mixture, as one can compute absorption for each partial pressure independently and combine the contributions after (whether they be added, multiplied or something else of course depends on the considered process).

6.3.2. Ionization energy and average energy per electron-ion pair

In order to understand the signals detected by a gaseous detector, the average number of electrons liberated by an incident particle should be known. This can be calculated given the energy loss in a gas required to produce a single electron-ion pair, called the \(W\text{-value}\),

\[ W = \frac{I}{⟨N⟩}. \]

Here, \(I\) is the mean ionization energy of the gas and \(⟨N⟩\) the average number of electron-ion pairs generated per interaction. \(⟨N⟩\) is usually smaller than one, \(\numrange{0.6}{0.7}\) for noble gases and even below \(\num{0.5}\) for molecular gases. Not all energy of the incoming particle is always deposited in the form of generation of, for example, a photoelectron. Other forms of energy loss are possible. In molecular gases vibrational and rotational modes offer even more possibilities, resulting in the smaller values.

The mean excitation energy \(I\) of an element is the weighted combination of the most likely energy levels the element is ionized from. The exact values are dependent on the specific element and tabulated values like from NIST (Hubbell and Seltzer 1996) exist. Above some \(Z\) (roughly argon, \(Z = 18\)) a rough approximation of \(I = \SI{10}{eV} · Z\) can be substituted (Zyla and others 2020), developed by Bloch (Bloch 1933).

The precise number for \(W\) strongly depends on the used gas mixture and typically requires experimental measurements to determine. Monte Carlo based simulations can be used as a rough guide, but care must be taken interpreting the results as significant uncertainty can remain. Tools for such Monte Carlo simulations include GEANT4 (Agostinelli and others 2003) 7 and MCNelectron (Poškus 2015) 8. These are based on atomic excitation cross sections, which are well tabulated in projects like ENDF (Brown et al. 2018) (citation for the latest data release) 9 and LXCat (Pancheshnyi et al. 2012; Pitchford et al. 2016; Carbone et al. 2021) 10.

6.3.3. Mean free path

While we have already discussed the mean free path for X-rays in sec. 6.1.1, we should revisit it quickly for electrons in a gas. It is a necessary concept when trying to understand other gaseous detector physics concepts, in particular for the gas amplification. 11

The mean free path of an electron in a gas can be described by

\begin{equation} \label{eq:theory:mean_free_path_e_def} λ = \frac{1}{nσ} \end{equation}

where \(n = N/V\) is the number density (atoms or molecules per unit volume) of the gas particles and \(σ\) the cross section of electrons interacting with them. Such cross sections are tabulated for different elements, for example again see LXCat (Pancheshnyi et al. 2012; Pitchford et al. 2016; Carbone et al. 2021).

Based on the ideal gas law, we can express it as a function of pressure \(p\) and temperature \(T\) to

\[ p V = N k_B T ⇔ p = n k_B T, \]

with the Boltzmann constant \(k_B\). Inserting this via \(n\) into eq. \eqref{eq:theory:mean_free_path_e_def} yields

\begin{equation} \label{eq:theory:mean_free_path_e} λ = \frac{k_B T}{p σ}. \end{equation}

6.3.4. Diffusion

Diffusion in the context of gaseous detectors is the process of the random walk of electrons either in longitudinal or transverse (to the electric field) direction they exhibit, when drifting towards the readout. Depending on the specific detector technology, some transverse diffusion may be a desired property. For long drift distances, magnetic fields can be used to significantly reduce the transverse diffusion.

For an overview of the mathematics of diffusion as a random walk process, see (Berg 1993). In the context of gaseous detectors see for example any of (Sauli 2014; Kolanoski and Wermes 2020; Hilke and Riegler 2020) In general, for precise numbers measurements must be taken. Monte Carlo simulations using tools like Magboltz (Biagi 1995b) or PyBoltz (Atoum et al. 2020) can be used as a general reference if no measurements are available.

The mean distance \(σ_t\) from a starting point after a certain amount of time \(t\) is given by

\[ σ_t = \sqrt{ 2 N D t } \]

for a random walk in \(N\) dimensions, where \(D\) is a diffusion coefficient specifying the movement in distance squared per time. For example for a point like source of multiple electrons, after diffusion of some distance a 2 dimensional gaussian distribution will be expected with a standard deviation of \(σ_t\). By relating the time to the drift velocity \(v\) along an axis and introducing the diffusion constant \(D_t\)

\[ D_t = \sqrt{ \frac{2 N D}{v} } \]

we can further express \(σ_t\) as

\begin{equation} \label{eq:gas_physics:diffusion_after_drift} σ_t = D_t · \sqrt{x}, \end{equation}

which is often practically useful to estimate the expected diffusion after a certain drift length.

Note that the terminology of "diffusion constant", "diffusion coefficient" and similar is often used ambiguously as to whether they refer to \(D\) or \(D_t\) (or sometimes even \(σ_t\)). Nor is the considered dimensionality \(N\) always clearly indicated. Keeping \(N = 1\) and handling multiple dimensions as independent random walks is a practical approach to take (as long as it is valid in the application). 12

6.3.5. Drift velocity

The drift velocity is the average speed at which electrons move towards the anode in a gaseous detector under the influence of an electric field. It is required to understand how recorded time information relates to longitudinal shape information of recorded data. Based on the so called 'friction force model' an analytical expression for the drift velocity in an electromagnetic field can be written to:

\[ \vec{v} = \frac{e}{m_e} \frac{τ}{1 + ω²τ²}\left( \vec{E} + \frac{ωτ}{B} (\vec{E} × \vec{B}) + \frac{ω²τ²}{B²}(\vec{E} · \vec{B}) \vec{B} \right) \]

with the electron charge \(e\) and mass \(m_e\), in an electric field \(\vec{E}\) and magnetic field \(\vec{B}\), given the Lamor frequency \(ω = eB / m_e\) and the mean collision time \(τ\). (Zyla and others 2020)

For detectors without a magnetic field \(B = 0, ω = 0\) and a constant, homogeneous electric field \(E\), this reduces to the Townsend expression:

\[ v = \frac{e E τ}{m_e}. \]

If measurements are not available, these can also be computed by Magboltz (Biagi 1995b) or PyBoltz (Atoum et al. 2020), which solve the underlying transport equation, the Boltzmann equation.

6.3.6. Gas amplification

In order to turn the individual electrons into a measurable signal, gaseous detectors use some form of gas amplification. Details vary, but it is usually a region in the gas volume close to the readout with a very strong electric field (multiple \(\si{kV.cm^{-1}}\)) such that each electron causes many secondary ionizations, leading to an avalanche effect. In case of the detectors described in this thesis, amplification factors between \(\numrange{2000}{4500}\) are desired.

An electron in a strong electric field \(\vec{E}\) will gain enough energy to ionize an atom of the gas after a distance

\[ l \geq \frac{I}{|\vec{E}|} \]

with the ionization potential of the gas, \(I\). This needs to be put into relation with the mean free path, eq. \eqref{eq:theory:mean_free_path_e}. If \(l \ll λ\) no secondary ionization takes place and if \(l \gg λ\) every interaction leads to ionization, likely resulting in a breakdown causing an arc (see also Paschen's law (Paschen 1889), not covered here). In the intermediate range some interactions cause secondary ionization, some does not.

We can make the statistical argument that

\[ \mathcal{N} = e^{-l/λ} \]

is the relative number of collisions with \(l > λ\). This allows to define the probability of finding a number of ionizations per unit length to be

\[ P(l) = \frac{1}{λ} e^{-l / λ} = α, \]

where we introduce the definition of the 'first Townsend coefficient', \(α\). We can insert the definition of the mean free path, eq. \eqref{eq:theory:mean_free_path_e}, and \(l\) into this equation to obtain

\begin{equation} \label{eq:theory:townsend_coefficient} α(T) = \frac{pσ}{kT} \exp\left( - \frac{I}{|\vec{E}|}\frac{pσ}{kT}\right). \end{equation}

With this we have an expression for the temperature and pressure dependency of the first Townsend coefficient. 13 This derivation followed (Scharenberg 2019) based on (Engel and Marton 1965), but see (Aoyama 1985) for a more general treatment. Similarly to diffusion and drift parameters, the first Townsend coefficient can be computed numerically using tools like Magboltz.

Note that (Sauli 2014) introduces the Townsend coefficient as \(α = \frac{1}{λ}\), introducing a specific \(λ\) and \(σ\) referring to the ionization mean free path and ionizing cross sections. This can be misleading as it makes it seem as if the Townsend coefficient is inversely proportional to the temperature. This is of course only the case in the regime where each interaction actually causes another ionization.

The first Townsend coefficient can be used to express the multiplication factor – or gas amplification – used in a detector,

\[ G = e^{α x} \]

as the number increases by \(α\) after each step \(\mathrm{d}x\).

The statistical distribution describing the number of electrons after gas amplification is the Pólya distribution

\begin{equation} \label{eq:theory:polya_distribution} p(x) = \frac{N}{G} \frac{(1 + θ)^{1 + θ}}{Γ(1 + θ)} \left(\frac{x}{G}\right)^θ \exp\left[- \frac{(1 + θ) x}{G}\right] \end{equation}

where \(N\) is a normalization constant, \(θ\) is another parameter performing shaping of the distribution and \(G\) is the effective gas gain. \(Γ\) refers to the gamma function. It is to note that the term "polya distribution" in this context is different from other mathematical definitions, in which polya distribution usually means a negative binomial distribution. The above definition goes back to Alkhazov (Alkhazov 1970) and in slight variations is commonly used. Due to the complexity of this definition, care needs to be taken when performing numerical fits to data with this function (using bounded parameters and preferring more general non-linear optimization routines instead of a normal Levenberg-Marquardt non-linear least squares approach).

Based on eq. \eqref{eq:theory:townsend_coefficient} the largest impacts on the expected gas amplification have the electric field, the choice of gas and the temperature of the gas. While the former two parameters are comparatively easy to control, the temperature in the amplification region may vary and is hard to measure. As such depending on the detector details and application, gas gain variations are expected and corrections based on a running gas gain value may be necessary.

As the large number of interactions in the amplification region can excite many atoms of the (typically) noble gas, UV photons can be produced. Their mean free path is comparatively long relative to the size of the amplification region. They can start further avalanches, potentially away from the location of the initial avalanche start, lowering spatial resolution and increasing apparent primary electron counts. Molecular gases are added – often only in small fractions – to the gas mixture to provide rotational or vibrational modes to dissipate energy without emitting UV photons. In this context the molecular additions are called 'quencher gases'.

6.3.7. Energy resolution

Because of the statistical processes associated with the full detection method used in gaseous detectors, even a perfect delta like signal in energy, will lead to a natural spread of the measured signal. The convolution of different ionization yields, potential losses and gas gain variation all contribute to such a spread.

As such a typical measure of interest for gaseous detectors is the energy resolution, which is commonly defined by

\[ ΔE = \frac{σ}{E} \]

where \(σ\) is the standard deviation of the normal distribution associated with the resolved peak in the detectors data, assuming a – for practical purposes – delta peak as the input spectrum. Sometimes definitions using the full width at half maximum (FWHM) are also used in place of \(σ\). Typical values for the energy resolution defined like this are smaller than \(\SI{15}{\%}\).

If the absolute magnitude of the \(σ\) at a given energy is constant, which at least is partially reasonable as the width is not fully due to energy dependent effects, the energy resolution is proportional to \(1/E\).

The Fano factor (Fano 1963), defined by the variance over the mean of a distribution \(F = σ² / μ\) (typically within some time window), improves the ideal energy resolution. It arises in the associated statistical processes, because there are a finite number of possible interaction states. For X-rays an additional aspect is due to the maximum energy transferable into the gas due to the X-rays energy. In practice though the energy resolution of gaseous detectors is usually limited by other effects.

6.3.7.1. More notes on Fano factor    extended

The Fano factor is related to the fact that the average energy needed to produce an electron-ion pair in each interaction.

(Kolanoski and Wermes 2020) derives the Fano factor on page 783 in sec. 17.10.2. The relationship to the W-value is studied in (Bronic 1992) and mentioned in (Sauli 2014) on page 62.

6.3.8. Escape photons and peaks | 55Fe as a typical calibration source

Finally, gaseous detectors need to be calibrated, monitored and tested. This is commonly done with a \cefe source. \cefe is a radioactive isotope of iron, which decays under inverse beta decay to \(\ce{^{55}Mn}\). Due to the required restructuring of the electronic shells, the manganese is in an excited state. While the emission of an Auger electron with \(\SI{5.19}{keV}\) dominates with a probability of \(\SI{60}{\%}\), as an X-ray source the Kα₁ and Kα₂ lines with combined energies of about \(\SI{5.9}{keV}\) are of note.

When using such a \cefe source as a calibration source for an argon filled gaseous detector, the \(\SI{5.9}{keV}\) photons will produce a photoelectron in argon. Mostly an inner shell electron will be liberated, producing a photoelectron of around \(\SI{2.7}{keV}\). The excited argon atom can now emit an Auger electron in about \(\SI{85}{\%}\) of cases, which will fully deposit its remaining energy into the surrounding gas via further ionizations, resulting in the 'photopeak' at around \(\SI{5.9}{keV}\).

If however another photon is produced with an energy below the \(K 1s\) energy of argon (\(\SI{3.2}{keV}\)) – for example via Kα₁ or Kα₂ fluorescence of argon, both at about \(\SI{2.95}{keV}\) – such a photon has a very long absorption length in the gas volume, about \(l_{\text{abs}} = \SI{3.5}{cm}\) (cf. fig. 1). This can cause it to easily escape the active detection region, especially if the sensitive region of the detector is comparatively small. The result is a measured signal of \(E_i - E_k = \SI{5.9}{keV} - \SI{2.95}{keV} \approx \SI{2.9}{keV}\), called the 'escape peak'. (Sauli 2014; Kolanoski and Wermes 2020; Hubbell and Seltzer 1996)

Such an additional escape peak is useful as a calibration tool, as it gives two distinct peaks in a \cefe calibration spectrum, which can be utilized for an energy calibration. One important consideration of such an escape peak is however, that this escape peak of about \(\SI{3}{keV}\) is not equivalent to a real \(\SI{3}{keV}\) X-ray. As the original X-ray is still the \(\SI{5.9}{keV}\) photon with its distinct absorption length, the geometric properties of ensembles of escape peak events and real \(\SI{3}{keV}\) photons is different. This will be important later in sec. 12.4.7.

Footnotes:

1

Outside of vetoes to tag muons that cause fluorescence for example.

4

Note that there are different common parametrizations of the Bethe-Bloch equation.

5

Energies of muons detected near Earth's surface are in this range precisely because they are MIPs. Below and above the much stronger interactions result either in stopping and decay or loss until they are in the MIP range.

6

The calculation is a "hybrid" Monte Carlo approach. We don't sample a large number of muons and transport them through the atmosphere. We just compute the loss through the atmosphere and allow the muon to decay randomly, dropping a single data point. This is for simplicity on the one hand and the fact that there is a very sharp transition of 'most muons traverse' to 'essentially no muons traverse' the atmosphere at a given energy. For that reason the flux in #fig:theory:muon_flux_surface:final at low energies is to be taken with a grain of salt. The fluxes are literally mappings from data points in #fig:theory:muon_flux_surface:initial to the corresponding energy left at the surface (i.e. assuming no muons decay). Further the \(\SI{300}{MeV}\) energy left is already well inside the energy range where a large number of muons would already decay.

11

Its usefulness depends on the angle from which different aspects are discussed of course. We mainly use it to understand the gas amplification later.

12

Later in chapter 12.4.2 we will discuss Monte Carlo event generation, which use the diffusion coefficient as an input to generate clusters after drift. Distance \(x\) and \(y\) are each sampled individually according to eq. \eqref{eq:gas_physics:diffusion_after_drift} and combined to a radial distance from the cluster center.

13

This will be a useful reference later when discussing possible temperature effects of the detector operated at CAST.

Click on any heading marked 'extended' to open it