CLensPy logo

CLensPy

Documentation Status License: MIT GitHub tag GitHub issues GitHub stars

Welcome to CLensPy, a Python package for cluster gravitational lensing analysis.

Overview

CLensPy provides a toolkit for computing weak-lensing observables from dark matter halo profiles: NFW and Einasto density profiles, projected surface density Sigma(R), excess surface density DeltaSigma(R), halo bias, and the two-halo term. It is designed to be:

  • Modular: each halo profile is a small, self-contained class with the same density / sigma / deltasigma interface

  • Fast: vectorized NumPy/SciPy implementations, with closed-form or series expansions used where available (e.g. the Einasto profile’s projected density)

  • Validated: cross-checked against independent codes (pyccl, cluster_toolkit, CLMM) in the test suite

Credits

The covariance modules are a refactor and an optimized version of the method and code of Wu et al. (2019, arXiv:1907.06611) and Hao-Yi Wu’s cluster-lensing-cov. See Covariance for what is theirs and what is new here.

CLensPy is also developed for and validated against the DES cluster cosmology analysis. We thank the DES cluster team, in particular Tom McClintock (cluster_toolkit; McClintock et al. 2019, arXiv:1805.00039) and the authors of y3_cluster_cpp. The cluster number-count and lensing forward model follows Aguena et al. 2023 (arXiv:2309.06593) and the optical selection-bias and projection-lensing model follows Costanzi et al. 2026 (PhRvD 113, 103508, arXiv:2604.05833). Full BibTeX is in the README.

Installation

CLensPy is not yet published on PyPI; install it from source:

git clone https://github.com/estevesjh/clenspy.git
cd clenspy
pip install -e .

See Installation for optional dependency groups (docs, compare, dev).

Quick Start

from clenspy.halo import EinastoProfile, NfwProfile

# h-free absolute units: mass in Msun, lengths in Mpc, densities in
# Msun/Mpc^3, wavenumbers in 1/Mpc -- no cosmology object needed, only the
# reference density rho_ref that mass_def is measured against (default:
# the comoving mean matter density, giving M_200m).
m200 = 1e14  # Msun
c200 = 5.0
nfw = NfwProfile(m200=m200, c200=c200)
print(f"r200 = {nfw.r200:.4f} Mpc, rs = {nfw.rs:.4f} Mpc, "
      f"rho_s = {nfw.rho_s:.3e} Msun/Mpc^3")

r = np.array([0.1, 0.5, 1.0, 2.0])  # Mpc
print("rho_NFW(r)     [Msun/Mpc^3] =", nfw.density(r))

# same r_s as the NFW halo (r_s = r200/c200), rho_0 solved so the enclosed
# mass at r200 matches m200 -- a fair shape-only comparison at fixed mass
# and scale radius. alpha=0.25 is a typical cluster shape (Retana-Montenegro
# et al. 2012 report alpha ~ 0.16-0.25 for clusters).
alpha = 0.25
rho0_unit = EinastoProfile(alpha=alpha, rho_0=1.0, r_s=nfw.rs, tol=1e-4)
rho0 = m200 / rho0_unit.enclosed_mass(nfw.r200)
einasto = EinastoProfile(alpha=alpha, rho_0=rho0, r_s=nfw.rs, tol=1e-4)
print("rho_Einasto(r) [Msun/Mpc^3] =", einasto.density(r))

# fourier() returns rho_tilde(k), the *unnormalized* FT -- units of mass,
# going to M as k -> 0, not the dimensionless mass-normalized u(k|M).
k = np.array([0.1, 1.0, 10.0])  # 1/Mpc
print("rho_tilde_NFW(k)     [Msun] =", nfw.fourier(k))
print("rho_tilde_Einasto(k) [Msun] =", einasto.fourier(k))

r200 = 1.4303 Mpc, rs = 0.2861 Mpc, rho_s = 3.547e+14 Msun/Mpc^3
rho_NFW(r)     [Msun/Mpc^3] = [5.57109391e+14 2.68756655e+13 5.02012592e+12 7.94381981e+11]
rho_Einasto(r) [Msun/Mpc^3] = [5.83600877e+14 2.77183496e+13 4.86300049e+12 6.13799586e+11]
rho_tilde_NFW(k)     [Msun] = [9.98998564e+13 9.05346969e+13 8.91993930e+12]
rho_tilde_Einasto(k) [Msun] = [1.83204136e+14 1.06342641e+14 9.27420738e+12]

This is one section of the same notebook every Theory page’s own example pulls from — see examples/getting_started.ipynb in the repository for the full runnable notebook, one section per physical effect, from the cosmology through the covariance.

API Reference

For a detailed breakdown of every class and function, see the API Reference.

Additional Resources