Halo–halo 2PCF¶
The halo–halo two-point correlation function xi_hh(r) is computed in a
periodic box with pycorr (Corrfunc
backend) for every auto and cross combination of the configured
log-mass bins.
Two flavours¶
halocat exposes two complementary entry points:
| Method | Result | Persisted? | Use for |
|---|---|---|---|
XiHHLoader.get |
All auto + cross pairs from the static MASS_BINS grid → XiHHRecord |
yes (xi_hh.hdf5) |
reproducible measurements you'll re-use across analyses |
XiHHLoader.measure_pair |
One ξ for an arbitrary (log10M1, log10M2) pair → XiHHPairRecord |
no | ad-hoc bins, parameter scans, exploratory analysis |
Static grid via get¶
The static grid is defined by MASS_BINS
and R_EDGES in halocat.config, and may be
overridden by scripts/config_xi_hh.yaml (see
Configuration).
from halocat import XiHHLoader
xi = XiHHLoader().get("LCDM", 0.25, imodel=1, ibox=1)
print(xi.pairs) # ['M0_M0', 'M0_M1', ..., 'Mp_Mp']
print(xi.xi.shape) # (n_pairs, n_r_bins)
print(xi.mass_bins) # (P, 2) [lo, hi] log10 M
Pair groups are named M{i}_M{j} with i <= j (autos plus
upper-triangular crosses). pair_indices gives the (i, j) pair as
integer columns.
Plotting r²ξ for the autos¶
import matplotlib.pyplot as plt
for k, (i, j) in enumerate(xi.pair_indices):
if i != j:
continue
lo, hi = xi.mass_bins[i]
plt.plot(xi.r[k], xi.r[k] ** 2 * xi.xi[k],
label=f"{xi.pairs[k]} log10M=[{lo:.2f}, {hi:.2f})")
plt.xscale("log")
plt.legend(fontsize=8)
Custom mass-bin pair via measure_pair¶
When you need xi_hh(r | bin1, bin2) for finite-width bins not in the
static grid:
loader = XiHHLoader()
# Auto-correlation of one custom bin
rec = loader.measure_pair(
"LCDM", 0.25, imodel=1, ibox=1,
log10M1=(13.0, 13.5), # log10M2 defaults to log10M1
)
rec.is_auto, rec.n1, rec.xi.shape
# Cross-correlation of two non-overlapping bins
rec_x = loader.measure_pair(
"LCDM", 0.25, 1, 1,
log10M1=(13.0, 13.3),
log10M2=(13.7, 14.0),
)
measure_pair:
- never writes anything to disk
- never reads
xi_hh.hdf5— it only loadshalo.hdf5(auto-reformatting the source.DATif missing) and runspycorrfor that one pair - accepts a custom
r_edges=...argument; the default isR_EDGES
The returned XiHHPairRecord
carries r, xi, r_edges, log10M1, log10M2, n1, n2 and an
attrs dict.
Bin-centre and empty-bin policy¶
Always arithmetic centres
Both code paths set r to the arithmetic mean of r_edges, never
pycorr.sepavg. sepavg returns NaN for empty bins and
fluctuates from realisation to realisation, which would prevent
sub-grid stacking from staying aligned.
Empty separation bins are flagged with xi = NaN. Empty mass-bin
selections (n1 == 0 or n2 == 0) yield an all-NaN row.
Stacking across iboxes¶
XiHHLoader.get_grid returns
arrays of shape (G, Z, M, B, P, K) for r and xi, plus per-pair
n1 / n2 of shape (G, Z, M, B, P) and a (G, Z, M, B) present
mask.
import numpy as np
grid = loader.get_grid(
gravities=["LCDM"], redshifts=[0.25],
imodels=[1], iboxes=[1, 2, 3, 4, 5],
)
xi_mean = np.nanmean(grid["xi"], axis=3) # mean over iboxes
Driver scripts¶
| Script | Purpose |
|---|---|
scripts/measure_xi_hh.py |
Batch measurement loop (reads YAML by default) |
scripts/load_xi_hh.py |
Inspect single records or a sub-grid summary |
scripts/plot_xi_hh.py |
One-panel r²ξ plot, one line per mass-bin pair |
See scripts/bookkeeping.md for the full flag list.