A Showcase of Components in halomod

In this demo, we will showcase each and every one of the different models and components in halomod, to give a taste of its capabilities. Note that the plots generated in this notebook appear in THIS PAPER.

[38]:
import hmf
import numpy as np

import halomod
from halomod.bias import make_colossus_bias
from halomod.concentration import make_colossus_cm
[39]:
print(f"Using halomod v{halomod.__version__} and hmf v{hmf.__version__}")
Using halomod v2.0.2.dev51+geee0902 and hmf v3.5.0

Setup Matplotlib

[2]:
%matplotlib inline

import matplotlib.pyplot as plt
from matplotlib import rc, rcParams

Since these plots will be included in a paper, we define some preset configurations for different kinds of plots, to achieve some consistency.

[19]:
paper_font_size = 10  # elsarticle


def single_column(height_ratio=1 / 1.5):
    linewidth = 3.49  # Get this in your tex using \printinunitsof{in}\prntlen{\linewidth}
    paper_settings(linewidth, height_ratio)


def two_column(height_ratio=1 / 1.5):
    textwidth = 7.22  # Get this in your tex using \printinunitsof{in}\prntlen{\textwidth}
    paper_settings(textwidth, height_ratio)


def paper_settings(width, height_ratio=1 / 1.5):
    plt.style.use("dark_background")

    rc("figure", figsize=(width, width * height_ratio), dpi=200)
    rc(("xtick", "ytick"), labelsize=paper_font_size - 2, direction="in")
    rc("legend", fontsize=paper_font_size - 1, frameon=False, columnspacing=1, handletextpad=0.4)
    rc("axes", labelsize=paper_font_size - 1)

    rc("savefig", dpi=600, bbox="tight")

Setup default model

Some of the components presented here can be calculated using lower-level frameworks, but all of them can be calculated from the TracerHaloModel, so we use this.

[4]:
from halomod import TracerHaloModel
[6]:
base_model = TracerHaloModel(
    dr_table=0.03,
    dlnk=0.05,
    dlog10m=0.05,
    transfer_params={"kmax": 1e2, "extrapolate_with_eh": True},
)

# Pre-compute many of the relevant quantities
base_model.power_auto_tracer;

Since most of the plots we make will be comparing all possible models of a particular component, we define a simple function to find all the models for a particular component:

For bias models:

[21]:
import importlib


def get_models(module, component, ignore=()):
    cmp = getattr(importlib.import_module(module), component)
    models = cmp.get_models()
    return [m for name, m in models.items() if name not in ignore]
[22]:
get_models("halomod.bias", "Bias")
[22]:
[halomod.bias.UnityBias,
 halomod.bias.Mo96,
 halomod.bias.Jing98,
 halomod.bias.ST99,
 halomod.bias.SMT01,
 halomod.bias.Seljak04,
 halomod.bias.Seljak04Cosmo,
 halomod.bias.Tinker05,
 halomod.bias.Mandelbaum05,
 halomod.bias.Pillepich10,
 halomod.bias.Manera10,
 halomod.bias.Tinker10,
 halomod.bias.Tinker10PBSplit]

Now, define a function that will do most of the plotting for us. It will grab all the models defined for a particular component, and plot a resulting quantity for all of them, with a lower panel showing the ratio to the fiducial model.

[13]:
import random


