{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Example 5: Inverse Compton Scattering"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "DarkHistory comes with the module [*darkhistory.electrons.ics*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/electrons/darkhistory.electrons.ics.html) to compute the inverse Compton scattering (ICS) scattered photon spectrum in the Thomson limit and in the relativistic limit, as well as the scattered electron energy-loss spectrum, which can be used to obtain the scattered electron spectrum. See our paper for more details and definitions for these terms. \n",
    "\n",
    "All of our calculations assume ICS off a **blackbody spectrum**. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Initialization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "import sys\n",
    "sys.path.append(\"..\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%autoreload\n",
    "\n",
    "import matplotlib\n",
    "matplotlib.rc_file('matplotlibrc')\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from matplotlib.colors import LogNorm\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "import darkhistory.physics as phys\n",
    "import darkhistory.utilities as utils\n",
    "import darkhistory.spec.spectools as spectools\n",
    "import darkhistory.spec.transferfunction as tf\n",
    "from darkhistory.spec.spectrum import Spectrum\n",
    "\n",
    "from darkhistory.electrons.ics.ics_spectrum import ics_spec\n",
    "from darkhistory.electrons.ics.ics_spectrum import thomson_spec\n",
    "from darkhistory.electrons.ics.ics_spectrum import rel_spec\n",
    "from darkhistory.electrons.ics.ics_engloss_spectrum import engloss_spec\n",
    "from darkhistory.electrons.ics.ics_cooling import get_ics_cooling_tf\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Computing the ICS Spectra"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we begin, the user should note that DarkHistory provides the transfer functions for inverse Compton scattering off the CMB for immediate use with the default electron and photon binning for spectra found in [*config*](https://darkhistory.readthedocs.io/en/latest/_autosummary/config.html). The user will not have to worry about computing ICS spectra separately if this is the only way in which the ICS results are used. \n",
    "\n",
    "However, some users may want to specifically use our ICS code to compute their own spectra, with their own binning in electron and photon energies. This example will illustrate how to do that. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scattered Photon Spectrum - Thomson Regime"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, we'll compute the scattered photon spectrum in the Thomson regime.  ICS occurs in the Thomson regime when\n",
    "\n",
    "$$ \\frac{4\\epsilon \\gamma_e}{m_e} \\ll 1 \\,,$$\n",
    "\n",
    "where $\\epsilon$ is the initial energy of the photon, $\\gamma_e$ is the Lorentz boost of the injected electron, and $m_e$ is the incoming mass. For the range of redshifts we consider in DarkHistory $1+z < 3000$, $\\gamma_e \\lesssim 10^4$ can be considered to lie well within the Thomson regime. Note that the Thomson and relativistic regime (where $\\gamma_e \\gg 1$) have a large overlap, and calculations in both regimes should yield almost identical results.\n",
    "\n",
    "The relevant function that we will call is [*ics.thomson_spec()*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/electrons/ics/ics_spectrum/darkhistory.electrons.ics.ics_spectrum.thomson_spec.html). This function takes an abscissa for the kinetic energy of electrons, the energy of photons, and the temperature at which we want the spectrum.\n",
    "\n",
    "The successful calculation of the spectrum looks like this:\n",
    "\n",
    "```\n",
    "Initializing...\n",
    "***** Computing Spectra by Expansion in beta ...... Complete! *****\n",
    "***** Computing Spectra by Analytic Series... *****\n",
    "Series 1/12...\n",
    "Series 2/12...\n",
    "Series 3/12...\n",
    "Series 4/12...\n",
    "Series 5/12...\n",
    "Series 6/12...\n",
    "Series 7/12...\n",
    "Series 8/12...\n",
    "Series 9/12...\n",
    "Series 10/12...\n",
    "Series 11/12...\n",
    "Series 12/12...\n",
    "***** Analytic Series Computation Complete! *****\n",
    "########### Spectrum computed! ###########\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nEe = 500\n",
    "nEp = 500\n",
    "\n",
    "Emax = 1.1e10\n",
    "Emin = 0.9e-8\n",
    "\n",
    "dlnEp = np.log(Emax/Emin)/nEp\n",
    "lowengEp = Emin*np.exp((np.arange(nEp)+0.5)*dlnEp)        \n",
    "\n",
    "dlnEe = np.log(Emax/Emin)/nEe\n",
    "lowengEe = Emin*np.exp((np.arange(nEe)+0.5)*dlnEe)\n",
    "\n",
    "ics_thomson_tf = thomson_spec(lowengEe, lowengEp, 0.25)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's make a plot of the spectrum produced. The matrix corresponding to the transfer function can be accessed directly by the attribute [*grid_vals*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/spec/transferfunction/darkhistory.spec.transferfunction.TransFuncAtRedshift.html), and is indexed by (``eleceng``, ``photeng``). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7.8, 6.2))\n",
    "\n",
    "plt.contourf(\n",
    "    lowengEe, lowengEp, np.transpose(ics_thomson_tf.grid_vals), \n",
    "    levels=10.**np.array([-25, -22, -18, -15, -12, -10, -8, -6, -4, -2, 0]), cmap = 'inferno',\n",
    "    norm = LogNorm()\n",
    "            )\n",
    "\n",
    "plt.colorbar(label=r'$\\frac{dN_\\gamma}{dE_\\gamma \\, dt}$ [eV$^{-1}$ s$^{-1}$ ]')\n",
    "ax = plt.gca()\n",
    "\n",
    "ax.set_xscale('log')\n",
    "ax.set_yscale('log')\n",
    "\n",
    "plt.title(r'\\textbf{ICS Scattered Photon Spectrum}')\n",
    "plt.xlabel('Electron Kinetic Energy [eV]')\n",
    "plt.ylabel('Scattered Photon Energy [eV]')\n",
    "\n",
    "plt.text(1e-7, 3e13, 'Thomson Regime', fontsize=20)\n",
    "plt.text(1e-7, 2e11, r'$T_{\\mathrm{CMB}} = $ 0.25 eV', fontsize=20)\n",
    "\n",
    "plt.axis([1e-8, 1e8, 1e-8, 1e16])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scattered Photon Spectrum - Relativistic Regime"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also get the spectrum in the relativistic regime, i.e. when $\\gamma_e \\gg 1$. In DarkHistory, the transition occurs by default at $\\gamma_e = 20$. The function that does this is [*ics.rel_spec()*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/electrons/ics/ics_spectrum/darkhistory.electrons.ics.ics_spectrum.rel_spec.html). Note that this function takes the *total electron energy* as input, rather than the kinetic energy. The inputs are otherwise the same as for `thomson_spec()`.\n",
    "\n",
    "`rel_spec` also has a flag `inf_upp_bound`, which determines the level of approximation to apply to the spectrum. If `inf_upp_bound` is True, only downscattering of electrons is included among other approximations, and returns a spectrum that is unreliable below a final photon energy of $E_1 < T_\\text{CMB}$. However, this part of the spectrum accounts for a minute portion of the total energy deposited into photons, and the produced transfer function can be reused for different redshifts. \n",
    "\n",
    "With `inf_upp_bound` set to False, upscattering of electrons is included, and the spectrum is accurate up to $\\mathcal{O}(1/\\gamma^2)$ terms. \n",
    "\n",
    "The successful execution of this calculation will produce the following printout:\n",
    "\n",
    "```\n",
    "Initializing...\n",
    "Computing series 1/4...\n",
    "Computing series 2/4...\n",
    "Computing series 3/4...\n",
    "Computing series 4/4...\n",
    "Relativistic Computation Complete!\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Emax_rel = 1e20\n",
    "Emin_rel = 1e-8\n",
    "nEe = 500\n",
    "nEp  = 500\n",
    "\n",
    "dlnEp = np.log(Emax_rel/Emin_rel)/nEp\n",
    "lowengEp_rel = Emin_rel*np.exp((np.arange(nEp)+0.5)*dlnEp)        \n",
    "\n",
    "dlnEe = np.log(Emax_rel/Emin_rel)/nEe\n",
    "lowengEe_rel = Emin_rel*np.exp((np.arange(nEe)+0.5)*dlnEe)\n",
    "\n",
    "ics_rel_tf = rel_spec(phys.me+lowengEe_rel, lowengEp_rel, 0.25, inf_upp_bound=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now we'll plot the spectrum. Observe that the relativistic spectrum at the low energy end of this plot is exactly the same as the Thomson spectrum at the same energy: we have chosen parameters such that both the Thomson and relativistic approximations are valid in this region."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7.8, 6.2))\n",
    "\n",
    "plt.contourf(\n",
    "    phys.me+lowengEe_rel, lowengEp_rel, np.transpose(ics_rel_tf.grid_vals), \n",
    "    levels=10.**np.array([-25, -22, -18,-15, -12, -10, -8, -6, -4, -2, 0]), cmap = 'inferno',\n",
    "    norm = LogNorm()\n",
    "            )\n",
    "\n",
    "plt.colorbar(label=r'$\\frac{dN_\\gamma}{dE_\\gamma \\, dt}$ [eV$^{-1}$ s$^{-1}$]')\n",
    "ax = plt.gca()\n",
    "\n",
    "ax.set_xscale('log')\n",
    "ax.set_yscale('log')\n",
    "\n",
    "plt.title(r'\\textbf{ICS Scattered Photon Spectrum}')\n",
    "plt.xlabel('Electron Kinetic Energy [eV]')\n",
    "plt.ylabel('Scattered Photon Energy [eV]')\n",
    "\n",
    "plt.text(3e8, 3e13, 'Relativistic Regime', fontsize=20)\n",
    "plt.text(3e8, 2e11, r'$T_{\\mathrm{CMB}} = $ 0.25 eV', fontsize=20)\n",
    "\n",
    "plt.axis([1e8, 1e14, 1e-8, 1e16])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scattered Photon Spectrum - All Regimes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The user may simply use [*ics.ics_spec()*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/electrons/ics/ics_spectrum/darkhistory.electrons.ics.ics_spectrum.ics_spec.html) to switch between the two regimes automatically. There are two ways to use this function: either by passing it the electron kinetic energy and photon energy abscissae, or by passing it transfer functions, over which a very fast interpolation can be done to get the spectrum at other temperatures. \n",
    "\n",
    "Here, let's use `ics.ics_spec` to calculate the spectrum from scratch. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Emax = 1e14\n",
    "Emin = 1e-8\n",
    "nEe = 500\n",
    "nEp  = 500\n",
    "\n",
    "dlnEp = np.log(Emax/Emin)/nEp\n",
    "Ep = Emin*np.exp((np.arange(nEp)+0.5)*dlnEp)        \n",
    "\n",
    "dlnEe = np.log(Emax/Emin)/nEe\n",
    "Ee = Emin*np.exp((np.arange(nEe)+0.5)*dlnEe)\n",
    "\n",
    "ics_tf = ics_spec(Ee, Ep, 0.25, inf_upp_bound=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now let's make the plot! Observe the smooth transition at $\\gamma_e = 20 \\sim $ 10 MeV in electron energy. We've  demonstrated a way of obtaining the ICS scattering spectrum of electrons off the CMB over a wide range of regimes. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7.8, 6.2))\n",
    "\n",
    "plt.contourf(\n",
    "    Ee, Ep, np.transpose(ics_tf.grid_vals), \n",
    "    levels=10.**np.array([-25, -22, -18,-15, -12, -10, -8, -6, -4, -2, 0]), cmap = 'inferno',\n",
    "    norm = LogNorm()\n",
    "            )\n",
    "\n",
    "plt.axvline(19*phys.me, color='gray', linestyle=':')\n",
    "\n",
    "plt.colorbar(label=r'$\\frac{dN_\\gamma}{dE_\\gamma \\, dt}$ [eV$^{-1}$ s$^{-1}$]')\n",
    "ax = plt.gca()\n",
    "\n",
    "ax.set_xscale('log')\n",
    "ax.set_yscale('log')\n",
    "\n",
    "plt.title(r'\\textbf{ICS Scattered Photon Spectrum}')\n",
    "plt.xlabel('Electron Kinetic Energy [eV]')\n",
    "plt.ylabel('Scattered Photon Energy [eV]')\n",
    "\n",
    "plt.text(3e-7, 3e13, r'$T_{\\mathrm{CMB}} = $ 0.25 eV', fontsize=20)\n",
    "plt.text(5e5, 3e8, 'Transition', fontsize=20, rotation=90, color='gray')\n",
    "\n",
    "plt.axis([1e-8, 1e14, 1e-8, 1e16])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The other way to use `ics_spec()` is to pass it two transfer functions, one corresponding to a Thomson regime transfer function, the other a relativistic regime transfer function, both evaluated at some reference temperature $T_\\text{ref}$. We can now interpolate over these two transfer functions to obtain the spectrum at different temperatures. First, we need to produce the relativistic regime transfer function with `inf_upp_bound` set to True. This is the correct setting for using the transfer function for interpolation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Emax_rel = 1e20\n",
    "Emin_rel = 1e-8\n",
    "nEe = 500\n",
    "nEp  = 500\n",
    "\n",
    "dlnEp = np.log(Emax_rel/Emin_rel)/nEp\n",
    "lowengEp_rel = Emin_rel*np.exp((np.arange(nEp)+0.5)*dlnEp)        \n",
    "\n",
    "dlnEe = np.log(Emax_rel/Emin_rel)/nEe\n",
    "lowengEe_rel = Emin_rel*np.exp((np.arange(nEe)+0.5)*dlnEe)\n",
    "\n",
    "ics_rel_interp_tf = rel_spec(phys.me+lowengEe_rel, lowengEp_rel, 0.25, inf_upp_bound=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now here's the code to get the ICS spectrum at $T = 10^{-2}$ eV from the transfer functions that we have already evaluated at $T = 0.25 $ eV. It's important that the transfer functions are evaluated over a large enough range of electron and photon energies. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Emax = 1e20\n",
    "Emin = 1e-5\n",
    "nEe = 500\n",
    "nEp  = 500\n",
    "\n",
    "dlnEp = np.log(Emax/Emin)/nEp\n",
    "Ep = Emin*np.exp((np.arange(nEp)+0.5)*dlnEp)        \n",
    "\n",
    "dlnEe = np.log(Emax/Emin)/nEe\n",
    "Ee = Emin*np.exp((np.arange(nEe)+0.5)*dlnEe)\n",
    "\n",
    "# The function arguments are \n",
    "#(electron kinetic energy, photon energy, temperature, Thomson transfer function,\n",
    "# relativistic transfer function, reference temperature)\n",
    "\n",
    "ics_interp_tf = ics_spec(Ee, Ep, 1e-2, thomson_tf = ics_thomson_tf, rel_tf = ics_rel_interp_tf, T_ref = 0.25)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And the plot of the spectra at $T = 10^{-2}$ eV. Notice the poor transition at the low end of the spectrum. This however does not affect the bulk of the spectrum (as measured in energy). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7.8, 6.2))\n",
    "\n",
    "plt.contourf(\n",
    "    Ee, Ep, np.transpose(ics_interp_tf.grid_vals), \n",
    "    levels=10.**np.array([-25, -22, -18,-15, -12, -10, -8, -6, -4, -2, 0]), cmap = 'inferno',\n",
    "    norm = LogNorm()\n",
    "            )\n",
    "\n",
    "plt.axvline(19*phys.me, color='gray', linestyle=':')\n",
    "\n",
    "plt.colorbar(label=r'$\\frac{dN_\\gamma}{dE_\\gamma \\, dt}$ [eV$^{-1}$ s$^{-1}$]')\n",
    "ax = plt.gca()\n",
    "\n",
    "ax.set_xscale('log')\n",
    "ax.set_yscale('log')\n",
    "\n",
    "plt.title(r'\\textbf{ICS Scattered Photon Spectrum}')\n",
    "plt.xlabel('Electron Kinetic Energy [eV]')\n",
    "plt.ylabel('Scattered Photon Energy [eV]')\n",
    "\n",
    "plt.text(1e-4, 3e13, r'$T_{\\mathrm{CMB}} = $ 0.25 eV', fontsize=20)\n",
    "plt.text(5e5, 3e8, 'Transition', fontsize=20, rotation=90, color='gray')\n",
    "\n",
    "plt.axis([1e-5, 1e14, 1e-5, 1e16])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Mean Electron Energy Loss Spectrum"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also compute the scattered electron net energy loss spectrum in a similar fashion. The function of interest is [*ics.ics_engloss_spectrum.engloss_spec()*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/electrons/ics/ics_engloss_spectrum/darkhistory.electrons.ics.ics_engloss_spectrum.engloss_spec.html), and works in a very similar way, although the calculation is more complicated. A successful execution will print the following:\n",
    "\n",
    "```\n",
    "###### THOMSON ENERGY LOSS SPECTRUM ######\n",
    "****** Energy Loss Spectrum by beta Expansion ******\n",
    "    Computing integrals 1/6...\n",
    "    Computing integrals 2/6...\n",
    "    Computing integrals 3/6...\n",
    "    Computing integrals 4/6...\n",
    "    Computing integrals 5/6...\n",
    "    Computing integrals 6/6...\n",
    "    Integrals computed!\n",
    "****** Complete! ******\n",
    "****** Energy Loss Spectrum by Analytic Series ******\n",
    "Computing upscattering loss spectra...\n",
    "    Computing series 1/8...\n",
    "    Computing series 2/8...\n",
    "    Computing series 3/8...\n",
    "    Computing series 4/8...\n",
    "    Computing series 5/8...\n",
    "    Computing series 6/8...\n",
    "    Computing series 7/8...\n",
    "    Computing series 8/8...\n",
    "Computing downscattering loss spectra...\n",
    "    Computing series 1/8...\n",
    "    Computing series 2/8...\n",
    "    Computing series 3/8...\n",
    "    Computing series 4/8...\n",
    "    Computing series 5/8...\n",
    "    Computing series 6/8...\n",
    "    Computing series 7/8...\n",
    "    Computing series 8/8...\n",
    "****** Complete! ******\n",
    "###### COMPLETE! ######\n",
    "```\n",
    "\n",
    "We can choose to use the Thomson expressions to compute the energy loss transfer function for all electron energies, by setting the flag `thomson_only` to True. If this flag is set to False, then as previously we switch into the relativistic regime for  $\\gamma_e > 20$, and in this regime we take the energy loss spectrum to be identical to the scattered photon spectrum, since the photons are upscattered to an energy much greater than their initial energy. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Emax = 1e10\n",
    "Emin = 1e-8\n",
    "nEe = 500\n",
    "nEp  = 500\n",
    "\n",
    "dlnEp = np.log(Emax/Emin)/nEp\n",
    "Ep = Emin*np.exp((np.arange(nEp)+0.5)*dlnEp)        \n",
    "\n",
    "dlnEe = np.log(Emax/Emin)/nEe\n",
    "Ee = Emin*np.exp((np.arange(nEe)+0.5)*dlnEe)\n",
    "\n",
    "ics_engloss_tf = engloss_spec(Ee, Ep, 0.25, thomson_only=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And the plot:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7.8, 6.2))\n",
    "\n",
    "plt.contourf(\n",
    "    Ee, Ep, np.transpose(ics_engloss_tf.grid_vals), \n",
    "    levels=10.**np.array([-18, -12, -10, -8, -6, -4, -2, 0]), cmap = 'inferno',\n",
    "    norm = LogNorm()\n",
    "            )\n",
    "\n",
    "plt.colorbar(label=r'$\\frac{dN_e}{d\\Delta \\, dt}$ [eV$^{-1}$ s$^{-1}$]')\n",
    "ax = plt.gca()\n",
    "\n",
    "ax.set_xscale('log')\n",
    "ax.set_yscale('log')\n",
    "\n",
    "plt.title(r'\\textbf{ICS Electron Net Energy Loss Spectrum}')\n",
    "plt.xlabel(r'Electron Kinetic Energy [eV]')\n",
    "plt.ylabel('Net Energy Loss $\\Delta$ [eV]')\n",
    "\n",
    "plt.text(1e-7, 3e8, 'Thomson Regime', fontsize=20)\n",
    "plt.text(1e-7, 1e7, r'$T_{\\mathrm{CMB}} = $ 0.25 eV', fontsize=20)\n",
    "\n",
    "plt.axis([1e-8, 1e10, 1e-8, 1e10])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Secondary Photon Spectrum"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With these scattered photon transfer functions, we can now compute the secondary photon spectrum transfer function, which takes injected electrons through its complete cooling process, and produces a final secondary photon spectrum and low energy (sub 3 keV) electron spectrum. This is computed through the function [*electrons.ics.ics_cooling.get_ics_cooling_tf()*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/electrons/ics/ics_cooling/darkhistory.electrons.ics.ics_cooling.get_ics_cooling_tf.html). \n",
    "\n",
    "We will perform this computation by interpolation of a reference transfer function, evaluated at a temperature of $400 T_{\\mathrm{CMB},0}$. We first define the required energy binning:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Emax = 1e20\n",
    "Emin = 1e-8\n",
    "nEe = 500\n",
    "nEp  = 500\n",
    "\n",
    "dlnEp = np.log(Emax/Emin)/nEp\n",
    "lowengEp_rel = Emin*np.exp((np.arange(nEp)+0.5)*dlnEp)        \n",
    "\n",
    "dlnEe = np.log(Emax/Emin)/nEe\n",
    "lowengEe_rel = Emin*np.exp((np.arange(nEe)+0.5)*dlnEe)\n",
    "\n",
    "Emax = 1e10\n",
    "Emin = 1e-8\n",
    "nEe = 500\n",
    "nEp  = 500\n",
    "\n",
    "dlnEp = np.log(Emax/Emin)/nEp\n",
    "lowengEp_nonrel = Emin*np.exp((np.arange(nEp)+0.5)*dlnEp)  \n",
    "\n",
    "dlnEe = np.log(Emax/Emin)/nEe\n",
    "lowengEe_nonrel = Emin*np.exp((np.arange(nEe)+0.5)*dlnEe)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we compute all of the reference scattered photon spectra (Thomson and relativistic) and the energy loss spectrum, which will be used as the baseline for the interpolation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('********* Thomson regime scattered photon spectrum *********')\n",
    "ics_thomson_ref_tf = thomson_spec(lowengEe_nonrel, lowengEp_nonrel, phys.TCMB(400))\n",
    "print('********* Relativistic regime scattered photon spectrum *********')\n",
    "ics_rel_ref_tf = rel_spec(lowengEe_rel, lowengEp_rel, phys.TCMB(400), inf_upp_bound=True)\n",
    "print('********* Thomson regime energy loss spectrum *********')\n",
    "engloss_ref_tf = engloss_spec(lowengEe_nonrel, lowengEp_nonrel, phys.TCMB(400), thomson_only=True)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we compute the full ICS secondary photon spectrum, as well as the low energy secondary electron spectrum, which are produced after the electrons cool completely, at a redshift of $1+z = 1000$. \n",
    "\n",
    "The outputs of `get_ics_cooling_tf()` are a 2D transfer function for the ICS secondary photons (`ics_sec_phot_tf`, whose grid values are indexed by (`eleceng`, `photeng`), a 2D transfer function for the low-energy electrons (`ics_sec_elec_tf`, indexed by (`eleceng` (in), `eleceng` (out)), a 1D array (indexed by initial electron energy) describing the amount of energy that has been removed from the CMB by scattering, and a 1D array (indexed by initial electron energy) storing the energy nonconservation as a result of numerical errors during the calculation (should all be much smaller than `eleceng`). These quantities are all normalized to a single electron completely cooling through only ICS."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eleceng = 10**np.arange(2, 12, 0.025)\n",
    "photeng = 10**np.arange(-4, 12, 0.025)\n",
    "\n",
    "\n",
    "(ics_sec_phot_tf, ics_sec_elec_tf, cont_loss, eng_discrep) = get_ics_cooling_tf(\n",
    "    ics_thomson_ref_tf, ics_rel_ref_tf, engloss_ref_tf,\n",
    "    eleceng, photeng, 1000\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And here's the plot of the result:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "plt.figure(figsize=(7.8, 6.2))\n",
    "\n",
    "ax = plt.gca()\n",
    "\n",
    "if ics_sec_phot_tf.spec_type == 'N':\n",
    "    ics_sec_phot_tf.switch_spec_type()\n",
    "\n",
    "plt.contourf(\n",
    "    eleceng, photeng, np.transpose(ics_sec_phot_tf.grid_vals), \n",
    "    levels=10.**np.array([ -12, -8, -4, 0, 4, 6, 8]), cmap = 'inferno',\n",
    "    norm = LogNorm()\n",
    "            )\n",
    "\n",
    "plt.text(9e3, 1e10, r'$T_{\\mathrm{CMB}} = $ 0.25 eV', fontsize=20)\n",
    "plt.text(9e3, 1e9, 'Single Electron', fontsize=20)\n",
    "\n",
    "plt.colorbar(label=r'$\\frac{dN_\\gamma}{dE_\\gamma}$ [eV$^{-1}$]')\n",
    "\n",
    "plt.title(r'\\textbf{ICS Secondary Photon Spectrum}')\n",
    "plt.xlabel('Electron Kinetic Energy [eV]')\n",
    "plt.ylabel('Secondary Photon Energy [eV]')\n",
    "\n",
    "ax.set_xscale('log')\n",
    "ax.set_yscale('log')\n",
    "\n",
    "plt.axis([3e3, 1e12, 1e-4, 1e12])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To use this transfer function, let's take an injected electron spectrum, and work out the spectrum of photons after the spectrum cools entirely through ICS (to include atomic processes, see [*electrons.elec_cooling*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/electrons/darkhistory.electrons.elec_cooling.html). Here, we consider a spectrum produced by annihilation of dark matter into a $W^+ W^-$ pair. This can be obtained from the [*darkhistory.spec.pppc*](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/spec/darkhistory.spec.pppc.html), which returns the same flux produced by PPPC4DMID [[1]](#cite_PPPC)[[2]](#cite_weakCorrections). We'll pick $m_\\text{DM}$ = 500 GeV, and obtain the spectrum per annihilation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%autoreload\n",
    "from darkhistory.spec.pppc import get_pppc_spec\n",
    "\n",
    "mDM = 500e9\n",
    "\n",
    "elec_spec = get_pppc_spec(mDM, eleceng, 'W', 'elec')\n",
    "elec_spec.switch_spec_type('N')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`elec_spec` here is a [`Spectrum`](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/spec/spectrum/darkhistory.spec.spectrum.Spectrum.html) object, and contains the abscissa (`eleceng`) and the spectrum, stored as dN/dE or N. Here, the data is stored as type `'N'`. To contract this spectrum with `ics_sec_phot_tf`, we use `TransferFuncAtRedshift.sum_specs`, which acts with the matrix stored in `ics_sec_phot_tf` on the spectrum. Keep in mind that `ics_sec_phot_tf` is normalized to a single electron, so multiplying by `elec_spec` of type `'N'` is appropriate."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phot_spec = ics_sec_phot_tf.sum_specs(elec_spec)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This produces a photon `Spectrum` object, with the abscissa given by `photeng`. \n",
    "\n",
    "We can also produce the initial spectrum of photons that got scattered. This is a blackbody spectrum, since we are only tracking photons that get upscattered in the nonrelativistic, Thomson regime (see the paper for more details). The CMB spectrum at any temperature can be obtained using [`physics.CMB_spec`](https://darkhistory.readthedocs.io/en/latest/_autosummary/darkhistory/physics/darkhistory.physics.CMB_spec.html). This returns a spectrum containing $dN/(dE\\, dV)$ of the CMB. We simply have to rescale this spectrum to the correct initial energy, and this information is stored in `cont_loss`. \n",
    "\n",
    "First, let's put the spectrum in a `Spectrum` object.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from darkhistory.spec.spectrum import Spectrum\n",
    "\n",
    "# Use physics.TCMB() to get the temperature at the right redshift.\n",
    "CMB_spec = Spectrum(photeng, phys.CMB_spec(photeng, phys.TCMB(1000)), spec_type='dNdE')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we want to rescale this spectrum so that it has the energy of the photons that got upscattered. This energy is given by `np.dot(cont_loss, elec_spec.N)`, since `cont_loss` has each bin normalized to one injected electron at some energy. \n",
    "\n",
    "We can do the rescaling simply by multiplying the `Spectrum` object itself. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "CMB_spec *= np.dot(cont_loss, elec_spec.N)/CMB_spec.toteng()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The curves below show the initial electron spectrum, the initial CMB photon spectrum prior to upscattering, and the upscattered photon spectrum."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "\n",
    "plt.loglog()\n",
    "\n",
    "elec_spec_plot, = plt.plot(eleceng, elec_spec.dNdE*eleceng**2, label=r'Injected $e^+e^-$ Spectrum')\n",
    "phot_spec_plot, = plt.plot(photeng, phot_spec.dNdE*photeng**2, label='ICS Secondary Photon Spectrum')\n",
    "CMB_plot,       = plt.plot(photeng, CMB_spec.dNdE*photeng**2,  'k--', label='CMB')\n",
    "\n",
    "plt.xlabel('Energy [eV]')\n",
    "plt.ylabel(r'$E^2\\, dN/dE$ [eV]')\n",
    "plt.title('ICS Secondary Spectrum')\n",
    "\n",
    "plt.text(0.1, 0.92, r'$\\chi \\chi \\to W^+W^-$, $m_\\chi$ = 500 GeV', fontsize=20, transform=ax.transAxes) \n",
    "\n",
    "plt.legend(handles=[elec_spec_plot, phot_spec_plot, CMB_plot], loc=(0.1, 0.65))\n",
    "\n",
    "plt.axis([1e-4, 5e12, 1e-10, 1e23])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bibliography"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[1]<a id='cite_PPPC'></a> M.Cirelli, G.Corcella, A.Hektor, G.Hütsi, M.Kadastik, P.Panci, M.Raidal, F.Sala, A.Strumia, \"PPPC 4 DM ID: A Poor Particle Physicist Cookbook for Dark Matter Indirect Detection,\" arXiv 1012.4515, JCAP 1103 (2011) 051. Erratum: JCAP 1210 (2012) E01.\n",
    "\n",
    "[2]<a id='cite_weakCorrections'></a> P. Ciafaloni, D. Comelli, A. Riotto, F. Sala, A. Strumia, A. Urbano, \"Weak corrections are relevant for dark matter indirect detection,\" arXiv 1009.0224, JCAP 1103 (2011) 019 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
