Post

ALP-aca v1.1

ALP-aca v1.1

We have released the vesrion v1.1 of ALP-aca, with some exciting new features!

Projections for displaced ALP searches in LHCb with Downstream tracks

The new Downstream algorithm [Comput.Softw.Big Sci. 9 (2025) 1, 10] at LHCb allows to reconstruct tracks that do not start at the VELO, which can improve the sensitivity to long-lived particles such as ALPs [Eur.Phys.J.C 84 (2024) 6, 608]. We have performed sensitivity studies using SensCalc to obtains projections for the bounds of different processes as a function of the mass and proper lifetime of the ALP:

  • $K_S^0 \to \pi^0 a (\to e^+ e^-)$
  • $K_S^0 \to \pi^0 a (\to \mu^+ \mu^-)$
  • $B^+ \to K^+ a (\to e^+ e^-)$
  • $B^+ \to K^+ a (\to \mu^+ \mu^-)$
  • $B^+ \to K^+ a (\to \gamma \pi^+ \pi^-)$
  • $B^+ \to K^+ a (\to \pi^0 \pi^+ \pi^-)$
  • $B^0 \to K^{*0} a (\to e^+ e^-)$
  • $B^0 \to K^{*0} a (\to \mu^+ \mu^-)$
  • $B^0 \to K^{*0} a (\to \gamma \pi^+ \pi^-)$
  • $B^0 \to K^{*0} a (\to \pi^0 \pi^+ \pi^-)$
  • $D_s^+ \to K^+ a (\to \mu^+ \mu^-)$

Additionally, we also have projections for T tracks, that is, tracks that are reconstructed using the SciFi detectors:

  • $K_S^0 \to \pi^0 a (\to e^+ e^-)$
  • $K_S^0 \to \pi^0 a (\to \mu^+ \mu^-)$
  • $B^+ \to K^+ a (\to e^+ e^-)$
  • $B^+ \to K^+ a (\to \mu^+ \mu^-)$
  • $B^0 \to K^{*0} a (\to e^+ e^-)$
  • $B^0 \to K^{*0} a (\to \mu^+ \mu^-)$

Since they are projections, they are only used for the analysis if the argument exclude_projections=False is introduced in get_chi2()

New recast of $B^+ \to K^+ \nu \bar{\nu}$ at Belle II

The Belle II experiment found an excess of events in $B^+ \to K^+ \nu\bar\nu$, which can be interpreted as a two-body decay $B^+ \to K^+ a$ where the ALP escapes the detector with $m_a \sim 2\mathrm{GeV}$. A new theoretical analysis, arXiv:2510.18953 [hep-ph], has refined the reconstruction of ITA events, and showed that it is possible to disentangle new physics effects in the two-body and three-body processes.

“BC” benchmarks

We have implemented the three ALP portal benchmarks defined in [J.Phys.G 47 (2020) 1, 010501]

  • BC9 (photo-phillic ALP):
\[\mathcal{L}_\mathrm{BC9} (\Lambda = 1\mathrm{TeV}) = \frac{g_{a\gamma\gamma}}{4} a F_{\mu\nu}\tilde{F}^{\mu\nu}\]

Note that at the scale $\Lambda = 1\mathrm{TeV}$, electroweak symmetry is restored, and thus it is not possible to generate ALP couplings exclusively to photons. We have chosen to approximate the BC9 benchmark as a $U(1)_Y$-phillic scenario with

\[c_B(\Lambda) = \frac{\pi}{\alpha_\mathrm{em}}g_{a\gamma\gamma} f_a\]

which in turn also describes interactions of the ALP with to $Z$ bosons and with one photon and one $Z$:

\[g_{aZZ} = \frac{s_w^2}{c_w^2} g_{a\gamma\gamma}\qquad g_{a\gamma Z} = -2\frac{s_w}{c_w} g_{a\gamma\gamma}\]
  • BC10 (fermion-phillic ALP):
\[\mathcal{L}_\mathrm{BC10} (\Lambda = 1\mathrm{TeV}) = \frac{g_Y}{2v}\partial_\mu a \sum_f \bar{f}\gamma^\mu \gamma_5 f\]

where the matching condition is

