Boost factors

import clmm
from clmm import Cosmology
from clmm.support import mock_data as mock
from clmm.galaxycluster import GalaxyCluster
import matplotlib.pyplot as plt
import sys
import clmm.utils as u

Make sure we know which version we’re using

clmm.__version__
'1.12.0'

Define cosmology object

mock_cosmo = Cosmology(H0=70.0, Omega_dm0=0.27 - 0.045, Omega_b0=0.045, Omega_k0=0.0)

First, we want to generate a \(\Delta\Sigma\) (excess surface density) profile from mock data, to which we can apply boost factors. The mock data is generated in the following cells.

Generate cluster object from mock data

cosmo = mock_cosmo
cluster_id = "Awesome_cluster"
cluster_m = 1.0e15
cluster_z = 0.3
concentration = 4
ngals = 1000

zsrc_min = cluster_z + 0.1  # we only want to draw background galaxies

noisy_data_z = mock.generate_galaxy_catalog(
    cluster_m,
    cluster_z,
    concentration,
    cosmo,
    "chang13",
    zsrc_min=zsrc_min,
    shapenoise=0.005,
    photoz_sigma_unscaled=0.05,
    ngals=ngals,
)

Loading this into a CLMM cluster object centered on (0,0)

cluster_ra = 0.0
cluster_dec = 0.0
cl = GalaxyCluster(cluster_id, cluster_ra, cluster_dec, cluster_z, noisy_data_z)

Compute cross and tangential excess surface density for each source galaxy

_ = cl.compute_tangential_and_cross_components(
    geometry="flat",
    shape_component1="e1",
    shape_component2="e2",
    tan_component="DeltaSigma_tan",
    cross_component="DeltaSigma_cross",
    add=True,
    cosmo=cosmo,
    is_deltasigma=True,
)
/global/homes/a/aguena/.local/perlmutter/python-3.11/lib/python3.11/site-packages/clmm-1.12.0-py3.11.egg/clmm/cosmology/parent_class.py:110: UserWarning:
Some source redshifts are lower than the cluster redshift.
Sigma_crit = np.inf for those galaxies.

Calculate the binned profile

cl.make_radial_profile(
    "kpc",
    cosmo=cosmo,
    tan_component_in="DeltaSigma_tan",
    cross_component_in="DeltaSigma_cross",
    tan_component_out="DeltaSigma_tan",
    cross_component_out="DeltaSigma_cross",
    table_name="DeltaSigma_profile",
)
# Format columns for display
for col in cl.DeltaSigma_profile.colnames:
    fmt = cl.DeltaSigma_profile[col].info.format
    if "DeltaSigma" in col:
        fmt = ".2e"
    elif any(typ in col for typ in ("z", "radius")):
        fmt = ".2f"
    cl.DeltaSigma_profile[col].info.format = fmt
# Show
cl.DeltaSigma_profile.show_in_notebook()
GCData length=10
idxradius_minradiusradius_maxDeltaSigma_tanDeltaSigma_tan_errDeltaSigma_crossDeltaSigma_cross_errzz_errn_srcW_l
0116.87441.96652.822.38e+143.64e+13-1.24e+131.62e+131.190.141818.0
1652.82967.931188.771.16e+143.55e+121.56e+112.06e+121.170.104747.0
21188.771480.841724.727.26e+132.05e+122.97e+122.51e+121.370.096767.0
31724.721989.062260.675.70e+132.01e+12-3.14e+111.72e+121.300.089292.0
42260.672531.032796.624.57e+131.80e+12-1.45e+121.38e+121.410.07139139.0
52796.623077.133332.573.94e+132.97e+123.13e+111.77e+121.270.06167167.0
63332.573598.823868.523.03e+132.02e+122.58e+123.69e+121.210.05196196.0
73868.524127.034404.482.58e+131.56e+121.54e+121.26e+121.210.05163163.0
84404.484659.494940.432.26e+131.80e+123.08e+121.74e+121.370.097676.0
94940.435184.625476.382.37e+139.03e+12-2.74e+113.98e+121.140.133535.0

Plot the \(\Delta\Sigma\) profile

plt.errorbar(
    cl.DeltaSigma_profile["radius"],
    cl.DeltaSigma_profile["DeltaSigma_tan"],
    cl.DeltaSigma_profile["DeltaSigma_tan_err"],
    marker="o",
)

plt.title("DeltaSigma profile")
plt.xlabel("Radius [kpc]")
plt.ylabel("$\Delta\Sigma [M_\odot\; Mpc^{-2}]$")

plt.show()
../_images/demo_boost_factors_16_0.png

Boost Factors

CLMM offers two boost models, the NFW boost model, and a powerlaw boost model.

Note that compute_nfw_boost requires two parameters to be specified, rs and b0, and compute_powerlaw_boost requires three paramters, rs, b0 and alpha. The default values are in kpc.

Details on these boost models can be found here

First, we can calculate the boost factors from the two models.