def model_comparison_plot(
    module,
    component,
    attr_name,
    x,
    y,
    ignore=(),
    height_ratio=1 / 1.5,
    ylab=None,
    y_unit=None,
    yratio_lab=None,
    ylog=True,
    comparison_limits=(0.8, 1.2),
    xlim=None,
    ylim=None,
    must_include=(),
    ncol_legend=1,
    legend_size=None,
    max_models=10,
    extra_models=None,
    extra_params=None,
):
    extra_params = extra_params or {}
    extra_models = extra_models or []
    # Get all the models for this component
    models = get_models(module, component, ignore)
    if extra_models:
        models += extra_models

    # Limit them to seven models (randomly)
    if len(models) > max_models:
        tmp_models = [m for m in models if m.__name__ in must_include or m in extra_models]
        for m in tmp_models:
            models.remove(m)

        models = random.sample(models, max_models - len(tmp_models))
        models.extend(tmp_models)

    # Most model comparison plots will be single column in the paper.
    single_column(height_ratio)

    subplot_kw = {"xscale": "log"}
    if xlim:
        subplot_kw["xlim"] = xlim

    # Create a two-panel plot with the lower panel slightly smaller.
    _fig, ax = plt.subplots(
        2, 1, sharex=True, gridspec_kw={"hspace": 0, "height_ratios": [3, 2]}, subplot_kw=subplot_kw
    )

    # Plot the different models.
    model = base_model.clone()  # Make a new updateable model.
    for i, cmp_model in enumerate(models):
        if cmp_model.__name__ != getattr(base_model, attr_name).__name__:
            model.update(
                **{attr_name.replace("model", "params"): extra_params.get(cmp_model.__name__, {})}
            )

        model.update(**{attr_name: cmp_model})
        ls = ":" if cmp_model in extra_models else ["-", "--", ":"][i // 10]

        xx = getattr(model, x)
        if x == "nu":
            xx = np.sqrt(xx)
        ax[0].plot(xx, getattr(model, y), label=cmp_model.__name__, lw=1, ls=ls)
        ax[1].plot(xx, getattr(model, y) / getattr(base_model, y), ls=ls)

    if ylog:
        ax[0].set_yscale("log")

    ax[0].legend(ncol=ncol_legend, fontsize=legend_size or rcParams["legend.fontsize"])

    ylab = ylab or y

    # Set axis labels and limits.
    ylabel = f"{ylab} [{y_unit}]" if y_unit else ylab

    ax[0].set_ylabel(ylabel)
    if x == "k":
        ax[1].set_xlabel("k [$h$ / Mpc]")
    elif x == "m":
        ax[1].set_xlabel(r"m [$h^{-1} M_\odot$]")
    elif x == "nu":
        ax[1].set_xlabel(r"Peak-height, $\nu$")

    if yratio_lab is None:
        yratio_lab = ylab

    ax[1].set_ylabel(
        r"{} / ${}_{{\rm {}}}$".format(
            yratio_lab, yratio_lab.replace("$", ""), getattr(base_model, attr_name).__name__
        )
    )
    ax[1].set_ylim(comparison_limits)

    if ylim:
        ax[0].set_ylim(ylim)

    # Save the figure
    plt.savefig(f"plots/{attr_name}s.pdf")

Transfer Function

First, transfer functions. We omit the models that require user input (eg. FromFile).

The transfer function determines the linear power spectrum, which in turn affects the halo mass function (more power on larger scales means more high-mass halos).

[23]:
model_comparison_plot(
    module="hmf.density_field.transfer_models",
    component="TransferComponent",
    ignore=("FromFile", "FromArray", "EH"),
    attr_name="transfer_model",
    x="k",
    y="power",
    ylab=r"$P_{\rm lin}(k)$",
    y_unit=r"$h^{-3}{\rm Mpc}^3$",
    xlim=(1e-4, 1e2),
    yratio_lab=r"$P^{\rm lin}$",
    height_ratio=1 / 1.2,
    ncol_legend=2,
    ylim=(1e-4, 1e5),
    comparison_limits=(0.8, 1.25),
)
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/hmf/density_field/transfer_models.py:233: UserWarning: 'extrapolate_with_eh' was not set. Defaulting to True, which is different behaviour than versions <=3.4.4. This warning may be removed in v4.0. Silence it by setting extrapolate_with_eh explicitly.
  warnings.warn(
../_images/examples_component-showcase_20_1.png

First notice that the fiducial model is CAMB (this is only true if camb is installed, otherwise it is EH_BAO). Since CAMB has BAOs, all the models without BAOs show up in the ratio as having wiggles. Interestingly, CAMB has much higher power on small scales than the other models. CAMB is much slower to run than the other analytic models.

The normalization difference on large scales is due to the difference in the integral of the power spectrum, which is normalized to be the same value via \(\sigma_8\).

Mass Definitions

Here we plot the change in mass and concentration when moving from one mass definition to another.

[24]:
from hmf.halos.mass_definitions import FOF, SOCritical, SOMean, SOVirial
[25]:
single_column(height_ratio=1 / 1.5)


fig, ax = plt.subplots(
    2,
    1,
    sharex=True,
    gridspec_kw={"hspace": 0, "height_ratios": [3, 2]},
    subplot_kw={"xscale": "log"},
)

for mdef in [SOMean(overdensity=300), SOCritical(overdensity=300), SOVirial(), FOF()]:
    m, r, c = base_model.mdef.change_definition(
        base_model.m, mdef=mdef, profile=base_model.halo_profile, cosmo=base_model.cosmo
    )

    ax[0].plot(base_model.m, m / base_model.m, label=mdef.colossus_name)
    ax[1].plot(base_model.m, c / base_model.cmz_relation)

ax[0].grid(True)
ax[1].grid(True)

ax[0].legend(ncol=2, frameon=True)

ax[0].set_ylabel(r"$m_{\rm new} / m_{\rm old}$")
ax[1].set_ylabel(r"$c_{\rm new} / c_{\rm old}$")
ax[1].set_xlabel(r"m, [$h^{-1}M_{\odot}$]")

plt.savefig("plots/mass_conversion.pdf")
../_images/examples_component-showcase_25_0.png

Notice that halos with a higher overdensity threshold move to lower masses and lower concentration.

Window Functions / Filters

The filter model, \(W(x)\), is convolved with the power spectrum to produce the mass variance (which in turn is used to define the halo mass function). Optimally, the shape of the filter model is matched to the shape of primordial patches that become halos. However, typically a simplified model such as a top-hat is used.

[26]:
model_comparison_plot(
    module="hmf.density_field.filters",
    component="Filter",
    attr_name="filter_model",
    x="m",
    y="sigma",
    ylab=r"$\sigma(m)$",
    yratio_lab=r"$\sigma$",
    height_ratio=1 / 1.2,
    ncol_legend=1,
    ylim=(0, 10),
    ylog=False,
    xlim=(1e8, 1e16),
    comparison_limits=(0.7, 1.3),
)
../_images/examples_component-showcase_29_0.png

Note that while any of these can be used to generate \(\sigma(m)\), the normalisation of the power spectrum, \(\sigma(R=8)\), is always calculated using the TopHat.

Mass Function

There are many fitting functions for the HMF included in hmf. Some are fitted against FOF-haloes, while others are fitted to SO halos of various definitions. Here we’ll only plot a few, so that the plot is not too confusing.

[28]:
model_comparison_plot(
    module="hmf.mass_function.fitting_functions",
    component="FittingFunction",
    attr_name="hmf_model",
    x="nu",
    y="fsigma",
    ylab=r"$\nu f(\nu)$",
    height_ratio=1 / 1.2,
    ncol_legend=2,
    ylim=(5e-8, 1),
    # ylog=False,
    xlim=(6e-2, 1e1),
    comparison_limits=(0.2, 2.1),
    must_include=("PS", "SMT"),
    ignore=("ST", "Jenkins"),  # Jenkins gets in the way of the legend
    legend_size=7,
    max_models=14,
    yratio_lab="f",
)
/home/sgm/work/halos/halomod/src/halomod/halo_model.py:384: UserWarning: Requested mass definition 'FoF(l=0.2)' is not in native definitions for the 'Duffy08' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'FoF(l=0.2)'.
  return self.halo_concentration_model(
/home/sgm/work/halos/halomod/src/halomod/halo_model.py:384: UserWarning: Requested mass definition 'SOCritical(500.0)' is not in native definitions for the 'Duffy08' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOCritical(500.0)'.
  return self.halo_concentration_model(
../_images/examples_component-showcase_33_1.png

Bias

The halo bias describes the clustering strength of halos of a given mass compared to the background matter. Many bias models are included in halomod, but even more can be accessed through the interface to colossus:

[29]:
comparat = make_colossus_bias("comparat17")
bhat11 = make_colossus_bias("bhattacharya11")
[30]:
model_comparison_plot(
    module="halomod.bias",
    component="Bias",
    attr_name="bias_model",
    x="nu",
    y="halo_bias",
    ylab=r"b($\nu$)",
    height_ratio=1 / 1.2,
    ncol_legend=2,
    comparison_limits=(0.5, 1.6),
    must_include=("Tinker05", "Mandelbaum05", "ST99", "SMT01", "Mo96", "Jing98"),
    ignore=("UnityBias", "Seljak04Cosmo"),
    legend_size=7,
    max_models=11,
    xlim=(6e-2, 1e1),
    ylim=(3e-1, 2e3),
    extra_models=[comparat, bhat11],
)
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/cosmology/cosmology.py:3338: UserWarning: Astropy cosmology class contains massive neutrinos, which are not taken into account in Colossus.
  warnings.warn('Astropy cosmology class contains massive neutrinos, which are not taken into account in Colossus.')
../_images/examples_component-showcase_37_1.png

Concentration

The concentration-mass relation affects the shape of the halo profile at different masses. halomod includes physically-motivated forms (eg. Bullock and Ludlow) and power-law forms. Since the power-law forms are really only applicable to a single cosmology, we focus on the analytic forms in this plot:

[31]:
model_comparison_plot(
    module="halomod.concentration",
    component="CMRelation",
    attr_name="halo_concentration_model",
    x="m",
    y="cmz_relation",
    ylab=r"c(m)",
    yratio_lab="c",
    height_ratio=1 / 1.2,
    ncol_legend=2,
    comparison_limits=(0.3, 2.1),
    ignore=("Ludlow2016", "Ludlow2016Empirical", "Zehavi11", "Bullock01Power"),
    legend_size=7,
    ylim=(2, 3e2),
    xlim=(7e7, 5e15),
    extra_models=[
        make_colossus_cm("prada12"),
        make_colossus_cm("diemer15"),
        make_colossus_cm("diemer19"),
        #        make_colossus_cm('ishiyama20'),
    ],
)
/home/sgm/work/halos/halomod/src/halomod/halo_model.py:384: UserWarning: Requested mass definition 'SOMean(200)' is not in native definitions for the 'Bullock01' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOMean(200)'.
  return self.halo_concentration_model(
/home/sgm/work/halos/halomod/src/halomod/halo_model.py:384: UserWarning: Requested mass definition 'SOMean(200)' is not in native definitions for the 'Ludlow16' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOMean(200)'.
  return self.halo_concentration_model(
/home/sgm/work/halos/halomod/src/halomod/halo_model.py:384: UserWarning: Requested mass definition 'SOMean(200)' is not in native definitions for the 'Ludlow16Empirical' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOMean(200)'.
  return self.halo_concentration_model(
/home/sgm/work/halos/halomod/src/halomod/concentration.py:198: UserWarning: Requested mass definition 'SOMean(200)' is not in native definitions for the 'Diemer19' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOMean(200)'.
  super().__init__(*args, **kwargs)
/home/sgm/work/halos/halomod/src/halomod/concentration.py:198: UserWarning: Requested mass definition 'SOMean(200)' is not in native definitions for the 'Prada12' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOMean(200)'.
  super().__init__(*args, **kwargs)
ERROR
Requested overdensity 8.39e-12 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 7.56e-13 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 5.11e-14 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 2.51e-15 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 8.54e-17 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 1.93e-18 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 2.76e-20 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 2.34e-22 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 7.63e-12 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 6.80e-13 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 4.54e-14 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 2.20e-15 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 7.37e-17 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 1.64e-18 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 2.29e-20 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 1.90e-22 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 8.66e-25 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 2.02e-27 cannot be evaluated for scale density 2.46e-11, out of range.
ERROR
Requested overdensity 2.20e-30 cannot be evaluated for scale density 2.46e-11, out of range.
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 1.12e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 1.26e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 1.41e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 1.58e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 1.78e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 2.00e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 2.24e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 2.51e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 2.82e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 3.16e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 3.55e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 3.98e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 4.47e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 5.01e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 5.62e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 6.31e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 7.08e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 7.94e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/colossus/halo/concentration.py:421: UserWarning: Could not find concentration for model prada12, mass 8.91e+17, mdef 200m.
  warnings.warn('Could not find concentration for model %s, mass %.2e, mdef %s.' \
/home/sgm/work/halos/halomod/src/halomod/concentration.py:198: UserWarning: Requested mass definition 'SOMean(200)' is not in native definitions for the 'Diemer15' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOMean(200)'.
  super().__init__(*args, **kwargs)
/home/sgm/work/halos/halomod/src/halomod/concentration.py:198: UserWarning: Requested mass definition 'SOMean(200)' is not in native definitions for the 'Diemer19' CMRelation. No mass conversion will be performed, so results will be wrong. Using 'SOMean(200)'.
  super().__init__(*args, **kwargs)
../_images/examples_component-showcase_40_3.png

Notice that both of the physical forms have discontinuities, which arise when the predicted redshift of formation for a given halo mass is less than zero. This typically does not present a problem for the halo model, for which intermediate masses are the most important.

Halo Exclusion

[32]:
model = base_model.clone()

single_column(height_ratio=1 / 1.5)

fig, ax = plt.subplots(
    2,
    1,
    sharex=True,
    gridspec_kw={"hspace": 0, "height_ratios": [3, 2]},
    subplot_kw={"xscale": "log"},
)

for i, excl in enumerate(["NoExclusion", "Sphere", "DblSphere_", "DblEllipsoid_", "NgMatched_"]):
    model.exclusion_model = excl

    ax[0].plot(model.r, model.corr_auto_tracer, label=excl.replace("_", ""), color=f"C{i}")
    ax[0].plot(model.r, model.corr_2h_auto_tracer, ls="--", color=f"C{i}", lw=1)
    ax[1].plot(model.r, model.corr_auto_tracer / base_model.corr_auto_tracer)

model.exclusion_model = "NoExclusion"

model.hc_spectrum = "filtered-lin"
ax[0].plot(model.r, model.corr_auto_tracer, label=r"Filt. $P_{\rm lin}(k)$", color=f"C{i + 1}")
ax[0].plot(model.r, model.corr_2h_auto_tracer, ls="--", color=f"C{i + 1}", lw=1)
ax[1].plot(
    model.r,
    model.corr_auto_tracer / base_model.corr_auto_tracer,
)

model.hc_spectrum = "filtered-nl"
ax[0].plot(model.r, model.corr_auto_tracer, label=r"Filt. $P_{\rm nl}(k)$", color=f"C{i + 2}")
ax[0].plot(model.r, model.corr_2h_auto_tracer, ls="--", color=f"C{i + 2}", lw=1)
ax[1].plot(
    model.r,
    model.corr_auto_tracer / base_model.corr_auto_tracer,
)

ax[0].legend(ncol=3, frameon=False, fontsize=7, bbox_to_anchor=(0.0, 1.02, 1.0, 0.102))
ax[0].set_yscale("log")

ax[0].set_xlim(1e-1, 1e1)
ax[0].set_ylim(1e-1, 1e4)
ax[1].set_ylabel(r"$\xi / \xi_{\rm No Excl}$")
ax[0].set_ylabel(r"$\xi_{gg}(r)$")
ax[1].set_xlabel("r, [$h^{-1}$Mpc]")
/home/sgm/work/halos/halomod/src/halomod/tools.py:507: UserWarning: to use a power-law, y must be all positive or negative. Switching to zero extrapolation.
  self.lfunc = self._get_extension_func(
/home/sgm/work/halos/halomod/src/halomod/tools.py:507: UserWarning: to use a power-law, y must be all positive or negative. Switching to zero extrapolation.
  self.lfunc = self._get_extension_func(
/home/sgm/work/halos/halomod/src/halomod/tools.py:507: UserWarning: to use a power-law, y must be all positive or negative. Switching to zero extrapolation.
  self.lfunc = self._get_extension_func(
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/hmf/_internals/_cache.py:162: UserWarning: power_2h_auto_tracer_fnc not found in recalc cache.
  warnings.warn(f"{name} not found in recalc cache.")
/home/sgm/work/halos/halomod/.venv/lib/python3.12/site-packages/hmf/_internals/_cache.py:167: UserWarning: power_2h_auto_tracer_fnc not found in recalc_prop_par cache
  warnings.warn(f"{name} not found in recalc_prop_par cache")
../_images/examples_component-showcase_43_1.png

HOD

For the HOD, we’ll only be showing models of point-source-like tracers (i.e. galaxies). A whole set of HOD models for continuous tracers (eg. HI gas) are also available. Also note that these models look very similar to each other. This is by design. Most of the models are essentially nested (i.e. you can get back Zehavi05 from setting some of the Contreras13 parameters to zero or one). By default, halomod sets the parameters so that the model returns the same or a similar occupation function. However, the HOD is not fundamental in nature – it depends on the actual galaxy sample being observed (and its depth/wavelength etc), so these models can vary widely for different parameters.

[33]:
single_column(height_ratio=1 / 1.5)

model = base_model.clone()
for hod in ["Zehavi05", "Zheng05", "Tinker05", "Geach12", "Contreras13"]:
    model.hod_model = hod
    plt.plot(model.m, model.total_occupation, label=hod)

plt.xscale("log")
plt.yscale("log")
plt.ylim(1e-4, 1e4)
plt.xlim(1e9, 1e16)
plt.legend(fontsize=8)

plt.xlabel(r"m, [$h^{-1}M_\odot$]")
plt.ylabel("N(m)")
[33]:
Text(0, 0.5, 'N(m)')
../_images/examples_component-showcase_46_1.png

The key things to recognize are a low-mass cutoff, essentially corresponding to the depth of the survey, and a high-mass powerlaw with slope close to one (i.e. doubling halo mass doubles the occupancy). The differentiation between central and satellite halos causes the bump seen above at \(m\sim10^{12}\).

Profiles

[36]:
two_column(height_ratio=1 / 2.3)

fig, ax = plt.subplots(
    2,
    2,
    sharex="col",
    gridspec_kw={"hspace": 0, "height_ratios": [3, 2]},
    subplot_kw={"xscale": "log"},
)

profiles = get_models(
    "halomod.profiles",
    "Profile",
    ignore=("ProfileInf", "Constant", "NFWInf", "HernquistInf", "MooreInf", "GeneralizedNFWInf"),
)

model = base_model.clone()
for profile in profiles:
    model.halo_profile_model = profile
    model.halo_profile_params = {"alpha": 1.5} if profile.__name__ == "GeneralizedNFW" else {}
    ax[0, 0].plot(
        model.r, model.halo_profile.rho(model.r, np.array([1e12])), label=profile.__name__
    )
    ax[1, 0].plot(
        model.r,
        model.halo_profile.rho(model.r, np.array([1e12]))
        / base_model.halo_profile.rho(model.r, np.array([1e12])),
    )

    ax[0, 1].plot(model.m, model.halo_profile.rho(0.03, model.m), label=profile.__name__)
    ax[1, 1].plot(
        model.m, model.halo_profile.rho(0.03, model.m) / base_model.halo_profile.rho(0.03, model.m)
    )

ax[0, 0].set_yscale("log")
ax[0, 1].set_yscale("log")

ax[0, 0].set_ylim(1e9, 1e17)
ax[0, 0].set_xlim(1e-2, 0.3)
ax[0, 0].legend(ncol=2, fontsize=8)
ax[1, 0].set_ylim(0.1, 2)

ax[0, 1].set_yscale("log")
ax[0, 1].set_ylim(1e9, 1e17)
ax[0, 1].set_xlim(1e9, 1e17)

ax[1, 0].set_xlabel(r"r, [$h^{-1}{\rm Mpc}$]")
ax[0, 0].set_ylabel(r"$\rho(r|m)$")

ax[0, 0].set_title(r"m = $10^{12} h^{-1}M_\odot$")
ax[0, 1].set_title("r = $0.03 h^{-1}$Mpc")

ax[1, 1].set_xlabel(r"m, [$h^{-1}M_\odot$]")
ax[1, 0].set_ylabel(r"$\rho/\rho_{\rm NFW}$")
/tmp/ipykernel_35768/2928401422.py:26: RuntimeWarning: invalid value encountered in divide
  model.halo_profile.rho(model.r, np.array([1e12]))
/tmp/ipykernel_35768/2928401422.py:32: RuntimeWarning: invalid value encountered in divide
  model.m, model.halo_profile.rho(0.03, model.m) / base_model.halo_profile.rho(0.03, model.m)
/tmp/ipykernel_35768/2928401422.py:26: RuntimeWarning: divide by zero encountered in divide
  model.halo_profile.rho(model.r, np.array([1e12]))
/tmp/ipykernel_35768/2928401422.py:32: RuntimeWarning: divide by zero encountered in divide
  model.m, model.halo_profile.rho(0.03, model.m) / base_model.halo_profile.rho(0.03, model.m)
[36]:
Text(0, 0.5, '$\\rho/\\rho_{\\rm NFW}$')
../_images/examples_component-showcase_49_2.png

We show here the profile for six models, plotted against mass and radius. We omit the “Inf” versions of each profile, since they are the same but extend to infinity (and are less useful for the halo model itself since some of them contain infinite mass).