Fit MRP parameters to model data

In this example, we do something very simple – fit the MRP parameters using a theoretically produced HMF. This might be one of the first things you’d want to do with the MRP. In addition to the simple fit, we’ll also change the truncation scale, to see how the fit performs over different mass ranges. Furthermore, we’ll change the redshift and halo overdensity to make sure the fit performs well in all cases.

The resulting plots appear as figures 1,2 in Murray, Robotham, Power (2018)

In [28]:
#Import necessary libraries
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import MaxNLocator
from mrpy.fitting.fit_curve import get_fit_curve

from os.path import join, expanduser

#We'll use the hmf package to produce the theoretical HMF
from hmf import MassFunction
from hmf.cosmo import Planck13
In [17]:
fig_folder = expanduser("~")

First, we produce a hmf model consistent with the Planck 2013 data, and resolved enough to produce a high-quality fit:

In [29]:
hmf = MassFunction(hmf_model="Tinker08",Mmin=5,Mmax=18,lnk_min=-13,lnk_max=13,
                   transfer_model="CAMB",
                   dlnk=0.01,
                   sigma_8=0.829,n=0.9603,dlog10m=0.1,cosmo_model=Planck13)

Here’s the important part: actually fitting the data.

Fit across redshift and overdensity

In [13]:
# Create the lists that we'll use to save the results
res = [0]*64
obj = [0]*64
max_dev = np.zeros(64)
rms_dev = np.zeros(64)
dndms_whole = [0]*64
ms_whole = [0]*64

# 4 different truncation masses
sigmins = [4,3,2,1.5]
mmins = [1e10,1e11,1e12,1e13]
deltahs = [200.0,400.0,800.0,1600.0]
zs = [0.0,0.5,1.0,2.0]

dndms = [0]*64
ii = 0
for i,z in enumerate(zs):
    for j,deltah in enumerate(deltahs):
        hmf.update(z=z,delta_h=deltah)

        for k,sigmin in enumerate(sigmins):
            # Get theoretical data
            mask = np.logical_and(hmf.sigma < sigmin, hmf.sigma > 0.5)
            dn = hmf.dndm[mask]
            m = hmf.m[mask]

            # Fit the MRP in the *simplest* way possible.
            res[ii], obj[ii] = get_fit_curve(m, dn,
                                             x0 = [14.4,-1.9,0.8,-43],
                                             bounds=[[None,None],[-2.5,-1.5],[0.2,None],[None,None]],
                                             jac=False)

            max_dev[ii] = np.abs(obj[ii].dndm()/dn-1).max()
            rms_dev[ii] = np.sqrt(np.mean((obj[ii].dndm()/dn-1)**2))
            dndms[ii] = dn
            dndms_whole[ii] = hmf.dndm
            ms_whole[ii] = hmf.m

            #print z, deltah, "%1.0e : "%mmin, res[ii].x, max_dev[ii], rms_dev[ii] # the actual result values
            ii += 1

Next the boring stuff… setting up and plotting the figure.

In [18]:
# Create the figure object
xmin_plot = 1e7
xmax_plot = 3e15
ymin_plot = -0.08
ymax_plot = 0.08
fig,ax = plt.subplots(4,4,sharex=True,sharey=True,squeeze=True,figsize=(12,8),
                      subplot_kw={"xscale":"log","ylim":(ymin_plot,ymax_plot),
                                  "xlim":(xmin_plot, xmax_plot)})

# Contract the space a bit
plt.subplots_adjust(wspace=0.08,hspace=0.10)

# Plot the fitted data.
# Note that 'obj' contains lots of quantities of interest, not least of which is a method
# to calculate dn/dm!
ii = 0
for i,z in enumerate(zs):
    for j,deltah in enumerate(deltahs):
        ax[i,j].text(2*xmin_plot,0.053, "RMS(%)" + r"$\sim %1.1f - %1.1f$"%(rms_dev[ii:ii+4].min()*100,rms_dev[ii:ii+4].max()*100),fontsize=13)
        ax[i,j].text(2*xmin_plot,-0.06,r"$z=%s,\ \Delta_h=%s$"%(z,deltah),fontsize=13)

        for k,mmin in enumerate(mmins):
            # Background grey scaled MF
            if k==0:
                mask = np.logical_and(ms_whole[ii]>xmin_plot, ms_whole[ii]<xmax_plot)
                dndm = np.log10(dndms_whole[ii][mask])
                if ii==0:
                    dndm_range = np.max(dndm) - np.min(dndm)
                dndm *= (ymax_plot - ymin_plot)/dndm_range
                dndm += ymax_plot - np.max(dndm)
                ax[i,j].plot(ms_whole[ii][mask], dndm, color='grey', alpha=0.4,lw=3)

            # Plot each iteration
            ax[i,j].plot(obj[ii].m,obj[ii].dndm()/dndms[ii]-1,lw=2)
            # Modify the ticks for prettiness
            ax[i,j].tick_params(axis='both', which='major', labelsize=11)
            ax[i,j].tick_params(axis='both', which='major', labelsize=11)
            ax[i,j].yaxis.set_major_locator(MaxNLocator(6))



            ii += 1