\[c_{f_R}^{ij}(\Lambda) = - c_{f_L}^{ij}(\Lambda) = \frac{g_Y}{2}\frac{f_a}{v}\delta^{ij}\]

Note that, even though the couplings are purely axial at the scale $\Lambda$, the runnning of the left-handed and right-handed couplings is different above the electroweak scale, and consequently a small vectorial coupling will be generated.

  • BC11 (gluon-phllic ALP):
\[\mathcal{L}_\mathrm{BC11} (\Lambda = 1\mathrm{TeV}) = \frac{1}{4f_G} a G_{\mu\nu}^a\tilde{G}^{\mu\nu,a}\]

The matching condition is

\[c_G(\Lambda) = \frac{\pi}{\alpha_s}\frac{f_a}{f_G}\]

These benchmarks are implemented as alpaca.benchmarks.BC9(), alpaca.benchmarks.BC10() and alpaca.benchmarks.BC11() respectively. Each benchmark takes two arguments, the coupling ($g_{a\gamma\gamma}$ in $\mathrm{GeV}^{-1}$, $g_Y$ is dimensionless and $f_G$ in $\mathrm{GeV}$) and $f_a$, and returns the ALPcouplings at the scale $\Lambda=1\mathrm{TeV}$:

1
2
3
from alpaca.benchmarks import BC10

bc10_coupling = BC10()(gY = 1e-3, fa=1e3)

Scans over parameter space

The new module alpaca.scan makes 2-dimensional scans over the ALP parameter space even easier!

The scan is performed over objects defined by the class alpaca.scan.Axis. There are three types of Axis:

  • Main axis: Each scan must contain exactly one main x axis and one y axis, which define the size of the scanning grid.
  • Functional axis: The values of this axis are calculated as a function (e. g. a Python lambda) of the corresponding main axis.
  • Dependent axis: The user can provide any arbitrary value for this axis, as long as it has the same dimension as the corresponding main axis.

The Axis object takes the following arguments:

  • values: For main and dependent axis, a list or numpy array of numerical values (typically created with np.linspace or np.logspace). For functional axis, a function that takes a float as argument and returns another float.
  • axis: For main axes either x or y. For functional axes, either x_func or y_func. For dependent axes, either x_dep or y_dep.
  • tex: $\TeX$ string to be used as the label of the axis when plotted.
  • name and units: Strings for the name and units of the axis. At the moment, only used by interactive plots (see below).

As an example, the following code defines a main axis corresponding to $f_a$ ranging from $10^3\mathrm{GeV}$ to $10^8\mathrm{GeV}$, and a functional axis for the UV matching scale $\Lambda = 4 \pi f_a$:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from alpaca.scan import Axis
import numpy as np

ax_fa = Axis(
  values = np.logspace(3, 8, 100),
  axis = 'y',
  tex = r'$f_a\ [\mathrm{GeV}]$',
  name = r'f<sub>a</sub>',
  units = 'GeV'
)

ax_lambda = Axis(
  values = lambda fa: 4*np.pi*fa,
  axis = 'y_func',
  tex = r'$\Lambda\ [\mathrm{GeV}]$',
  name = '&#923;',
  units = 'GeV'
)

Now we are ready to define our scan with alpaca.scan.Scan. Its arguments are:

  • model: An object of either alpaca.uvmodels.ModelBase (or its subclasses), or of alpaca.ALPcouplings, or alpaca.benchmarks.Benchmark (for the new BC benchmarks). For the UV models and benchmarks, it is possible to scan over the parameters of the model.
  • ma: Mass of the ALP, in GeV. It can be a float (if the mass is fixed for the scan) or an Axis.
  • fa: $f_a$ of the ALP, in GeV. It can be a float (if it is fixed for the scan) or an Axis. It is ignored for benchmarks.
  • lambda_scale: Matching scale between the UV theory and the ALP-EFT, in GeV (only for UV models). It can be a float (if it is fixed for the scan) or an Axis.
  • mu_scale: Final scale for the running of the ALP couplings, in GeV. If set to None (default value), the running is not pre-computed, and will be performed automatically when calculating observables. It can be a float (if it is fixed for the scan) or an Axis.
  • brdark: Model-dependent branching ratio of the ALP decays into dark sectors, by default set to 0. It can be a float (if it is fixed for the scan) or an Axis.
  • model_pars: Dictionary that contains the values of the parameters for the UV model. The keys are the model parameters (as obtained e.g. by the method .model_parameters() of each model, or gagg for BC9, gY for BC10, fG for BC11), and the corresponding values are a float (if it is fixed for the scan) or an Axis.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from alpaca.scan import Axis, Scan
