Corr_2PCF: Isotropic and Anisotropic Two-Point Statistics#
This notebook continues from quick_start.ipynb and uses its saved base SFCField for the isotropic and lightweight anisotropic calculations. It follows the same high-level-to-low-level progression as the other PyHermes examples:
run isotropic and anisotropic
Corr_2PCFtasks from YAML configurations;inspect saved
Corr2PCFDataproducts and make standard plots;override a high-level task from Python;
reconstruct \(\xi(s)\) directly from field convolution and spatial averaging;
construct custom isotropic and anisotropic binning windows with the low-level
WindowFuncAPI;load the saved production-resolution RSD results used in the paper.
The older exploratory cosine and disk/cylinder/cylshell scans are intentionally omitted so that the notebook remains a focused user guide and a companion to Section 4.3.1 of the paper.
[1]:
from pathlib import Path
import copy
import os
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.cm as cm
import numpy as np
from numba import njit
from pyhermes.base.sfc_projection import SFCProjection
from pyhermes.io import Corr2PCFData, SFCField, WindowFunc
from pyhermes.utils.plot import plot_corr2pcf_2d
from pyhermes.param.parambase import read_param
from pyhermes.theory.corr2pcf import Corr_2PCF
from pyhermes.utils.special_functions import jn_numba
cwd = Path.cwd().resolve()
if cwd.name == "notebooks" and cwd.parent.name == "examples":
PROJECT_ROOT = cwd.parent.parent
elif cwd.name == "examples":
PROJECT_ROOT = cwd.parent
elif (cwd / "examples").exists():
PROJECT_ROOT = cwd
else:
raise RuntimeError("Run this notebook from the project root or examples/notebooks.")
EXAMPLES_DIR = PROJECT_ROOT / "examples"
OUTPUT_DIR = EXAMPLES_DIR / "output"
FIGS_DIR = EXAMPLES_DIR / "figs"
FIGS_DIR.mkdir(exist_ok=True)
os.chdir(EXAMPLES_DIR)
THREADS = 8
SFC_PATH = OUTPUT_DIR / "quijote8000_snap004_sfc.pkl"
STYLE = {
"font.family": "serif",
"font.size": 16,
"axes.labelsize": 17,
"axes.titlesize": 17,
"legend.fontsize": 12,
"xtick.labelsize": 14,
"ytick.labelsize": 14,
"axes.grid": False,
}
COLORS = {"blue": "#1f77b4", "orange": "#ff7f0e", "green": "#2ca02c"}
plt.rcParams.update(STYLE)
print(f"project root: {PROJECT_ROOT}")
print(f"output dir : {OUTPUT_DIR}")
project root: /Users/xutengpeng/xutp/PycharmProjects/PyHermes
output dir : /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output
1. From configuration to result#
A two-point workflow has three main objects. Corr_2PCF describes and executes the estimator, Corr2PCFData reads its saved products, and WindowFunc provides the lower-level Fourier-space windows used by the estimator. Most users can begin with the first two objects and only use WindowFunc when defining a new binning geometry.
1.1 High-level YAML tasks#
The isotropic example uses configs/param_2pcf.yaml, while the lightweight anisotropic example uses configs/param_2pcf_smu_test.yaml. The latter deliberately contains only a \(6\times6\) sampling grid so that it can be run interactively.
[2]:
ISO_CONFIG = Path("./configs/param_2pcf.yaml")
SMU_TEST_CONFIG = Path("./configs/param_2pcf_smu_test.yaml")
iso_params = read_param(config_path=str(ISO_CONFIG))
smu_test_params = read_param(config_path=str(SMU_TEST_CONFIG))
print("Isotropic task:", iso_params["Corr_2PCF"])
print("Lightweight anisotropic task:", smu_test_params["Corr_2PCF"])
12:09:28 - INFO - pyhermes.param.parambase:ParamBase - Reading configure file: 'configs/param_2pcf.yaml'
12:09:28 - INFO - pyhermes.param.parambase:ParamBase - Input parameter file format is YAML
12:09:28 - INFO - pyhermes.param.parambase:ParamBase - Reading configure file: 'configs/param_2pcf_smu_test.yaml'
12:09:28 - INFO - pyhermes.param.parambase:ParamBase - Input parameter file format is YAML
Isotropic task: {'sfc_field': './output/quijote8000_snap004_sfc.pkl', 'random': 'uniform', 'binning_window': 'shell', 'sampling': {'s': {'min': 0.0, 'max': 150.0, 'n': 31}}, 'products': ['dd', 'dr', 'rd', 'xi'], 'threads': 2, 'fout_path': './output/quijote8000_snap004_2pcf.pkl'}
Lightweight anisotropic task: {'sfc_field': './output/quijote8000_snap004_sfc.pkl', 'random': 'uniform', 'binning_window': 'ring', 'sampling': {'s': {'min': 0.0, 'max': 150.0, 'n': 6}, 'mu': {'min': 0.0, 'max': 1.0, 'n': 6}}, 'threads': 4, 'products': 'xi', 'fout_path': './output/quijote8000_snap004_2pcf_smu_test.pkl'}
The repository runner reads the same YAML file on every MPI rank. These two commands generate the saved isotropic result and the lightweight \((s,\mu)\) result used below. On a machine without MPI, the same tasks can be executed through the Python interface in Section 1.3.
[3]:
! mpirun -np 2 python ./scripts/run_2pcf.py ./configs/param_2pcf.yaml
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Reading configure file: './configs/param_2pcf.yaml'
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Input parameter file format is YAML
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.sfc_field' from 'empty' to './output/quijote8000_snap004_sfc.pkl'
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.random' from 'None' to 'uniform'
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.binning_window'.
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.sampling'.
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.products' from 'xi' to '['dd', 'dr', 'rd', 'xi']'
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.threads' from '1' to '2'
12:09:29 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.fout_path' from 'empty' to './output/quijote8000_snap004_2pcf.pkl'
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 2 MPI ranks with 2 threads per rank
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=31, min=0.0, max=150.0, threads=2
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[dd, dr, rd, xi] | computed=[dd, dr, rd, delta_dd, rr] | derived=[xi]
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=shell | mapping=s_to_R | kernel_mode=octant
12:09:29 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> ./output/quijote8000_snap004_sfc.pkl <---
12:09:29 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:09:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:09:30 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:09:30 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:09:30 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:09:30 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:09:30 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.4789 sec
12:09:30 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=31, ranks=2, per_rank=15-16
12:09:30 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:09:33 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 9.68%
12:09:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 22.58%
12:09:36 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 29.03%
12:09:38 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 41.94%
12:09:39 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 48.39%
12:09:41 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 61.29%
12:09:42 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 67.74%
12:09:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 80.65%
12:09:46 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 87.10%
12:09:48 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:09:48 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 17.9009 sec
12:09:48 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:09:48 - WARNING - pyhermes.io.funcs:check_fout - Output file './output/quijote8000_snap004_2pcf.pkl' already exists and will be overwritten (overwrite=True).
12:09:48 - INFO - pyhermes.io.base:Corr2PCFData - Writing 2PCF data to ---> ./output/quijote8000_snap004_2pcf.pkl <---
12:09:48 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 18.3830 sec
[4]:
! mpirun -np 2 python ./scripts/run_2pcf.py ./configs/param_2pcf_smu_test.yaml
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Reading configure file: './configs/param_2pcf_smu_test.yaml'
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Input parameter file format is YAML
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.sfc_field' from 'empty' to './output/quijote8000_snap004_sfc.pkl'
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.random' from 'None' to 'uniform'
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.binning_window'.
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.sampling'.
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.threads' from '1' to '4'
12:09:49 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.fout_path' from 'empty' to './output/quijote8000_snap004_2pcf_smu_test.pkl'
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 2 MPI ranks with 4 threads per rank
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=6, min=0.0, max=150.0 | mu: n=6, min=0.0, max=1.0, threads=4
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=ring | mapping=smu_to_RH | kernel_mode=auto | los=(0.0, 0.0, 1.0)
12:09:49 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> ./output/quijote8000_snap004_sfc.pkl <---
12:09:49 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.1969 sec
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=36, ranks=2, per_rank=18-18
12:09:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:09:52 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 8.33%
12:09:52 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 19.44%
12:09:53 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 25.00%
12:09:54 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 36.11%
12:09:54 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 41.67%
12:09:56 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 52.78%
12:09:56 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 58.33%
12:09:57 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 66.67%
12:09:58 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 77.78%
12:09:59 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 83.33%
12:10:00 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 94.44%
12:10:00 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:10:00 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 10.4481 sec
12:10:00 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:10:00 - WARNING - pyhermes.io.funcs:check_fout - Output file './output/quijote8000_snap004_2pcf_smu_test.pkl' already exists and will be overwritten (overwrite=True).
12:10:00 - INFO - pyhermes.io.base:Corr2PCFData - Writing 2PCF data to ---> ./output/quijote8000_snap004_2pcf_smu_test.pkl <---
12:10:00 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 10.6471 sec
1.2 Saved result objects#
Corr2PCFData exposes the requested count products and derived correlation function as arrays. The isotropic configuration requests dd, dr, rd, and xi; rr is also available because it is required by the estimator. The first plot verifies the stored \(\xi\) against the explicit Landy–Szalay combination.
[5]:
corr_s = Corr2PCFData(data_path="./output/quijote8000_snap004_2pcf.pkl", threads=THREADS)
fig, ax = plt.subplots(figsize=(7.2, 4.8))
ax.plot(corr_s.s, corr_s.s**2 * corr_s.xi, color=COLORS["blue"], lw=2.2, label=r"stored $\xi$")
if all(getattr(corr_s, name, None) is not None for name in ("dd", "dr", "rd", "rr")):
xi_landy_szalay = (corr_s.dd - corr_s.dr - corr_s.rd + corr_s.rr) / corr_s.rr
ax.plot(corr_s.s, corr_s.s**2 * xi_landy_szalay, "--", color=COLORS["orange"], lw=2.2, label=r"from $DD$, $DR$, $RD$, $RR$")
ax.set_xlabel(r"$s\ (h^{-1}\mathrm{Mpc})$")
ax.set_ylabel(r"$s^2\xi(s)$")
ax.legend(frameon=False)
fig.tight_layout()
plt.show()
12:10:00 - INFO - pyhermes.io.base:Corr2PCFData - Reading 2PCF data from ---> ./output/quijote8000_snap004_2pcf.pkl <---
12:10:00 - INFO - pyhermes.io.base:Corr2PCFData - s: Num = 31, Min = 0, Max = 150
12:10:00 - INFO - pyhermes.io.base:Corr2PCFData - Products loaded: ['dd', 'dr', 'rd', 'delta_dd', 'rr', 'xi']
[6]:
corr_smu_test = Corr2PCFData(
data_path="./output/quijote8000_snap004_2pcf_smu_test.pkl",
threads=THREADS,
)
plot_corr2pcf_2d(corr_smu_test, s_min=0.0)
plt.tight_layout()
plt.show()
12:10:01 - INFO - pyhermes.io.base:Corr2PCFData - Reading 2PCF data from ---> ./output/quijote8000_snap004_2pcf_smu_test.pkl <---
12:10:01 - INFO - pyhermes.io.base:Corr2PCFData - s: Num = 6, Min = 0, Max = 150
12:10:01 - INFO - pyhermes.io.base:Corr2PCFData - mu: Num = 6, Min = 0, Max = 1
12:10:01 - INFO - pyhermes.io.base:Corr2PCFData - Products loaded: ['delta_dd', 'rr', 'xi']
1.3 Run and override a task in Python#
The YAML dictionary can also initialize Corr_2PCF directly. Attributes assigned afterwards override the corresponding configuration entries. Here the sampling grid is shortened for an interactive run, and save_result=False returns the result without replacing the production output file.
[7]:
interactive_params = copy.deepcopy(iso_params)
interactive_task = Corr_2PCF(param_task=interactive_params)
interactive_task.sampling["s"] = np.linspace(5.0, 150.0, 16)
interactive_task.products = "xi"
interactive_task.threads = THREADS
corr_s_interactive = interactive_task.run(save_result=False)
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.sfc_field' from 'empty' to './output/quijote8000_snap004_sfc.pkl'
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.random' from 'None' to 'uniform'
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.binning_window'.
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.sampling'.
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.products' from 'xi' to '['dd', 'dr', 'rd', 'xi']'
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.threads' from '1' to '2'
12:10:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.fout_path' from 'empty' to './output/quijote8000_snap004_2pcf.pkl'
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=16, min=5.0, max=150.0, source=explicit array, threads=8
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=shell | mapping=s_to_R | kernel_mode=octant
12:10:01 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> ./output/quijote8000_snap004_sfc.pkl <---
12:10:01 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.2975 sec
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=16, ranks=1, per_rank=16-16
12:10:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:10:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 6.25%
12:10:03 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 12.50%
12:10:03 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 18.75%
12:10:03 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 25.00%
12:10:03 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 31.25%
12:10:03 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 37.50%
12:10:03 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 43.75%
12:10:04 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:10:04 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 56.25%
12:10:04 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 62.50%
12:10:04 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 68.75%
12:10:04 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 75.00%
12:10:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 81.25%
12:10:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 87.50%
12:10:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 93.75%
12:10:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:10:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 4.0291 sec
12:10:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:10:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 4.3271 sec
2. Direct low-level reconstruction of \(\xi(s)\)#
The high-level Corr_2PCF task evaluates the same field–window–field operation at every requested separation. This section exposes that operation directly, following the low-level reconstruction style used in corr3pcf.ipynb.
For a uniform random field, let
After the optional spherical smoothing \(\Delta D_w=\Delta D@W_{\rm sphere}\), the shell-binned estimator at one separation is
Here @ performs the Fourier-space convolution, multiplication forms the local field product, and as_array().mean() takes its spatial average. Consequently, the helper below can evaluate one chosen \(s\) without constructing a complete Corr_2PCF task.
[8]:
# Build the multiresolution coefficient field directly from the catalogue.
projection_params = read_param(config_path="./configs/param_sfc_projection.yaml")
projection_params["SFCProjection"]["threads"] = THREADS
D = SFCProjection(projection_params).run(save_result=False)
D.threads = THREADS
# For the uniform-random shortcut, the random field is the mean density rho.
rho = D.field_mean_density(value_unit="grid")
RR = rho ** 2
deltaD = D - rho
# Smooth the fluctuation field with a spherical top-hat of radius 5 h^-1 Mpc.
W_smooth = WindowFunc(
{"type": "sphere", "len_args": {"R": 5.0}},
D.sfc_info,
threads=THREADS,
)
deltaD_w = deltaD @ W_smooth
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Reading configure file: './configs/param_sfc_projection.yaml'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Input parameter file format is YAML
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <base> ...
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.fin.path' from 'empty' to 'https://pyhermes.astroslacker.com/downloads/group_tab_004.0'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.fin.format' from 'bin' to 'fof'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.fin.download.cache_path' from 'empty' to './data/quijote_halos/8000/groups_004/group_tab_004.0'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.fin.download.sha256' from 'empty' to '4a1c6ca4f6747a70e9e552685226ecf5d678c6c97551e2caa7cc3883502eac85'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.threads' from '1' to '8'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.save_particle_data' from 'False' to 'True'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.particle_data_path' from 'empty' to './output/quijote8000_snap004_particles.npz'
12:10:05 - INFO - pyhermes.param.parambase:ParamBase - Default 'SFCProjection.fout_path' from 'empty' to './output/quijote8000_snap004_sfc.pkl'
12:10:05 - INFO - pyhermes.pipeline.pipeline:SFCProjection - SFCProjection runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:10:05 - INFO - pyhermes.pipeline.pipeline:SFCProjection - Preparing SFCProjection input fields ...
12:10:05 - INFO - pyhermes.pipeline.pipeline:SFCProjection - J=8, L=256, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:10:05 - INFO - pyhermes.io.resources:resolve_data_path - Using cached particle data: data/quijote_halos/8000/groups_004/group_tab_004.0
12:10:05 - INFO - pyhermes.io.readers:read_particle_data - Selected input particle format: fof
12:10:05 - INFO - pyhermes.io.readers:read_fof - Reading Quijote FoF halo data from ---> data/quijote_halos/8000/groups_004/group_tab_004.0 <---
12:10:05 - INFO - pyhermes.pipeline.pipeline:SFCProjection - Input particles ready | source=file=path=https://pyhermes.astroslacker.com/downloads/group_tab_004.0 format=fof | particle_count=406728 | catalog_weight_key=None | field_value_key=None | weight_normalization=catalog | catalog_weight_sum=4.067280e+05 | field_integral=1.000000e+00
12:10:05 - INFO - pyhermes.pipeline.pipeline:SFCProjection - Saved particle positions, catalogue weights, and field values to ./output/quijote8000_snap004_particles.npz
12:10:05 - INFO - pyhermes.pipeline.pipeline:SFCProjection - Single process mode
12:10:06 - INFO - pyhermes.pipeline.pipeline:SFCProjection - The time for scaling function: 0.9606 sec
12:10:06 - INFO - pyhermes.pipeline.pipeline:SFCProjection - The time for task: 1.0016 sec
[9]:
def xi_at_s(delta_field, s, RR, threads=THREADS):
"""Evaluate the shell-binned 2PCF at one separation s."""
W_shell = WindowFunc(
{"type": "shell", "len_args": {"R": float(s)}},
delta_field.sfc_info,
threads=threads,
)
local_pair_product = (delta_field @ W_shell) * delta_field
return float(local_pair_product.as_array().mean() / RR)
s_demo = 40.0
xi_demo = xi_at_s(deltaD_w, s_demo, RR)
print(f"xi({s_demo:.1f} h^-1 Mpc) = {xi_demo:.6e}")
xi(40.0 h^-1 Mpc) = 3.198802e-02
[10]:
s_direct = np.linspace(0.0, 150.0, 26)
xi_direct = np.array([xi_at_s(deltaD_w, s, RR) for s in s_direct])
with plt.rc_context(STYLE):
fig, ax = plt.subplots(figsize=(7.2, 4.8))
ax.plot(s_direct, s_direct ** 2 * xi_direct, color=COLORS["blue"], lw=2.2)
ax.axhline(0.0, color="0.75", lw=0.9, zorder=0)
ax.set_xlabel(r"$s\ [h^{-1}\mathrm{Mpc}]$")
ax.set_ylabel(r"$s^2\xi(s)$")
ax.tick_params(direction="in", top=True, right=True, width=1.0, length=4)
for spine in ax.spines.values():
spine.set_linewidth(1.0)
fig.tight_layout()
plt.show()
3. Low-level isotropic binning windows#
The high-level shell setting used above selects the elementary isotropic binning window. PyHermes also provides a built-in finite-width thick_shell, whose Fourier kernel is the normalized difference of two spheres, while a Gaussian shell replaces the sharp radial bin with a smooth radial profile. These examples run directly in the notebook because they only evaluate a one-dimensional sampling grid.
[11]:
def make_thick_shell_binning_window(delta_R):
return {
"type": "thick_shell",
"len_args": {"R": None, "delta_R": float(delta_R)},
"mapping": "s_to_R",
"kernel_mode": "octant",
}
def make_gaussian_shell_binning_window(R_smooth):
def mapping_s_to_gaussian_shell(sample, binning_window):
params = copy.deepcopy(binning_window)
params["len_args"]["R_shell"] = float(sample["s"])
params["len_args"]["R_smooth"] = float(R_smooth)
return params
return {
"type": "gaussian_shell",
"len_args": ["R_shell", "R_smooth"],
"mapping": mapping_s_to_gaussian_shell,
"kernel_mode": "octant",
}
def run_isotropic_xi_s(binning_window, s_values, threads=THREADS):
task = Corr_2PCF()
task.sfc_field1 = str(SFC_PATH)
task.sfc_field2 = str(SFC_PATH)
task.random1 = "uniform"
task.random2 = "uniform"
task.binning_window = copy.deepcopy(binning_window)
task.sampling["s"] = np.asarray(s_values, dtype=float)
task.products = "xi"
task.threads = threads
return task.run(save_result=False)
[12]:
s_values = np.linspace(5.0, 150.0, 30)
iso_curves = {
"thin shell": run_isotropic_xi_s("shell", s_values),
r"thick shell ($\Delta R=5$)": run_isotropic_xi_s(make_thick_shell_binning_window(5.0), s_values),
r"Gaussian shell ($\sigma_R=5$)": run_isotropic_xi_s(make_gaussian_shell_binning_window(5.0), s_values),
r"thick shell ($\Delta R=10$)": run_isotropic_xi_s(make_thick_shell_binning_window(10.0), s_values),
r"Gaussian shell ($\sigma_R=10$)": run_isotropic_xi_s(make_gaussian_shell_binning_window(10.0), s_values),
}
12:10:13 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=30, min=5.0, max=150.0, source=explicit array, threads=8
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=shell | mapping=s_to_R | kernel_mode=octant
12:10:13 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl <---
12:10:13 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:10:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.3477 sec
12:10:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=30, ranks=1, per_rank=30-30
12:10:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:10:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 10.00%
12:10:15 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 20.00%
12:10:16 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 30.00%
12:10:17 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 40.00%
12:10:17 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:10:19 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 60.00%
12:10:20 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 70.00%
12:10:21 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 80.00%
12:10:21 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 90.00%
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 8.2295 sec
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 8.5777 sec
12:10:22 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=30, min=5.0, max=150.0, source=explicit array, threads=8
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=thick_shell | mapping=s_to_R | kernel_mode=octant
12:10:22 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl <---
12:10:22 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.2295 sec
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=30, ranks=1, per_rank=30-30
12:10:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:10:23 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 10.00%
12:10:24 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 20.00%
12:10:25 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 30.00%
12:10:27 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 40.00%
12:10:28 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:10:29 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 60.00%
12:10:31 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 70.00%
12:10:33 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 80.00%
12:10:34 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 90.00%
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 12.6899 sec
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 12.9201 sec
12:10:35 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=30, min=5.0, max=150.0, source=explicit array, threads=8
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=gaussian_shell | mapping=mapping_s_to_gaussian_shell | kernel_mode=octant
12:10:35 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl <---
12:10:35 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.5016 sec
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=30, ranks=1, per_rank=30-30
12:10:35 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:10:37 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 10.00%
12:10:38 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 20.00%
12:10:38 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 30.00%
12:10:39 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 40.00%
12:10:40 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:10:41 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 60.00%
12:10:41 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 70.00%
12:10:42 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 80.00%
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 90.00%
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 8.1349 sec
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 8.6376 sec
12:10:43 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=30, min=5.0, max=150.0, source=explicit array, threads=8
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:10:43 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=thick_shell | mapping=s_to_R | kernel_mode=octant
12:10:43 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl <---
12:10:44 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.2506 sec
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=30, ranks=1, per_rank=30-30
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:10:44 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 10.00%
12:10:45 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 20.00%
12:10:46 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 30.00%
12:10:47 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 40.00%
12:10:47 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:10:48 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 60.00%
12:10:49 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 70.00%
12:10:50 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 80.00%
12:10:50 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 90.00%
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 7.3966 sec
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 7.6476 sec
12:10:51 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=30, min=5.0, max=150.0, source=explicit array, threads=8
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=gaussian_shell | mapping=mapping_s_to_gaussian_shell | kernel_mode=octant
12:10:51 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl <---
12:10:51 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=/Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.2480 sec
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=30, ranks=1, per_rank=30-30
12:10:51 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:10:52 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 10.00%
12:10:53 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 20.00%
12:10:54 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 30.00%
12:10:54 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 40.00%
12:10:55 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:10:56 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 60.00%
12:10:56 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 70.00%
12:10:57 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 80.00%
12:10:58 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 90.00%
12:10:59 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:10:59 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 7.2224 sec
12:10:59 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:10:59 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 7.4709 sec
[13]:
with plt.rc_context(STYLE):
fig, ax = plt.subplots(figsize=(7.2, 4.8))
styles = {
"thin shell": dict(color="0.20", ls="-", lw=2.4),
r"thick shell ($\Delta R=5$)": dict(color=COLORS["blue"], ls="-", lw=2.2),
r"Gaussian shell ($\sigma_R=5$)": dict(color=COLORS["blue"], ls="--", lw=2.2),
r"thick shell ($\Delta R=10$)": dict(color=COLORS["orange"], ls="-", lw=2.2),
r"Gaussian shell ($\sigma_R=10$)": dict(color=COLORS["orange"], ls="--", lw=2.2),
}
for label, data in iso_curves.items():
ax.plot(data.s, data.s ** 2 * data.xi, label=label, **styles[label])
ax.set_xlabel(r"$s\ [h^{-1}\mathrm{Mpc}]$")
ax.set_ylabel(r"$s^2\xi(s)$")
ax.grid(False)
ax.legend(frameon=False, ncol=1)
ax.tick_params(direction="in", top=True, right=True, width=1.0, length=4)
for spine in ax.spines.values():
spine.set_linewidth(1.0)
fig.tight_layout()
fig.savefig(FIGS_DIR / "corr2pcf_binning_window_shell_gaussian_shell_comparison.png", dpi=240, bbox_inches="tight")
plt.show()
4. Low-level anisotropic window composition#
For the redshift-space 2PCF, the usual ring window can be factored into a transverse part and a line-of-sight part. The Gaussian-ring variant uses the same line-of-sight factor but multiplies the transverse ring by a Gaussian factor. The first cell demonstrates this lower-dimensional composition explicitly. The following cell then packages the same Gaussian ring into one custom Fourier-space kernel and supplies a callable mapping from each \((s,\mu)\) sample to its transverse radius
\(R\) and line-of-sight displacement \(H\). This lets the ordinary Corr_2PCF task use the custom geometry without defining a task subclass.
[14]:
@njit
def kernel_ring_2d(ki, kj, kk, R):
k_perp = np.sqrt(ki * ki + kj * kj)
return jn_numba(0, 2.0 * np.pi * k_perp * R)
@njit
def kernel_gauss_2d(ki, kj, kk, sigma_perp):
k_perp = np.sqrt(ki * ki + kj * kj)
q = 2.0 * np.pi * k_perp * sigma_perp
return np.exp(-0.5 * q * q)
@njit
def kernel_double_delta_1d(ki, kj, kk, H):
return np.cos(2.0 * np.pi * kk * H)
reference_field = SFCField(data_path=str(SFC_PATH), threads=THREADS)
R_demo = 30.0
H_demo = 20.0
sigma_demo = 5.0
W_ring_2d = WindowFunc(
{"type": "ring_2d", "func": kernel_ring_2d, "len_args": {"R": R_demo}, "kernel_mode": "octant"},
reference_field.sfc_info,
threads=THREADS,
)
W_gauss_2d = WindowFunc(
{"type": "gauss_2d", "func": kernel_gauss_2d, "len_args": {"sigma_perp": sigma_demo}, "kernel_mode": "octant"},
reference_field.sfc_info,
threads=THREADS,
)
W_los_1d = WindowFunc(
{"type": "double_delta_1d", "func": kernel_double_delta_1d, "len_args": {"H": H_demo}, "kernel_mode": "octant"},
reference_field.sfc_info,
threads=THREADS,
)
W_ring_3d = W_ring_2d * W_los_1d
W_gauss_ring_3d = W_ring_2d * W_gauss_2d * W_los_1d
print("Constructed W_ring_3d and W_gauss_ring_3d from lower-dimensional factors.")
12:10:59 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_sfc.pkl <---
12:10:59 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
Constructed W_ring_3d and W_gauss_ring_3d from lower-dimensional factors.
[15]:
@njit
def window_function_gaussian_ring_numba(ki, kj, kk, R, H, sigma_perp):
k_perp = np.sqrt(ki * ki + kj * kj)
q_ring = 2.0 * np.pi * k_perp * R
q_smooth = 2.0 * np.pi * k_perp * sigma_perp
q_parallel = 2.0 * np.pi * kk * H
return (
jn_numba(0, q_ring)
* np.exp(-0.5 * q_smooth * q_smooth)
* np.cos(q_parallel)
)
def mapping_smu_to_gaussian_ring(sample, binning_window):
params = copy.deepcopy(binning_window)
s = float(sample["s"])
mu = float(sample["mu"])
params["len_args"]["R"] = s * np.sqrt(max(0.0, 1.0 - mu * mu))
params["len_args"]["H"] = s * mu
return params
gaussian_ring_win_params = {
"type": "gaussian_ring",
"func": window_function_gaussian_ring_numba,
"len_args": {"R": None, "H": None, "sigma_perp": 5.0},
"mapping": mapping_smu_to_gaussian_ring,
"kernel_mode": "octant",
}
4.1 Lightweight custom-window tasks#
The same param_2pcf_smu_test.yaml configuration now provides a small common sampling grid for both the built-in ring and the custom Gaussian-ring dictionary. The custom dictionary is assigned directly to task.binning_window, so the standard task handles its callable kernel and \((s,\mu)\) mapping exactly like a built-in geometry. Running both estimators on this grid is a quick functional check before loading the denser production results.
[16]:
ring_test_task = Corr_2PCF(param_task=copy.deepcopy(smu_test_params))
ring_test_task.threads = THREADS
corr_ring_test = ring_test_task.run(save_result=False)
gauss_ring_test_task = Corr_2PCF(param_task=copy.deepcopy(smu_test_params))
gauss_ring_test_task.binning_window = copy.deepcopy(gaussian_ring_win_params)
gauss_ring_test_task.threads = THREADS
corr_gauss_ring_test = gauss_ring_test_task.run(save_result=False)
12:11:01 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:11:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.sfc_field' from 'empty' to './output/quijote8000_snap004_sfc.pkl'
12:11:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.random' from 'None' to 'uniform'
12:11:01 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.binning_window'.
12:11:01 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.sampling'.
12:11:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.threads' from '1' to '4'
12:11:01 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.fout_path' from 'empty' to './output/quijote8000_snap004_2pcf_smu_test.pkl'
12:11:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:11:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:11:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:11:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=6, min=0.0, max=150.0 | mu: n=6, min=0.0, max=1.0, threads=8
12:11:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:11:01 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=ring | mapping=smu_to_RH | kernel_mode=auto | los=(0.0, 0.0, 1.0)
12:11:01 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> ./output/quijote8000_snap004_sfc.pkl <---
12:11:02 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.3807 sec
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=36, ranks=1, per_rank=36-36
12:11:02 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:11:03 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 8.33%
12:11:04 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 16.67%
12:11:05 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 25.00%
12:11:06 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 33.33%
12:11:08 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 41.67%
12:11:09 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:11:10 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 58.33%
12:11:11 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 66.67%
12:11:12 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 75.00%
12:11:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 83.33%
12:11:13 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 91.67%
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 12.5032 sec
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 12.8844 sec
12:11:14 - INFO - pyhermes.param.parambase:ParamBase - Set default parameters of module <theory> ...
12:11:14 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.sfc_field' from 'empty' to './output/quijote8000_snap004_sfc.pkl'
12:11:14 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.random' from 'None' to 'uniform'
12:11:14 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.binning_window'.
12:11:14 - INFO - pyhermes.param.parambase:ParamBase - Using user-provided replacement value for 'Corr_2PCF.sampling'.
12:11:14 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.threads' from '1' to '4'
12:11:14 - INFO - pyhermes.param.parambase:ParamBase - Default 'Corr_2PCF.fout_path' from 'empty' to './output/quijote8000_snap004_2pcf_smu_test.pkl'
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF runtime configuration: running on 1 MPI ranks with 8 threads per rank
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization rule | task weight_normalization=catalog | catalog fields are converted to the task rule; derived fields are used as-is except for task='unit'.
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Preparing Corr_2PCF input fields ...
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling: s: n=6, min=0.0, max=150.0 | mu: n=6, min=0.0, max=1.0, threads=8
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Products: requested=[xi] | computed=[delta_dd, rr] | derived=[xi]
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Binning window: type=gaussian_ring | mapping=mapping_smu_to_gaussian_ring | kernel_mode=octant
12:11:14 - INFO - pyhermes.io.base:SFCField - Reading SFCField data from ---> ./output/quijote8000_snap004_sfc.pkl <---
12:11:14 - INFO - pyhermes.io.base:SFCField - epsilon: Shape(256, 256, 256), Min = -3.215e-06, Max = 1.343e-05, Mean = 5.96e-08, Sum = 1
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Corr_2PCF input compatibility check passed.
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Shared required parameters | J=8, box_size=1000.0, phi_resolution=1024, wavelet_mode=db2, wavelet_level=10
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 1 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field weight normalization | field_kind=catalog_field | input=catalog | task=catalog | effective=catalog
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Field vertex 2 ready | source=path=./output/quijote8000_snap004_sfc.pkl | window=no additional window convolution
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 1 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Random vertex 2 ready | source=uniform random density | window=uniform shortcut | rho=5.96046e-08
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Start to calculate 2PCF ...
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Pre-2PCF setup time: 0.2600 sec
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Sampling tasks: total=36, ranks=1, per_rank=36-36
12:11:14 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 0.00%
12:11:16 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 8.33%
12:11:16 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 16.67%
12:11:17 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 25.00%
12:11:18 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 33.33%
12:11:19 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 41.67%
12:11:20 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 50.00%
12:11:20 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 58.33%
12:11:21 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 66.67%
12:11:22 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 75.00%
12:11:23 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 83.33%
12:11:24 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 91.67%
12:11:24 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Progress: 100.00%
12:11:24 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - 2PCF main loop time: 9.8515 sec
12:11:24 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - Main 2PCF loop finished, gathering results on rank 0 ...
12:11:24 - INFO - pyhermes.pipeline.pipeline:Corr_2PCF - The time for task: 10.1120 sec
[17]:
plot_corr2pcf_2d(
corr_ring_test,
corr_gauss_ring_test,
label1="ring",
label2="Gaussian ring",
s_min=0.0,
)
plt.tight_layout()
plt.show()
5. Saved production-resolution RSD results#
The interactive checks above use the \(6\times6\) grid from param_2pcf_smu_test.yaml. The final figure instead reads the denser, previously saved redshift-space results used in the paper. Keeping production execution outside this notebook makes Run All practical while preserving the full-resolution scientific comparison.
Generate the two files from examples/ with:
python scripts/run_2pcf.py configs/param_2pcf_smu.yaml
python scripts/run_2pcf_smu_gaussian_ring.py configs/param_2pcf_smu.yaml 5.0
For a cluster run, the matching entry points are slurm/run_2pcf_smu.slurm and slurm/run_2pcf_smu_gaussian_ring.slurm.
[18]:
ring_output = OUTPUT_DIR / "quijote8000_snap004_rsd_2pcf_smu.pkl"
gauss_ring_output = OUTPUT_DIR / "quijote8000_snap004_rsd_2pcf_smu_gaussian_ring_sigma5.pkl"
missing = [path for path in (ring_output, gauss_ring_output) if not path.exists()]
if missing:
raise FileNotFoundError("Missing production output(s): " + ", ".join(map(str, missing)))
print(f"ring result : {ring_output}")
print(f"Gaussian-ring result: {gauss_ring_output}")
ring result : /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_rsd_2pcf_smu.pkl
Gaussian-ring result: /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_rsd_2pcf_smu_gaussian_ring_sigma5.pkl
5.1 Ring versus Gaussian ring#
The plotted quantity is (\log`(1+:nbsphinx-math:xi`)). The two panels use the same color scale and contour levels so that the response to the transverse Gaussian factor can be read directly.
[19]:
corr_ring = Corr2PCFData(data_path=str(ring_output), threads=THREADS)
corr_gauss_ring = Corr2PCFData(data_path=str(gauss_ring_output), threads=THREADS)
plot_kwargs = dict(
s_min=0.0,
add_contour=True,
value="log10_1p_xi",
cmap="viridis",
vmin=-0.0005,
vmax=0.28,
n_levels=121,
contour_levels=[-0.001, 0.003, 0.010, 0.025, 0.050, 0.100, 0.200],
contour_kwargs={"colors": "yellow", "linewidths": 0.9, "linestyles": "dashdot"},
center_line_kwargs={"color": "black", "lw": 0.8, "alpha": 0.95, "zorder": 20},
label_fontsize=13,
tick_fontsize=10,
text_fontsize=12,
spine_linewidth=0.9,
)
fig, axes = plt.subplots(1, 2, figsize=(8.1, 3.9), sharex=True, sharey=True)
fig.subplots_adjust(left=0.08, right=0.86, bottom=0.15, top=0.93, wspace=0.22)
for ax, corr, label in zip(axes, [corr_ring, corr_gauss_ring], ["ring", "Gauss-ring"]):
plot_corr2pcf_2d(corr, ax=ax, label1=label, add_colorbar=False, **plot_kwargs)
ax.set_xlim(-100, 100)
ax.set_ylim(-100, 100)
ax.set_aspect("equal")
axes[1].set_ylabel("")
axes[1].tick_params(labelleft=False)
norm = mcolors.Normalize(vmin=plot_kwargs["vmin"], vmax=plot_kwargs["vmax"])
sm = cm.ScalarMappable(norm=norm, cmap=plot_kwargs["cmap"])
sm.set_array([])
cax = fig.add_axes([0.89, 0.23, 0.025, 0.54])
cbar = fig.colorbar(sm, cax=cax, orientation="vertical", extend="both")
cbar.set_label(r"$\log(1+\xi)$", fontsize=13)
cbar.ax.tick_params(labelsize=10)
fig.savefig(FIGS_DIR / "corr2pcf_ring_gaussian_ring_smu.png", dpi=240, bbox_inches="tight")
plt.show()
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - Reading 2PCF data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_rsd_2pcf_smu.pkl <---
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - s: Num = 46, Min = 0, Max = 180
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - mu: Num = 51, Min = 0, Max = 1
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - Products loaded: ['delta_dd', 'rr', 'xi']
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - Reading 2PCF data from ---> /Users/xutengpeng/xutp/PycharmProjects/PyHermes/examples/output/quijote8000_snap004_rsd_2pcf_smu_gaussian_ring_sigma5.pkl <---
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - s: Num = 46, Min = 0, Max = 180
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - mu: Num = 51, Min = 0, Max = 1
12:11:25 - INFO - pyhermes.io.base:Corr2PCFData - Products loaded: ['delta_dd', 'rr', 'xi']
6. Summary#
YAML files and
Corr_2PCFprovide the ordinary high-level workflow.Corr2PCFDataseparates task execution from plotting and downstream analysis.The direct field–window–field calculation exposes how one value of \(\xi(s)\) is formed.
WindowFuncand a small task subclass expose the low-level extension point for a new binning geometry.Isotropic and test-grid anisotropic calculations run in the notebook; the last figure reads saved production-resolution RSD results.
Next steps#
This notebook completes the two-point branch, from the Quick Start estimator to anisotropic production outputs and custom binning windows. Continue with corr3pcf.ipynb to move from pairs to angular and multipole triangle statistics.