fig.text(0.5, 0.04, r"Mass, $h^{-1}M_\odot$",fontsize=15, ha='center', va='center')
fig.text(0.06, 0.5, 'Relative Residuals', fontsize=15,ha='center', va='center', rotation='vertical')

#Save image
if fig_folder:
    fig.savefig(join(fig_folder,"comparison_tinker.pdf"))
../_images/examples_fit_curve_against_analytic_10_0.png

Fit against different cosmology and halo type

In [22]:
hmf = MassFunction(hmf_model="Warren",Mmin=5,Mmax=18,lnk_min=-13,lnk_max=13,
                   transfer_model="CAMB",
                   dlnk=0.01,
                   sigma_8=0.829,n=0.9603,dlog10m=0.1,cosmo_model=Planck13)
In [23]:
# Create the lists that we'll use to save the results
res = [0]*64
obj = [0]*64
max_dev = np.zeros(64)
rms_dev = np.zeros(64)
# 4 different truncation masses
sigmins = [4,3,2,1.5]
oms = [0.2, 0.25, 0.3, 0.35]
s8s = [0.7, 0.8, 0.9, 1.0]

dndms = [0]*64
ii = 0
for i,s8 in enumerate(s8s):
    for j,om in enumerate(oms):
        hmf.update(cosmo_params={"Om0":om}, sigma_8 = s8)

        for k,sigmin in enumerate(sigmins):


            # Get theoretical data
            mask = np.logical_and(hmf.sigma < sigmin, hmf.sigma > 0.5)
            dn = hmf.dndm[mask]
            m = hmf.m[mask]

            # Fit the MRP in the *simplest* way possible.
            res[ii], obj[ii] = get_fit_curve(m, dn,
                                             x0 = [14.4,-1.9,0.8,-43],
                                             bounds=[[None,None],[-2.5,-1.5],[0.2,None],[None,None]],
                                             jac=False)

            max_dev[ii] = np.abs(obj[ii].dndm()/dn-1).max()
            rms_dev[ii] = np.sqrt(np.mean((obj[ii].dndm()/dn-1)**2))
            dndms[ii] = dn

            dndms_whole[ii] = hmf.dndm
            ms_whole[ii] = hmf.m

            #print z, deltah, "%1.0e : "%mmin, res[ii].x, max_dev[ii], rms_dev[ii] # the actual result values
            ii += 1
In [25]:
xmin_plot = 1e7
xmax_plot = 3e15
ymin_plot = -0.08
ymax_plot = 0.08
fig,ax = plt.subplots(4,4,sharex=True,sharey=True,squeeze=True,figsize=(12,8),
                      subplot_kw={"xscale":"log","ylim":(ymin_plot,ymax_plot),
                                  "xlim":(xmin_plot, xmax_plot)})
# Contract the space a bit
plt.subplots_adjust(wspace=0.08,hspace=0.10)

# Plot the fitted data.
# Note that 'obj' contains lots of quantities of interest, not least of which is a method
# to calculate dn/dm!
ii = 0
for i,s8 in enumerate(s8s):
    for j,om in enumerate(oms):
        ax[i,j].text(2*xmin_plot,0.053, "RMS(%)" + r"$\sim %1.1f - %1.1f$"%(rms_dev[ii:ii+4].min()*100,rms_dev[ii:ii+4].max()*100),fontsize=13)
        ax[i,j].text(2*xmin_plot,-0.06,r"$\Omega_m=%s,\ \sigma_8=%s$"%(om,s8),fontsize=13)

        for k,mmin in enumerate(mmins):
            if k==0:
                mask = np.logical_and(ms_whole[ii]>xmin_plot, ms_whole[ii]<xmax_plot)
                dndm = np.log10(dndms_whole[ii][mask])
                if ii==0:
                    dndm_range = np.max(dndm) - np.min(dndm)
                dndm *= (ymax_plot - ymin_plot)/dndm_range
                dndm += ymax_plot - np.max(dndm)
                ax[i,j].plot(ms_whole[ii][mask], dndm, color='grey', alpha=0.4,lw=3)

            # Plot each iteration
            ax[i,j].plot(obj[ii].m,obj[ii].dndm()/dndms[ii]-1,lw=2)

            # Modify the ticks for prettiness
            ax[i,j].tick_params(axis='both', which='major', labelsize=11)
            ax[i,j].tick_params(axis='both', which='major', labelsize=11)
            ax[i,j].yaxis.set_major_locator(MaxNLocator(6))



            ii += 1

fig.text(0.5, 0.04, r"Mass, $h^{-1}M_\odot$",fontsize=15, ha='center', va='center')
fig.text(0.06, 0.5, 'Relative Residuals', fontsize=15,ha='center', va='center', rotation='vertical')

#Save image
if fig_folder:
    fig.savefig(join(fig_folder,"comparison_warren.pdf"))
../_images/examples_fit_curve_against_analytic_14_0.png