nfw_boost = u.compute_nfw_boost(cl.DeltaSigma_profile["radius"], rscale=1000, boost0=0.1)

powerlaw_boost = u.compute_powerlaw_boost(
    cl.DeltaSigma_profile["radius"], rscale=1000, boost0=0.1, alpha=-1.0
)

Plot the two boost factors, \(B(R)\)

plt.plot(cl.DeltaSigma_profile["radius"], nfw_boost, label="NFW boost factor")
plt.plot(cl.DeltaSigma_profile["radius"], powerlaw_boost, label="Powerlaw boost factor")
plt.xlabel("Radius [kpc]")
plt.ylabel("$B(R)$")
plt.legend()
plt.show()
../_images/demo_boost_factors_21_0.png

The \(\Delta\Sigma\) profiles can be corrected with the boost factor using correct_sigma_with_boost_values or correct_sigma_with_boost_model.

correct_sigma_with_boost_values requires us to precompute the boost factor, e.g. using compute_nfw_boost. correct_sigma_with_boost_model simply requires us to specify the boost model.

Note that the boost factor can be used in one of two ways.

Either the boost factor can be applied to the observed data vector to correct for the dilution of the signal by cluster member galaxies. In this case the amplitude of the corrected profile will increase.

Or the boost factor can be applied to the model prediction. In this case it behaves as a dilution factor, and the resulting model prediction will be lower than the original one.

Both scenarios will improve the agreement between the mock data and observed data, by accounting for cluster member galaxy contamination.

In this notebook, we use the second approach, where the data is generated using mock data that does not account for dilution until the boost factor is applied. The corrected profiles from the mock data are lower than the uncorrected one.

Essentially we are diluting the mock profile to mimick the effect of contamination by cluster members.

First we will apply the boost factor with correct_sigma_with_boost_values

Sigma_corrected_powerlaw_boost = u.correct_sigma_with_boost_values(
    cl.DeltaSigma_profile["DeltaSigma_tan"], powerlaw_boost
)
Sigma_corrected_nfw_boost = u.correct_sigma_with_boost_values(
    cl.DeltaSigma_profile["DeltaSigma_tan"], nfw_boost
)

Plot the result

plt.errorbar(
    cl.DeltaSigma_profile["radius"],
    Sigma_corrected_nfw_boost,
    cl.DeltaSigma_profile["DeltaSigma_tan_err"],
    marker="o",
    label="$\Delta \Sigma$ / NFW boost factor",
)

plt.errorbar(
    cl.DeltaSigma_profile["radius"],
    Sigma_corrected_powerlaw_boost,
    cl.DeltaSigma_profile["DeltaSigma_tan_err"],
    marker="o",
    label="$\Delta \Sigma$ / Powerlaw boost factor",
)

plt.errorbar(
    cl.DeltaSigma_profile["radius"],
    cl.DeltaSigma_profile["DeltaSigma_tan"],
    cl.DeltaSigma_profile["DeltaSigma_tan_err"],
    marker="o",
    label="uncorrected $\Delta \Sigma$",
    color="grey",
)

# plt.loglog()
plt.title("DeltaSigma profile")
plt.xlabel("Radius [kpc]")
plt.ylabel("$\Delta\Sigma [M_\odot\; Mpc^{-2}]$")
plt.legend()
plt.show()
../_images/demo_boost_factors_27_0.png

Now the same again but with correct_sigma_with_boost_model

Sigma_corrected_powerlaw_boost = u.correct_sigma_with_boost_model(
    cl.DeltaSigma_profile["radius"],
    cl.DeltaSigma_profile["DeltaSigma_tan"],
    boost_model="powerlaw_boost",
)
Sigma_corrected_nfw_boost = u.correct_sigma_with_boost_model(
    cl.DeltaSigma_profile["radius"],
    cl.DeltaSigma_profile["DeltaSigma_tan"],
    boost_model="nfw_boost",
)

plt.errorbar(
    cl.DeltaSigma_profile["radius"],
    Sigma_corrected_nfw_boost,
    cl.DeltaSigma_profile["DeltaSigma_tan_err"],
    marker="o",
    label="$\Delta \Sigma$ / NFW boost factor",
)

plt.errorbar(
    cl.DeltaSigma_profile["radius"],
    Sigma_corrected_powerlaw_boost,
    cl.DeltaSigma_profile["DeltaSigma_tan_err"],
    marker="o",
    label="$\Delta \Sigma$ / Powerlaw boost factor",
)

plt.errorbar(
    cl.DeltaSigma_profile["radius"],
    cl.DeltaSigma_profile["DeltaSigma_tan"],
    cl.DeltaSigma_profile["DeltaSigma_tan_err"],
    marker="o",
    label="uncorrected $\Delta \Sigma$",
    color="grey",
)

# plt.loglog()
plt.title("DeltaSigma profile")
plt.xlabel("Radius [kpc]")
plt.ylabel("$\Delta\Sigma [M_\odot\; Mpc^{-2}]$")
plt.legend()
plt.show()
../_images/demo_boost_factors_29_0.png