The Metagalactic X-ray Background¶
In this example, we’ll compute the Meta-Galactic X-ray background over a series of redshifts (\(10 \leq z \leq 40\)):
# Initialize radiation background
pars = \
{
# Source properties
'pop_sfr_model': 'sfrd-func',
'pop_sfrd': lambda z: 0.1,
'pop_sed': 'pl',
'pop_alpha': -1.5,
'pop_Emin': 2e2,
'pop_Emax': 3e4,
'pop_EminNorm': 5e2,
'pop_EmaxNorm': 8e3,
'pop_rad_yield': 2.6e39,
'pop_rad_yield_units': 'erg/s/sfr',
# Solution method
'pop_solve_rte': True,
'tau_redshift_bins': 400,
'initial_redshift': 60.,
'final_redshift': 5.,
}
To summarize these inputs, we’ve got:
A constant SFRD of \(0.1 \ M_{\odot} \ \mathrm{yr}^{-1} \ \mathrm{cMpc}^{-3}\), given by the
pop_sfrd
parameter.A power-law spectrum with index \(\alpha=-1.5\), given by
pop_sed
andpop_alpha
, extending from 0.2 keV to 30 keV.A yield of \(2.6 \times 10^{39} \ \mathrm{erg} \ \mathrm{s}^{-1} \ (M_{\odot} \ \mathrm{yr})^{-1}\) in the \(0.5 \leq h\nu / \mathrm{keV} \leq 8\) band, set by
pop_EminNorm
,pop_EmaxNorm
,pop_yield
, andpop_yield_units
. This is the \(L_X-\mathrm{SFR}\) relation found by Mineo et al. (2012).
See Population Parameters for a complete listing of parameters relevant to ares.populations.GalaxyPopulation
objects.
Now, to initialize a calculation:
import ares
mgb = ares.simulations.MetaGalacticBackground(**pars)
Now, let’s run the thing:
mgb.run()
We’ll pull out the evolution of the background just as we did in the previous example:
z, E, flux = mgb.get_history(flatten=True)
and plot up the result (at the final redshift):
import matplotlib.pyplot as pl
from ares.physics.Constants import erg_per_ev
pl.semilogy(E, flux[0] * E * erg_per_ev, color='k')
pl.xlabel(ares.util.labels['E'])
pl.ylabel(ares.util.labels['flux_E'])
z, E, flux = mgb.get_history(flatten=True)
Compare to the analytic solution, given by Equation A1 in Mirocha (2014) (the cosmologically-limited solution to the radiative transfer equation)
with \(\alpha = -1.5\), \(\beta = 0\), \(z=5\), and \(z_i=60\),
import numpy as np
from ares.physics.Constants import c, ev_per_hz
# Grab the GalaxyPopulation instance
pop = mgb.pops[0]
# Compute cosmologically-limited solution
e_nu = np.array(map(lambda E: pop.Emissivity(10., E), E))
e_nu *= c / 4. / np.pi / pop.cosm.HubbleParameter(5.)
e_nu *= (1. + 5.)**6. / -3.
e_nu *= ((1. + 60.)**-3. - (1. + 5.)**-3.)
e_nu *= ev_per_hz
# Plot it
pl.semilogy(E, e_nu, color='b', ls='--')
Neutral Absorption by the Diffuse IGM¶
The calculation above is basically identical to the optically-thin UV background calculations performed in the previous example, at least in the cases where we neglected any sawtooth effects. While there is no modification to the X-ray background due to resonant absorption in the Lyman series (of Hydrogen or Helium II), bound-free absorption by intergalactic hydrogen and helium atoms acts to harden the spectrum. By default, ARES will not include these effects.
To “turn on” bound-free absorption in the IGM, modify the dictionary of parameters you’ve got already:
pars['tau_approx'] = 'neutral'
Now, initialize and run a new calculation:
mgb2 = ares.simulations.MetaGalacticBackground(**pars)
mgb2.run()
and plot the result on the same axes:
z2, E2, flux2 = mgb2.get_history(flatten=True)
pl.loglog(E2, flux2[0] * E2 * erg_per_ev, color='k', ls=':')
pl.savefig('ares_crte_xr.png')
The behavior at low photon energies (\(h\nu \lesssim 0.3 \ \mathrm{keV}\))
is an artifact that arises due to poor redshift resolution. This is a trade
made for speed in solving the cosmological radiative transfer equation,
discussed in detail in Section 3 of Mirocha (2014). For more accurate
calculations, you must enhance the redshift sampling using the tau_redshift_bins
parameter, e.g.,
pars['tau_redshift_bins'] = 500
The optical depth lookup tables that ship with ARES use tau_redshift_bins=400
as a default. If you run with tau_redshift_bins=500
, you should see some improvement in the soft X-ray spectrum. It’ll take a few minutes to generate a new table. Run $ARES/input/optical_depth/generate_optical_depth_tables.py
to make more!
Alternative Methods¶
The technique outlined above is the fastest way to integrate the cosmological radiative transfer equation (RTE), but it assumes that we can tabulate the optical depth ahead of time. What if instead we wanted to study the radiation background in a decreasingly opaque IGM? Well, we can solve the RTE at several photon energies in turn:
E = np.logspace(2.5, 4.5, 100)
To determine the background intensity at \(z=10\) due to the same BH population as above, we could do something like:
# Function describing evolution of IGM ionized fraction with respect to redshift
# (fully ionized for all time in this case, meaning IGM is optically thin)
xofz = lambda z: 1.0
# Compute flux at z=10 and each observed energy due to emission from
# sources at 10 <= z <= 20.
F = [rad.AngleAveragedFlux(10., nrg, zf=20., xavg=xofz) for nrg in E]
pl.loglog(E, F)
You’ll notice that computing the background intensity is much slower when we do not pre-compute the IGM optical depth.
Let’s compare this to an IGM with evolving ionized fraction:
# Here's a function describing the ionization evolution for a scenario
# in which reionization is halfway done at z=10 and somewhat extended.
xofz2 = lambda z: ares.util.xHII_tanh(z, zr=10., dz=4.)
# Compute fluxes
F2 = [rad.AngleAveragedFlux(10., nrg, zf=20., xavg=xofz2) for nrg in E]
# Plot results
pl.loglog(E, F2)
# Add some nice axes labels
pl.xlabel(ares.util.labels['E'])
pl.ylabel(ares.util.labels['flux'])
Notice how the plot of F2
has been hardened by neutral absorption in the IGM!
Self-Consistent Meta-Galactic Background & IGM¶
If we don’t already know the IGM optical depth a-priori, then the calculations above will only bracket the result expected in a more complex, evolving IGM, in which the radiation background ionizes the IGM, thus making the IGM more transparent, which then softens the meta-galactic background, and so on. A dynamic background calculator that takes this into account is on the ARES wish-list – shoot me an email if you’re so inclined.