Skip to content

Commit

Permalink
Allowing for cross-correlations in HOD power spectrum. (#1224)
Browse files Browse the repository at this point in the history
* Allowing for cross-correlations in HOD power spectrum.

* Adding tests for consistency with Profile2pt.

---------

Co-authored-by: damonge <[email protected]>
  • Loading branch information
anicola and damonge authored Feb 13, 2025
1 parent 406b548 commit 4aa9c0b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
channel-priority: strict
show-channel-urls: true
miniforge-version: latest
miniforge-variant: Mambaforge
miniforge-variant: Miniforge3

- name: "macOS: set env variables"
if: matrix.label == 'osx-64'
Expand Down
29 changes: 21 additions & 8 deletions pyccl/halos/profiles_2pt.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ def fourier_2pt(self, cosmo, k, M, a, prof, *, prof2=None, diag=True):
prof2 (:class:`~pyccl.halos.profiles.hod.HaloProfileHOD` or :obj:`None`):
second halo profile for which the second-order moment
is desired. If ``None``, the assumption is that you want
an auto-correlation. Note that only auto-correlations
are allowed in this case.
an auto-correlation. If the profiles are different we assume disjoint
tracers and revert back to the usual Fourier-space two-point moment
(see Profile2pt).
diag (bool): If True, both halo profiles depend on the same k. If
False, they will depend on k and k', respectively and we will
approximate <uk uk'> to <uk><uk'>. The output dimension will
Expand All @@ -145,21 +146,33 @@ def fourier_2pt(self, cosmo, k, M, a, prof, *, prof2=None, diag=True):
if prof2 is None:
prof2 = prof

# If the profiles are different assume disjoint tracers
if prof != prof2:
raise ValueError("prof and prof2 must be equivalent")
uk1 = prof.fourier(cosmo, k, M, a)
uk2 = prof2.fourier(cosmo, k, M, a)

HOD = HaloProfileHOD
if not (isinstance(prof, HOD) and isinstance(prof2, HOD)):
raise TypeError("prof and prof2 must be HaloProfileHOD")

# TODO: This should be implemented in _fourier_variance
if (diag is True) or (isinstance(k, float)):
output = prof._fourier_variance(cosmo, k, M, a)
if prof == prof2:
output = prof._fourier_variance(cosmo, k, M, a)
else:
output = uk1 * uk2 * (1 + self.r_corr)
elif isinstance(M, float):
uk1 = prof.fourier(cosmo, k, M, a)
output = uk1[None, :] * uk1[:, None] * (1 + self.r_corr)
if prof == prof2:
uk1 = prof.fourier(cosmo, k, M, a)
output = uk1[None, :] * uk1[:, None] * (1 + self.r_corr)
else:
output = uk1[None, :] * uk2[:, None] * (1 + self.r_corr)
else:
uk1 = prof.fourier(cosmo, k, M, a)
output = uk1[:, None, :] * uk1[:, :, None] * (1 + self.r_corr)
if prof == prof2:
uk1 = prof.fourier(cosmo, k, M, a)
output = uk1[:, None, :] * uk1[:, :, None] * (1 + self.r_corr)
else:
output = uk1[:, None, :] * uk2[:, :, None] * (1 + self.r_corr)

return output

Expand Down
34 changes: 27 additions & 7 deletions pyccl/tests/test_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,12 @@ def test_hod_2pt():
pgood = ccl.halos.HaloProfileHOD(mass_def='200c', concentration=c)
pgood_b = ccl.halos.HaloProfileHOD(mass_def='200c', concentration=c)
p2 = ccl.halos.Profile2ptHOD()
p2pt = ccl.halos.Profile2pt()
F0 = p2.fourier_2pt(COSMO, 1., 1e13, 1., pgood, prof2=pgood)
assert np.allclose(p2.fourier_2pt(COSMO, 1., 1e13, 1., pgood, prof2=None),
F0, rtol=0)

# raises for non-HOD profile
with pytest.raises(TypeError):
p2.fourier_2pt(COSMO, 1., 1E13, 1., pbad)

Expand All @@ -337,26 +339,44 @@ def test_hod_2pt():

p2.fourier_2pt(COSMO, 1., 1E13, 1., pgood, prof2=pgood_b)

with pytest.raises(ValueError):
pgood_b.update_parameters(log10M0_0=10.)
p2.fourier_2pt(COSMO, 1., 1E13, 1., pgood, prof2=pgood_b)
# doesn't raise for two different HOD profiles
pgood_b.update_parameters(log10M0_0=10.)
F = p2.fourier_2pt(COSMO, 1., 1E13, 1., pgood, prof2=pgood_b)

with pytest.raises(ValueError):
# check consistency with Profile2pt
assert np.allclose(F, p2pt.fourier_2pt(COSMO, 1., 1E13, 1., pgood,
prof2=pgood_b))

# raises for non-HOD profile
with pytest.raises(TypeError):
p2.fourier_2pt(COSMO, 1., 1e13, 1., pgood, prof2=pbad)

# Test diag = False
F = p2.fourier_2pt(COSMO, 1., 1E13, 1., pgood, prof2=pgood, diag=False)
assert np.ndim(F) == 0
# check consistency with Profile2pt
F = p2.fourier_2pt(COSMO, 1., 1E13, 1., pgood, prof2=pgood_b, diag=False)
assert np.allclose(F, p2pt.fourier_2pt(COSMO, 1., 1E13, 1., pgood,
prof2=pgood_b, diag=False))
F = p2.fourier_2pt(COSMO, [1., 2], 1E13, 1., pgood, prof2=pgood,
diag=False)
assert F.shape == (2, 2)
# check consistency with Profile2pt
F = p2.fourier_2pt(COSMO, [1., 2], 1E13, 1., pgood, prof2=pgood_b,
diag=False)
assert np.allclose(F, p2pt.fourier_2pt(COSMO, [1., 2], 1E13, 1., pgood,
prof2=pgood_b, diag=False))
F = p2.fourier_2pt(COSMO, [1., 2], [1e12, 5e12, 1e13], 1., pgood,
prof2=pgood, diag=False)
assert F.shape == (3, 2, 2)
F2 = ccl.halos.Profile2pt().fourier_2pt(COSMO, [1., 2],
[1e12, 5e12, 1e13], 1., pgood,
diag=False)
F2 = p2pt.fourier_2pt(COSMO, [1., 2], [1e12, 5e12, 1e13], 1., pgood,
diag=False)
assert np.all(F == F2)
# check consistency with Profile2pt
F = p2.fourier_2pt(COSMO, [1., 2], [1e12, 5e12, 1e13], 1., pgood,
prof2=pgood_b, diag=False)
assert np.allclose(F, p2pt.fourier_2pt(COSMO, [1., 2], [1e12, 5e12, 1e13],
1., pgood, prof2=pgood_b, diag=False))


def test_2pt_rcorr_smoke():
Expand Down

0 comments on commit 4aa9c0b

Please sign in to comment.