from alpaca.uvmodels import QED_DFSZ
import numpy as np

ax_ma = Axis(
  values = np.logspace(-2, 1, 100),
  axis = 'x',
  tex = r'$m_a\ [\mathrm{GeV}]$',
  name = r'm<sub>a</sub>',
  units = 'GeV'
)

scan_qeddfsz = Scan(
  model = QED_DFSZ,
  ma = ax_ma,
  fa = ax_fa,
  lambda_scale = ax_lambda,
  mu_scale = 10.1,
  model_pars = {QED_DFSZ.model_pars()[0]: np.arctan(1)}
)

The method .compute_grid() of the scan obtains the ALPcouplings for all points of the grid, performing their running if necessary. The values of the couplings are stored, so future calls to .compute_grid() do not compute them again.

The scan also has the following methods:

  • .decay_width
  • .branching_ratio
  • .cross_section
  • .meson_mixing
  • .alp_channels_decay_widths
  • .alp_channels_branching_ratios
  • .get_chi2

They work like their usual conterparts, except they do not take the arguments for ma, couplings, fa and br_dark. So, for example, if we want to use the previously defined Scan to compute the values of $\mathrm{BR}(B^+\to K^+ \mu^+ \mu^-)$ at each point of the grid:

1
br_bkmumu = scan_qeddfsz.branching_ratio('B+ -> K+ mu mu', integrator='no_rge')

Axis objects can also be used to specify the $x$ and $y$ values in exclusionplot and $x$ values in alp_channels_plot.

Interactive plots

Is your plot too cluttered and do you want to hide some of the bounds? Do you want to zoom in a region of the parameter space? Or simply make your talks more lively? We introduce interactive plots powered by plotly!

1
from alpaca.plotting.plotly import exclusionplot

The syntax is the same as the matplotlib version of the plots, but with new optional arguments: xvar, yvar, xunits and yunits. These are four strings that contain the name and units for the x and y coordinates when hovering over the plot. Sadly, latex is not supported for this hover text, but some HTML formatting is possible (e.g. superindices with <sup> </sup>, subindices with <sub> </sub>, and greek characters with unicode, like &#946; for β).

(Note: on some notebooks, for example in VSCode, it might be necessary to run some initialization routines contained in alpaca.plotting.plotly.prepare_nb() to properly display the interactive plots)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from alpaca.plotting.plotly import exclusionplot, prepare_nb

prepare_nb()

fig = exclusionplot(
  x_tanbeta,
  1/y_fa,
  chi2_global,
  r'$\tan\beta$',
  r'$1/f_a\ [\mathrm{GeV}^{-1}]$',
  'QED-DFSZ ALP',
  xvar = r'tan &#946;',
  yvar = r'1/f<sub>a</sub>',
  yunits = r'GeV<sup>-1</sup>'
)

fig.show()

The figure can be exported to html as

1
fig.write_html('qed_dfsz.html', include_mathjax='cdn', include_plotlyjs='cdn', full_html=True)

The two “include” options are used to load the required javascript libraries from the web. Changing to True instead of cdn includes the libraries inside the html output, increasing significantly its file size. If full_html = False, the output is just a <div> HTML element that can be embedded in larger files.

It is also possible to use ALP-aca to generate HTML pages with templates

1
2
3
from alpaca.plotting.plotly import save_html

save_html(fig, 'qed_dfsz.html', title = 'Web page title', template='basic')

At the moment, only the basic template is available.

Installation of optional dependencies

The plotting backends, matplotlib and plotly, are not included as depencencies of ALP-aca, but now they can be installed as optional dependencies. To install with matplotlib

1
pip3 install alpaca-ALPs[matplotlib]

with plotly

1
pip3 install alpaca-ALPs[plotly]

and with both

1
pip3 install alpaca-ALPs[matplotlib,plotly]
This post is licensed under CC BY 4.0 by the author.