Author: Lehman Garrison (https://lgarrison.github.io)
Thanks to Daniel Eisenstein for deriving an even simpler expression! The notebook has been updated with this version.
Power-law cosmologies ($P(k) = A k^n$) have an analytic form for $\sigma^2(R)$ with a spherical tophat window $W_T$:
$$\begin{align} \sigma^2(R) &= \int \frac{d^3k}{(2\pi)^3}\tilde W_T^2(kR)P(k) \\ &= A \int_0^\infty \frac{k^2dk}{2\pi^2} \left[\frac{3(\sin(kR) - kR\cos(kR))}{(kR)^3}\right]^2 k^n\\ &= \frac{9 A (n+1) 2^{-n-1} R^{-n-3} \sin \left(\frac{\pi n}{2}\right) \Gamma (n-1)}{\pi^2 (n-3)} \end{align} $$where we used the Fourier transform $\tilde W_T(kR)$ of the spherical tophat window. The result in the last line (obtained via Mathematica) is only valid for $-3 < n < 1$; otherwise, the variance is undefined.
However, the result is numerically unstable if $n$ is an integer in this range, since the function $\Gamma(n-1)$ in the numerator goes to infinity for 0 and the negative integers.
The $\sigma^2(R)$ function itself is perfectly smooth and finite in this range, as seen below:
import numpy as np
from scipy.special import gamma
def sigma2_mathematica(n,R):
return 9*(n + 1)*2**(-n - 1)*R**(-n - 3)*np.sin(np.pi*n/2)*gamma(n - 1)/(np.pi**2*(n-3))
%matplotlib inline
import matplotlib.pyplot as plt
nrange = np.linspace(-3,1,101, endpoint=False)
plt.plot(nrange, sigma2_mathematica(nrange,1))
plt.xlabel('Power law index $n$')
plt.ylabel('Tophat variance $\sigma^2(R=1)$');
plt.yscale('log')
plt.savefig('scale_free_sigma8.pdf')
For plotting purposes, we avoided exact integers in the $n$ range and used $A=1$.
So, to find a numerically stable variant of the above expression, we need to re-write it to avoid evaluating $\Gamma(n-1)$ at negative integers.
Using some properties of the $\Gamma$ function (https://dlmf.nist.gov/5.5), we can arrive at the following expression:
$$ \sigma^2(R) = \frac{9 A R^{-(3+n)}}{2 \pi^{3/2}} \frac{\Gamma[(3+n)/2]}{\Gamma[(2-n)/2] (n-3)(n-1)} $$It's worth noting that this expression is exact; we have not used any approximations.
Note: a previous version of this notebook had two expressions, one for even poles and one for odd, which are now combined into one expression.
def sigma2_robust(n,R):
return 9*R**-(3 + n)/(2*np.pi**(3/2)) * gamma((3 + n)/2)/(gamma((2 - n)/2)*(n - 3)*(n - 1))
import pandas as pd
def test_all():
n = np.array([-2.9, -2.5, -2, -1.3, -1, -0.01, 0, 0.001, 0.75])
R = 8.
res = pd.DataFrame(index=n)
res.index.name = 'n'
res['Naive'] = sigma2_mathematica(n, R)
res['Stable'] = sigma2_robust(n, R)
return res
res = test_all()
res
print(res.to_latex(float_format=lambda s:'{:.6g}'.format(s)))