Cell-based cluster model

To illustrate an example from a cell model, we will use Model B from Reynolds et al. 2020, “Astrophysical Limits on Very Light Axion-like Particles from Chandra Grating Spectroscopy of NGC 1275”. It is an approximate model for a turbulent field in the Perseus cluster.

The total magnetic field strength at a distance \(z\) along the line of sight is set according to

\[B(z) = 7.5\,\mu{\rm G} \left[ \frac{n_e(z)}{n_e(25\,{\rm kpc})} \right]^{0.5}\]

where the electron density \(n_e\) is set by the analytic profile given by Churazov et al:

\[n_e(z) = \frac{3.9\times10^{-2}}{\left[1+ (z/80\, {\rm kpc})^2\right]^{1.8}} + \frac{4.05\times10^{-3}}{\left[1+ (z/280\, {\rm kpc})^2\right]^{0.87}} ~{\rm cm}^{-3}.\]

The cell sizes \(\Delta z\) are set according to a probability distribution function \(p(\Delta z)\), given by a power-law so that

\[p(\Delta z) \propto \Delta z^{-1.2}\]

spanning \(3.5-10\), kpc. In their model B, Reynolds et al. 2020 scale these minimum and maximum cell sizes linearly with radius as \(z/50{\rm kpc}\), because coherence lengths are expected to grow with distance from the cluster centre.

[1]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import alpro
alpro.util.set_default_plot_params()

First we initialise the survival class and set up the ALP parameters

[2]:
s = alpro.Survival("1275b")
s.init_model()
s.set_params(1e-12 * 1e-9, 1e-13)

We can now initialise a random cell model with a given random number seed and plot \(B_\perp\) as a function of line of sight distance \(z\)

[3]:
Lmax = 1800.0
i = 0
s.domain.create_box_array(Lmax, i, s.coherence_func, r0=0)
plt.step(s.domain.rcen, s.domain.B * 1e6, where="mid")
plt.ylabel("$B_\perp (\mu G)$")
plt.xlabel("$z$ (kpc)")
[3]:
Text(0.5, 0, '$z$ (kpc)')
../_images/examples_cell_5_1.png

Now we can use ALPRO to compute the survival probability

[4]:
energies = np.logspace(3,4,1000)
P, Pradial = s.propagate(s.domain, energies, pol="both")

Let’s plot the survival probability first using the inbuilt method

[5]:
fig = s.default_plot(plot_kwargs = {"lw": 3}, mode="survival")
../_images/examples_cell_9_0.png

We can also plot the radial profile at the highest energy

[6]:
plt.plot(s.domain.rcen, 1.0 - Pradial[:,-1])
plt.semilogx()
[6]:
[]
../_images/examples_cell_11_1.png

We can also plot the radial profile in every energy bin using a colormesh plot

[7]:
plt.pcolormesh(energies,s.domain.rcen, Pradial)
plt.colorbar()
plt.semilogx()
plt.xlabel("$E$ (eV)")
plt.ylabel("$z$ (kpc)")
[7]:
Text(0, 0.5, '$z$ (kpc)')
../_images/examples_cell_13_1.png

 Setting the coherence length / cell size distribution

Model B uses a scaling of the coherence length with radius. This can be modified or turned off via the command set_coherence_r0. Setting it to None turns it off so the minimum and maximum scale lengths are constant

[8]:
s.set_coherence_r0(None)
s.domain.create_box_array(Lmax, i, s.coherence_func, r0=0)
plt.step(s.domain.rcen, s.domain.B * 1e6, where="mid")
plt.ylabel("$B_\perp (\mu G)$")
plt.xlabel("$z$ (kpc)")
[8]:
Text(0.5, 0, '$z$ (kpc)')
../_images/examples_cell_15_1.png

The power-law function can also be customised using the set_coherence_pl function. For example, if we want a PDF of the form \(p(\Delta z) \propto \Delta z^{-3}\) chosen between 50 and 100 kpc, we can do the following:

[9]:
s.set_coherence_pl(n=3, xmin=50.0, xmax=100.0, r0=None)
s.domain.create_box_array(Lmax, i, s.coherence_func, r0=0)
plt.step(s.domain.rcen, s.domain.B * 1e6, where="mid")
plt.ylabel("$B_\perp (\mu G)$")
plt.xlabel("$z$ (kpc)")
[9]:
Text(0.5, 0, '$z$ (kpc)')
../_images/examples_cell_17_1.png

For more control over the distribution of cell sizes, any function that chooses random numbers can be passed to the coherence_func object - a convenient way to do this is using a scipy stats function like rayleigh using the inbuilt rvs method.

[10]:
from scipy.stats import rayleigh
s.coherence_func = rayleigh(50).rvs
s.domain.create_box_array(Lmax, i, s.coherence_func, r0=0)
plt.step(s.domain.rcen, s.domain.B * 1e6, where="mid")
plt.ylabel("$B_\perp (\mu G)$")
plt.xlabel("$z$ (kpc)")
[10]:
Text(0.5, 0, '$z$ (kpc)')
../_images/examples_cell_19_1.png