orpheus-npcf 0.2.1__cp310-cp310-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orpheus/__init__.py +9 -0
- orpheus/catalog.py +1216 -0
- orpheus/covariance.py +153 -0
- orpheus/direct.py +1091 -0
- orpheus/flat2dgrid.py +68 -0
- orpheus/npcf_base.py +766 -0
- orpheus/npcf_fourth.py +1716 -0
- orpheus/npcf_second.py +620 -0
- orpheus/npcf_third.py +1684 -0
- orpheus/orpheus_clib.cpython-310-x86_64-linux-gnu.so +0 -0
- orpheus/patchutils.py +369 -0
- orpheus/utils.py +198 -0
- orpheus_npcf-0.2.1.dist-info/METADATA +67 -0
- orpheus_npcf-0.2.1.dist-info/RECORD +19 -0
- orpheus_npcf-0.2.1.dist-info/WHEEL +5 -0
- orpheus_npcf-0.2.1.dist-info/licenses/LICENSE +674 -0
- orpheus_npcf-0.2.1.dist-info/sboms/auditwheel.cdx.json +1 -0
- orpheus_npcf-0.2.1.dist-info/top_level.txt +1 -0
- orpheus_npcf.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
orpheus/npcf_second.py
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
from .catalog import Catalog, ScalarTracerCatalog, SpinTracerCatalog
|
|
6
|
+
from .npcf_base import BinnedNPCF
|
|
7
|
+
|
|
8
|
+
__all__ = ["NNCorrelation", "GGCorrelation"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
###############################
|
|
12
|
+
## SECOND - ORDER STATISTICS ##
|
|
13
|
+
###############################
|
|
14
|
+
|
|
15
|
+
class NNCorrelation(BinnedNPCF):
|
|
16
|
+
r"""Compute pair counts and (optionally) the projected angular clustering two-point correlation function.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
min_sep: float
|
|
21
|
+
The smallest distance of each vertex for which the NPCF is computed.
|
|
22
|
+
max_sep: float
|
|
23
|
+
The largest distance of each vertex for which the NPCF is computed.
|
|
24
|
+
shuffle_pix: int, optional
|
|
25
|
+
Choice of how to define centers of the cells in the spatial hash structure.
|
|
26
|
+
Defaults to ``1``, i.e. random positioning.
|
|
27
|
+
**kwargs
|
|
28
|
+
Passed to :class:`~orpheus.npcf_base.BinnedNPCF`.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Attributes
|
|
32
|
+
----------
|
|
33
|
+
npair: numpy.ndarray
|
|
34
|
+
The number of unweighted pairs.
|
|
35
|
+
npair_cell: numpy.ndarray
|
|
36
|
+
The number cell-pairs.
|
|
37
|
+
xi: numpy.ndarray
|
|
38
|
+
The scalar two-point correlation function.
|
|
39
|
+
|
|
40
|
+
Notes
|
|
41
|
+
-----
|
|
42
|
+
- Inherits all other parameters and attributes from :class:`BinnedNPCF`.
|
|
43
|
+
- Additional child-specific parameters can be passed via ``kwargs``.
|
|
44
|
+
|
|
45
|
+
- Binning:
|
|
46
|
+
- Either ``nbinsr`` or ``binsize`` must be provided to fix the binning scheme.
|
|
47
|
+
- If both are provided, the parent class rules determine which takes precedence.
|
|
48
|
+
|
|
49
|
+
- Pixel hashing / grid setup:
|
|
50
|
+
- ``shuffle_pix=1`` is the default (random cell centers).
|
|
51
|
+
- This differs from shear-based correlation functions where another default may be used.
|
|
52
|
+
|
|
53
|
+
- Estimator:
|
|
54
|
+
The scalar correlation function ``xi`` is formed from the pair counts via the Landy-Szalay estimator
|
|
55
|
+
|
|
56
|
+
.. math::
|
|
57
|
+
|
|
58
|
+
\xi(r) = \frac{DD(r) - 2\,DR(r) + RR(r)}{RR(r)}.
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, min_sep, max_sep, shuffle_pix=1, **kwargs):
|
|
63
|
+
super().__init__(order=2, spins=np.array([0,0], dtype=np.int32), n_cfs=1, min_sep=min_sep, max_sep=max_sep, shuffle_pix=shuffle_pix, **kwargs)
|
|
64
|
+
self.projection = None
|
|
65
|
+
self.projections_avail = [None]
|
|
66
|
+
self.nbinsz = None
|
|
67
|
+
self.nzcombis = None
|
|
68
|
+
self.npair = None
|
|
69
|
+
self.npair_cell = None
|
|
70
|
+
self.xi = None
|
|
71
|
+
|
|
72
|
+
# (Add here any newly implemented projections)
|
|
73
|
+
self._initprojections(self)
|
|
74
|
+
|
|
75
|
+
def saveinst(self, path_save, fname):
|
|
76
|
+
|
|
77
|
+
if not Path(path_save).is_dir():
|
|
78
|
+
raise ValueError('Path to directory does not exist.')
|
|
79
|
+
|
|
80
|
+
np.savez(path_save+fname,
|
|
81
|
+
nbinsz=self.nbinsz,
|
|
82
|
+
min_sep=self.min_sep,
|
|
83
|
+
max_sep=self.max_sep,
|
|
84
|
+
binsr=self.nbinsr,
|
|
85
|
+
method=self.method,
|
|
86
|
+
shuffle_pix=self.shuffle_pix,
|
|
87
|
+
tree_resos=self.tree_resos,
|
|
88
|
+
rmin_pixsize=self.rmin_pixsize,
|
|
89
|
+
resoshift_leafs=self.resoshift_leafs,
|
|
90
|
+
minresoind_leaf=self.minresoind_leaf,
|
|
91
|
+
maxresoind_leaf=self.maxresoind_leaf,
|
|
92
|
+
nthreads=self.nthreads,
|
|
93
|
+
bin_centers=self.bin_centers,
|
|
94
|
+
bin_centers_mean=self.bin_centers_mean,
|
|
95
|
+
xi=self.xi,
|
|
96
|
+
npair=self.npair,
|
|
97
|
+
npair_cell=self.npair_cell)
|
|
98
|
+
|
|
99
|
+
def __process_patches(self, cat, dotomo=True, do_dc=True, adjust_tree=False,
|
|
100
|
+
save_patchres=False, save_filebase="", keep_patchres=False):
|
|
101
|
+
|
|
102
|
+
if save_patchres:
|
|
103
|
+
if not Path(save_patchres).is_dir():
|
|
104
|
+
raise ValueError('Path to directory does not exist.')
|
|
105
|
+
|
|
106
|
+
for elp in range(cat.npatches):
|
|
107
|
+
if self._verbose_python:
|
|
108
|
+
print('Doing patch %i/%i'%(elp+1,cat.npatches))
|
|
109
|
+
|
|
110
|
+
# Compute statistics on patch
|
|
111
|
+
pcat = cat.frompatchind(elp)
|
|
112
|
+
pcorr = NNCorrelation(
|
|
113
|
+
min_sep=self.min_sep,
|
|
114
|
+
max_sep=self.max_sep,
|
|
115
|
+
nbinsr=self.nbinsr,
|
|
116
|
+
method=self.method,
|
|
117
|
+
shuffle_pix=self.shuffle_pix,
|
|
118
|
+
tree_resos=self.tree_resos,
|
|
119
|
+
rmin_pixsize=self.rmin_pixsize,
|
|
120
|
+
resoshift_leafs=self.resoshift_leafs,
|
|
121
|
+
minresoind_leaf=self.minresoind_leaf,
|
|
122
|
+
maxresoind_leaf=self.maxresoind_leaf,
|
|
123
|
+
nthreads=self.nthreads,
|
|
124
|
+
verbosity=self.verbosity)
|
|
125
|
+
pcorr.process(pcat, dotomo=dotomo, do_dc=do_dc)
|
|
126
|
+
|
|
127
|
+
# Update the total measurement
|
|
128
|
+
if elp == 0:
|
|
129
|
+
self.nbinsz = pcorr.nbinsz
|
|
130
|
+
self.nzcombis = pcorr.nzcombis
|
|
131
|
+
self.bin_centers = np.zeros_like(pcorr.bin_centers)
|
|
132
|
+
self.npair = np.zeros_like(pcorr.npair)
|
|
133
|
+
self.npair_cell = np.zeros_like(pcorr.npair_cell)
|
|
134
|
+
if keep_patchres:
|
|
135
|
+
centers_patches = np.zeros((cat.npatches, *pcorr.bin_centers.shape), dtype=pcorr.bin_centers.dtype)
|
|
136
|
+
npair_patches = np.zeros((cat.npatches, *pcorr.npair.shape), dtype=pcorr.npair.dtype)
|
|
137
|
+
npair_cell_patches = np.zeros((cat.npatches, *pcorr.npair_cell.shape), dtype=pcorr.npair_cell.dtype)
|
|
138
|
+
self.bin_centers += pcorr.npair*pcorr.bin_centers
|
|
139
|
+
self.npair += pcorr.npair
|
|
140
|
+
self.npair_cell += pcorr.npair_cell
|
|
141
|
+
if keep_patchres:
|
|
142
|
+
centers_patches[elp] += pcorr.bin_centers
|
|
143
|
+
npair_patches[elp] += pcorr.npair
|
|
144
|
+
npair_cell_patches[elp] += pcorr.npair_cell
|
|
145
|
+
if save_patchres:
|
|
146
|
+
pcorr.saveinst(save_patchres, save_filebase+'_patch%i'%elp)
|
|
147
|
+
|
|
148
|
+
# Finalize the measurement on the full footprint
|
|
149
|
+
self.bin_centers /= self.npair
|
|
150
|
+
self.bin_centers_mean = np.mean(self.bin_centers, axis=0)
|
|
151
|
+
|
|
152
|
+
if keep_patchres:
|
|
153
|
+
return centers_patches, npair_patches, npair_cell_patches
|
|
154
|
+
|
|
155
|
+
def process(self, cat, cat_random=None, dotomo=True, do_dc=True, adjust_tree=False,
|
|
156
|
+
save_patchres=False, save_filebase="", keep_patchres=False):
|
|
157
|
+
r"""
|
|
158
|
+
Compute NN pair counts for a catalog, and optionally the clustering 2PCF ``xi``.
|
|
159
|
+
|
|
160
|
+
If ``cat_random`` is provided, ``xi`` is computed using the Landy–Szalay estimator.
|
|
161
|
+
Otherwise only pair counts are computed.
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
cat: orpheus.ScalarTracerCatalog
|
|
166
|
+
The (clustered) catalog for which the pair counts are computed
|
|
167
|
+
cat_random: orpheus.ScalarTracerCatalog, optional
|
|
168
|
+
A random catalog. If this is set, the clustering correlation function ``xi`` is computed.
|
|
169
|
+
dotomo: bool
|
|
170
|
+
Flag that decides whether the tomographic information in the catalog should be used. Defaults to `True`.
|
|
171
|
+
do_dc: bool
|
|
172
|
+
Flag that decides whether to double-count the pair counts. This will be required when looking at data-random pairs.
|
|
173
|
+
within a tomographic catalog. Defaults to `True`. In case ``xi`` is computed, this argument is internally set to `True`.
|
|
174
|
+
adjust_tree: bool
|
|
175
|
+
Overrides the original setup of the tree-approximations in the instance based on the nbar of the catalog.
|
|
176
|
+
Not implemented yet, therefore no effect. Has no effect yet. Defaults to `False`.
|
|
177
|
+
save_patchres: bool or str
|
|
178
|
+
If the catalog has been decomposed in patches, flag whether to save the NN measurements on the individual patches.
|
|
179
|
+
Note that the path needs to exist, otherwise a `ValueError` is raised. For a flat-sky catalog this parameter
|
|
180
|
+
has no effect. Defaults to `False`.
|
|
181
|
+
save_filebase: str
|
|
182
|
+
Base of the filenames in which the patches are saved. The full filename will be `<save_patchres>/<save_filebase>_patchxx.npz`.
|
|
183
|
+
Only has an effect if the catalog consists of multiple patches and `save_patchres` is not `False`.
|
|
184
|
+
keep_patchres: bool
|
|
185
|
+
If the catalog consists of multiple patches, returns all measurements on the patches. Defaults to `False`.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
# If random catalog present, use the __compute_xi method
|
|
189
|
+
if cat_random is not None:
|
|
190
|
+
assert(isinstance(cat_random, ScalarTracerCatalog))
|
|
191
|
+
self.__compute_xi(cat, cat_random, dotomo=dotomo, adjust_tree=adjust_tree,
|
|
192
|
+
save_patchres=save_patchres, keep_patchres=keep_patchres, estimator="LS")
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
# Make sure that in case the catalog is spherical, it has been decomposed into patches
|
|
196
|
+
if cat.geometry == 'spherical' and cat.patchinds is None:
|
|
197
|
+
raise ValueError('Error: Spherical catalog needs to be first decomposed into patches using the Catalog._topatches method.')
|
|
198
|
+
|
|
199
|
+
# Catalog consist of multiple patches
|
|
200
|
+
if cat.patchinds is not None:
|
|
201
|
+
return self.__process_patches(cat, dotomo=dotomo, do_dc=do_dc, adjust_tree=adjust_tree,
|
|
202
|
+
save_patchres=save_patchres, save_filebase=save_filebase, keep_patchres=keep_patchres)
|
|
203
|
+
# Catalog does not consist of patches
|
|
204
|
+
else:
|
|
205
|
+
# Prechecks
|
|
206
|
+
self._checkcats(cat, self.spins)
|
|
207
|
+
if not dotomo:
|
|
208
|
+
self.nbinsz = 1
|
|
209
|
+
old_zbins = cat.zbins[:]
|
|
210
|
+
cat.zbins = np.zeros(cat.ngal, dtype=np.int32)
|
|
211
|
+
self.nzcombis = 1
|
|
212
|
+
else:
|
|
213
|
+
self.nbinsz = cat.nbinsz
|
|
214
|
+
zbins = cat.zbins
|
|
215
|
+
self.nzcombis = self.nbinsz*self.nbinsz
|
|
216
|
+
|
|
217
|
+
z2r = self.nbinsz*self.nbinsz*self.nbinsr
|
|
218
|
+
sz2r = (self.nbinsz*self.nbinsz, self.nbinsr)
|
|
219
|
+
bin_centers = np.zeros(z2r).astype(np.float64)
|
|
220
|
+
npair = np.zeros(z2r).astype(np.float64)
|
|
221
|
+
npair_cell = np.zeros(z2r).astype(np.int64)
|
|
222
|
+
|
|
223
|
+
cutfirst = np.int32(self.tree_resos[0]==0.)
|
|
224
|
+
mhash = cat.multihash(dpixs=self.tree_resos[cutfirst:], dpix_hash=self.tree_resos[-1],
|
|
225
|
+
shuffle=self.shuffle_pix, normed=False)
|
|
226
|
+
ngal_resos, pos1s, pos2s, weights, zbins, isinners, allfields, index_matchers, pixs_galind_bounds, pix_gals, dpixs1_true, dpixs2_true = mhash
|
|
227
|
+
weight_resos = np.concatenate(weights).astype(np.float64)
|
|
228
|
+
pos1_resos = np.concatenate(pos1s).astype(np.float64)
|
|
229
|
+
pos2_resos = np.concatenate(pos2s).astype(np.float64)
|
|
230
|
+
zbin_resos = np.concatenate(zbins).astype(np.int32)
|
|
231
|
+
isinner_resos = np.concatenate(isinners).astype(np.float64)
|
|
232
|
+
index_matcher = np.concatenate(index_matchers).astype(np.int32)
|
|
233
|
+
pixs_galind_bounds = np.concatenate(pixs_galind_bounds).astype(np.int32)
|
|
234
|
+
pix_gals = np.concatenate(pix_gals).astype(np.int32)
|
|
235
|
+
index_matcher_flat = np.argwhere(cat.index_matcher>-1).flatten()
|
|
236
|
+
nregions = len(index_matcher_flat)
|
|
237
|
+
|
|
238
|
+
args_treeresos = (np.int32(self.tree_nresos), np.int32(self.tree_nresos-cutfirst),
|
|
239
|
+
dpixs1_true.astype(np.float64), dpixs2_true.astype(np.float64), self.tree_redges,
|
|
240
|
+
np.int32(self.resoshift_leafs), np.int32(self.minresoind_leaf),
|
|
241
|
+
np.int32(self.maxresoind_leaf), np.array(ngal_resos, dtype=np.int32), )
|
|
242
|
+
args_resos = (isinner_resos, weight_resos, pos1_resos, pos2_resos, zbin_resos,
|
|
243
|
+
index_matcher, pixs_galind_bounds, pix_gals, )
|
|
244
|
+
args_hash = (np.float64(cat.pix1_start), np.float64(cat.pix1_d), np.int32(cat.pix1_n),
|
|
245
|
+
np.float64(cat.pix2_start), np.float64(cat.pix2_d), np.int32(cat.pix2_n),
|
|
246
|
+
np.int32(nregions), index_matcher_flat.astype(np.int32),)
|
|
247
|
+
args_binning = (np.float64(self.min_sep), np.float64(self.max_sep), np.int32(self.nbinsr), np.int32(do_dc), )
|
|
248
|
+
args_output = (bin_centers, npair, npair_cell, )
|
|
249
|
+
func = self.clib.alloc_nn_doubletree
|
|
250
|
+
args = (*args_treeresos,
|
|
251
|
+
np.int32(self.nbinsz),
|
|
252
|
+
*args_resos,
|
|
253
|
+
*args_hash,
|
|
254
|
+
*args_binning,
|
|
255
|
+
np.int32(self.nthreads),
|
|
256
|
+
np.int32(self._verbose_c)+np.int32(self._verbose_debug),
|
|
257
|
+
*args_output)
|
|
258
|
+
|
|
259
|
+
func(*args)
|
|
260
|
+
|
|
261
|
+
self.bin_centers = bin_centers.reshape(sz2r)
|
|
262
|
+
self.bin_centers_mean = np.mean(self.bin_centers, axis=0)
|
|
263
|
+
self.npair = npair.reshape(sz2r)
|
|
264
|
+
self.npair_cell = npair_cell.reshape(sz2r)
|
|
265
|
+
self.projection = None
|
|
266
|
+
|
|
267
|
+
if not dotomo:
|
|
268
|
+
cat.zbins = old_zbins
|
|
269
|
+
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
def __compute_xi(self, cat_data, cat_rand, dotomo=True, adjust_tree=False,
|
|
273
|
+
save_patchres=False, keep_patchres=False, estimator="LS"):
|
|
274
|
+
|
|
275
|
+
# Define joint tomographic bins across data and random catalog
|
|
276
|
+
zbins = np.zeros(cat_data.ngal + cat_rand.ngal, dtype=int)
|
|
277
|
+
zbins[:cat_data.ngal] += cat_data.zbins
|
|
278
|
+
zbins[cat_data.ngal:] += cat_data.nbinsz + cat_rand.zbins
|
|
279
|
+
if not dotomo:
|
|
280
|
+
zbins[:cat_data.ngal] = 0
|
|
281
|
+
zbins[cat_data.ngal:] = 1
|
|
282
|
+
|
|
283
|
+
# Define joint catalog by appending randoms to data. This means it will have nz_joint=2*nz_data ordered as
|
|
284
|
+
# Z_1=Z_1_data, ..., Z_nz=Z_nz_data, Z_nz+1=Z_1_rand, ..., Z_2nz=Z_nz_rand
|
|
285
|
+
joint_cat = ScalarTracerCatalog(
|
|
286
|
+
pos1=np.append(cat_data.pos1, cat_rand.pos1),
|
|
287
|
+
pos2=np.append(cat_data.pos2, cat_rand.pos2),
|
|
288
|
+
tracer=np.ones(cat_data.ngal + cat_rand.ngal),
|
|
289
|
+
geometry=cat_data.geometry,
|
|
290
|
+
units_pos1= cat_data.units_pos1,
|
|
291
|
+
units_pos2= cat_data.units_pos1,
|
|
292
|
+
zbins=zbins)
|
|
293
|
+
|
|
294
|
+
# In case of a spherical geometry, decompose the joint catalog in patches of the same target geometry as
|
|
295
|
+
# the geometry that was specified in the data catalog
|
|
296
|
+
if cat_data.geometry=="spherical":
|
|
297
|
+
joint_cat.topatches(npatches=cat_data.npatches,
|
|
298
|
+
patchextend_deg=cat_data.patchinds['info']['patchextend_deg'],
|
|
299
|
+
nside_hash=cat_data.patchinds['info']['nside_hash'],
|
|
300
|
+
method=cat_data.patchinds['info']['method'],
|
|
301
|
+
kmeanshp_maxiter=cat_data.patchinds['info']['kmeanshp_maxiter'],
|
|
302
|
+
kmeanshp_tol=cat_data.patchinds['info']['kmeanshp_tol'],
|
|
303
|
+
kmeanshp_randomstate=cat_data.patchinds['info']['kmeanshp_randomstate'],
|
|
304
|
+
healpix_nside=cat_data.patchinds['info']['healpix_nside'])
|
|
305
|
+
|
|
306
|
+
# Compute NN counts of joint catalog
|
|
307
|
+
self.process(cat=joint_cat, dotomo=True, do_dc=True, adjust_tree=adjust_tree,
|
|
308
|
+
save_patchres=save_patchres, keep_patchres=keep_patchres)
|
|
309
|
+
|
|
310
|
+
# Now infer all the tomographic dd,dr,rd,rr pairs pairs from the joint correlator
|
|
311
|
+
# From the z-binning of the joint catalog given above the 2pcf will have the block structure
|
|
312
|
+
# DD DR
|
|
313
|
+
# RD RR
|
|
314
|
+
# where each block is of shape (nz, nz) and the ordering of the indices is the same across all blocks.
|
|
315
|
+
_zshift = cat_data.nbinsz
|
|
316
|
+
_creshape = self.npair.reshape((2*_zshift, 2*_zshift, self.nbinsr))
|
|
317
|
+
dds = _creshape[:_zshift,:_zshift].reshape((_zshift*_zshift, self.nbinsr))
|
|
318
|
+
rrs = _creshape[_zshift:,_zshift:].reshape((_zshift*_zshift, self.nbinsr))
|
|
319
|
+
drs = _creshape[:_zshift,_zshift:].reshape((_zshift*_zshift, self.nbinsr))
|
|
320
|
+
rds = _creshape[_zshift:,:_zshift].reshape((_zshift*_zshift, self.nbinsr))
|
|
321
|
+
|
|
322
|
+
# Get number of galaxies per tomo bin
|
|
323
|
+
_, ngal_zdata = np.unique(cat_data.zbins, return_counts=True)
|
|
324
|
+
_, ngal_zrand = np.unique(cat_rand.zbins, return_counts=True)
|
|
325
|
+
ngal_zdata = ngal_zdata.astype(float)
|
|
326
|
+
ngal_zrand = ngal_zrand.astype(float)
|
|
327
|
+
# Get prefactors of LS estimator
|
|
328
|
+
ngal_zrand_second = np.outer(ngal_zrand,(ngal_zrand-1))
|
|
329
|
+
pref_DD = np.outer(ngal_zrand,(ngal_zrand-1))/np.outer(ngal_zdata,(ngal_zdata-1))
|
|
330
|
+
pref_DR, pref_RD = np.meshgrid(ngal_zrand/ngal_zdata,ngal_zrand/ngal_zdata)
|
|
331
|
+
pref_DD = pref_DD.flatten()[:, np.newaxis]
|
|
332
|
+
pref_DR = pref_DR.flatten()[:, np.newaxis]
|
|
333
|
+
pref_RD = pref_RD.flatten()[:, np.newaxis]
|
|
334
|
+
|
|
335
|
+
# Combine all pair counts to get 2pcf estimator
|
|
336
|
+
if estimator=="LS":
|
|
337
|
+
self.xi = pref_DD*dds/rrs - pref_DR*drs/rrs - pref_RD*rds/rrs + 1
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def computeNap2(self, radii, tofile=False):
|
|
341
|
+
""" Computes second-order aperture statistics given the projected angular clustering correlation function.
|
|
342
|
+
Uses the Crittenden 2002 filter.
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
nap2 = np.zeros((self.xi.shape[0], len(radii)), dtype=float)
|
|
346
|
+
for elr, R in enumerate(radii):
|
|
347
|
+
thetared = self.bin_centers_mean[np.newaxis,:]/R
|
|
348
|
+
measure = (self.bin_edges[1:]-self.bin_edges[:-1])*self.bin_centers_mean/(R**2)
|
|
349
|
+
filt = (thetared**4-16*thetared**2+32)/(128) * np.exp(-thetared**2/4.)
|
|
350
|
+
nap2[:,elr] = np.sum(measure*filt*self.xi,axis=1)
|
|
351
|
+
|
|
352
|
+
return nap2
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class GGCorrelation(BinnedNPCF):
|
|
356
|
+
r""" Compute second-order correlation functions of spin-2 fields.
|
|
357
|
+
|
|
358
|
+
Parameters
|
|
359
|
+
----------
|
|
360
|
+
min_sep: float
|
|
361
|
+
The smallest distance of each vertex for which the NPCF is computed.
|
|
362
|
+
max_sep: float
|
|
363
|
+
The largest distance of each vertex for which the NPCF is computed.
|
|
364
|
+
|
|
365
|
+
Attributes
|
|
366
|
+
----------
|
|
367
|
+
xip: numpy.ndarray
|
|
368
|
+
The ξ₊ correlation function.
|
|
369
|
+
xim: numpy.ndarray
|
|
370
|
+
The ξ₋ correlation function.
|
|
371
|
+
norm: numpy.ndarray
|
|
372
|
+
The number of weighted pairs.
|
|
373
|
+
npair: numpy.ndarray
|
|
374
|
+
The number of unweighted pairs.
|
|
375
|
+
|
|
376
|
+
Notes
|
|
377
|
+
-----
|
|
378
|
+
Inherits all other parameters and attributes from :class:`BinnedNPCF`.
|
|
379
|
+
Additional child-specific parameters can be passed via ``kwargs``.
|
|
380
|
+
Either ``nbinsr`` or ``binsize`` has to be provided to fix the binning scheme .
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
def __init__(self, min_sep, max_sep, **kwargs):
|
|
384
|
+
super().__init__(order=2, spins=np.array([2,2], dtype=np.int32), n_cfs=2, min_sep=min_sep, max_sep=max_sep, **kwargs)
|
|
385
|
+
self.projection = None
|
|
386
|
+
self.projections_avail = [None]
|
|
387
|
+
self.nbinsz = None
|
|
388
|
+
self.nzcombis = None
|
|
389
|
+
self.counts = None
|
|
390
|
+
self.xip = None
|
|
391
|
+
self.xim = None
|
|
392
|
+
self.norm = None
|
|
393
|
+
self.npair = None
|
|
394
|
+
|
|
395
|
+
# (Add here any newly implemented projections)
|
|
396
|
+
self._initprojections(self)
|
|
397
|
+
|
|
398
|
+
def saveinst(self, path_save, fname):
|
|
399
|
+
|
|
400
|
+
if not Path(path_save).is_dir():
|
|
401
|
+
raise ValueError('Path to directory does not exist.')
|
|
402
|
+
|
|
403
|
+
np.savez(path_save+fname,
|
|
404
|
+
nbinsz=self.nbinsz,
|
|
405
|
+
min_sep=self.min_sep,
|
|
406
|
+
max_sep=self.max_sep,
|
|
407
|
+
binsr=self.nbinsr,
|
|
408
|
+
method=self.method,
|
|
409
|
+
shuffle_pix=self.shuffle_pix,
|
|
410
|
+
tree_resos=self.tree_resos,
|
|
411
|
+
rmin_pixsize=self.rmin_pixsize,
|
|
412
|
+
resoshift_leafs=self.resoshift_leafs,
|
|
413
|
+
minresoind_leaf=self.minresoind_leaf,
|
|
414
|
+
maxresoind_leaf=self.maxresoind_leaf,
|
|
415
|
+
nthreads=self.nthreads,
|
|
416
|
+
bin_centers=self.bin_centers,
|
|
417
|
+
xip=self.xip,
|
|
418
|
+
xim=self.xim,
|
|
419
|
+
npair=self.npair,
|
|
420
|
+
norm=self.norm)
|
|
421
|
+
|
|
422
|
+
def __process_patches(self, cat, dotomo=True, do_dc=False, rotsignflip=False, apply_edge_correction=False, adjust_tree=False,
|
|
423
|
+
save_patchres=False, save_filebase="", keep_patchres=False):
|
|
424
|
+
|
|
425
|
+
if save_patchres:
|
|
426
|
+
if not Path(save_patchres).is_dir():
|
|
427
|
+
raise ValueError('Path to directory does not exist.')
|
|
428
|
+
|
|
429
|
+
for elp in range(cat.npatches):
|
|
430
|
+
if self._verbose_python:
|
|
431
|
+
print('Doing patch %i/%i'%(elp+1,cat.npatches))
|
|
432
|
+
|
|
433
|
+
# Compute statistics on patch
|
|
434
|
+
pcat = cat.frompatchind(elp,rotsignflip=rotsignflip)
|
|
435
|
+
pcorr = GGCorrelation(
|
|
436
|
+
min_sep=self.min_sep,
|
|
437
|
+
max_sep=self.max_sep,
|
|
438
|
+
nbinsr=self.nbinsr,
|
|
439
|
+
method=self.method,
|
|
440
|
+
shuffle_pix=self.shuffle_pix,
|
|
441
|
+
tree_resos=self.tree_resos,
|
|
442
|
+
rmin_pixsize=self.rmin_pixsize,
|
|
443
|
+
resoshift_leafs=self.resoshift_leafs,
|
|
444
|
+
minresoind_leaf=self.minresoind_leaf,
|
|
445
|
+
maxresoind_leaf=self.maxresoind_leaf,
|
|
446
|
+
nthreads=self.nthreads,
|
|
447
|
+
verbosity=self.verbosity)
|
|
448
|
+
pcorr.process(pcat, dotomo=dotomo, do_dc=do_dc)
|
|
449
|
+
|
|
450
|
+
# Update the total measurement
|
|
451
|
+
if elp == 0:
|
|
452
|
+
self.nbinsz = pcorr.nbinsz
|
|
453
|
+
self.nzcombis = pcorr.nzcombis
|
|
454
|
+
self.bin_centers = np.zeros_like(pcorr.bin_centers)
|
|
455
|
+
self.xip = np.zeros_like(pcorr.xip)
|
|
456
|
+
self.xim = np.zeros_like(pcorr.xim)
|
|
457
|
+
self.norm = np.zeros_like(pcorr.norm)
|
|
458
|
+
self.npair = np.zeros_like(pcorr.norm)
|
|
459
|
+
if keep_patchres:
|
|
460
|
+
centers_patches = np.zeros((cat.npatches, *pcorr.bin_centers.shape), dtype=pcorr.bin_centers.dtype)
|
|
461
|
+
xip_patches = np.zeros((cat.npatches, *pcorr.xip.shape), dtype=pcorr.xip.dtype)
|
|
462
|
+
xim_patches = np.zeros((cat.npatches, *pcorr.xim.shape), dtype=pcorr.xim.dtype)
|
|
463
|
+
norm_patches = np.zeros((cat.npatches, *pcorr.norm.shape), dtype=pcorr.norm.dtype)
|
|
464
|
+
npair_patches = np.zeros((cat.npatches, *pcorr.npair.shape), dtype=pcorr.npair.dtype)
|
|
465
|
+
self.bin_centers += pcorr.norm*pcorr.bin_centers
|
|
466
|
+
self.xip += pcorr.norm*pcorr.xip
|
|
467
|
+
self.xim += pcorr.norm*pcorr.xim
|
|
468
|
+
self.norm += pcorr.norm
|
|
469
|
+
self.npair += pcorr.npair
|
|
470
|
+
if keep_patchres:
|
|
471
|
+
centers_patches[elp] += pcorr.bin_centers
|
|
472
|
+
xip_patches[elp] += pcorr.xip
|
|
473
|
+
xim_patches[elp] += pcorr.xim
|
|
474
|
+
norm_patches[elp] += pcorr.norm
|
|
475
|
+
npair_patches[elp] += pcorr.npair
|
|
476
|
+
if save_patchres:
|
|
477
|
+
pcorr.saveinst(save_patchres, save_filebase+'_patch%i'%elp)
|
|
478
|
+
|
|
479
|
+
# Finalize the measurement on the full footprint
|
|
480
|
+
self.bin_centers /= self.norm
|
|
481
|
+
self.bin_centers_mean = np.mean(self.bin_centers, axis=0)
|
|
482
|
+
self.xip /= self.norm
|
|
483
|
+
self.xim /= self.norm
|
|
484
|
+
self.projection = "xipm"
|
|
485
|
+
|
|
486
|
+
if keep_patchres:
|
|
487
|
+
return centers_patches, xip_patches, xim_patches, norm_patches, npair_patches
|
|
488
|
+
|
|
489
|
+
def process(self, cat, dotomo=True, do_dc=False, rotsignflip=False, adjust_tree=False,
|
|
490
|
+
save_patchres=False, save_filebase="", keep_patchres=False):
|
|
491
|
+
r"""
|
|
492
|
+
Compute a shear 2PCF given a shape catalog
|
|
493
|
+
|
|
494
|
+
Parameters
|
|
495
|
+
----------
|
|
496
|
+
cat: orpheus.SpinTracerCatalog
|
|
497
|
+
The shape catalog to process.
|
|
498
|
+
dotomo: bool
|
|
499
|
+
Flag that decides whether the tomographic information in the shape catalog should be used. Defaults to `True`.
|
|
500
|
+
do_dc: bool
|
|
501
|
+
Whether to double-count pair counts. This will have no impact on :math:`\xi_\pm`, but can
|
|
502
|
+
significantly reduce the amplitude of :math:`\xi_\times`. Defaults to `False`.
|
|
503
|
+
rotsignflip: bool
|
|
504
|
+
If the shape catalog is has been decomposed in patches, choose whether the rotation angle should be flipped.
|
|
505
|
+
For simulated data this was always ok to set to 'False`. Defaults to `False`.
|
|
506
|
+
adjust_tree: bool
|
|
507
|
+
Overrides the original setup of the tree-approximations in the instance based on the nbar of the shape catalog.
|
|
508
|
+
Not implemented yet, therefore no effect. Has no effect yet. Defaults to `False`
|
|
509
|
+
save_patchres: bool or str
|
|
510
|
+
If the shape catalog is has been decomposed in patches, flag whether to save the GG measurements on the individual patches.
|
|
511
|
+
Note that the path needs to exist, otherwise a `ValueError` is raised. For a flat-sky catalog this parameter
|
|
512
|
+
has no effect. Defaults to `False`
|
|
513
|
+
save_filebase: str
|
|
514
|
+
Base of the filenames in which the patches are saved. The full filename will be `<save_patchres>/<save_filebase>_patchxx.npz`.
|
|
515
|
+
Only has an effect if the shape catalog consists of multiple patches and `save_patchres` is not `False`.
|
|
516
|
+
keep_patchres: bool
|
|
517
|
+
If the catalog consists of multiple patches, returns all measurements on the patches. Defaults to `False`.
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
# Make sure that in case the catalog is spherical, it has been decomposed into patches
|
|
521
|
+
if cat.geometry == 'spherical' and cat.patchinds is None:
|
|
522
|
+
raise ValueError('Error: Spherical catalog needs to be first decomposed into patches using the Catalog._topatches method.')
|
|
523
|
+
|
|
524
|
+
# Catalog consist of multiple patches
|
|
525
|
+
if cat.patchinds is not None:
|
|
526
|
+
return self.__process_patches(cat, dotomo=dotomo, do_dc=do_dc, rotsignflip=rotsignflip, adjust_tree=adjust_tree,
|
|
527
|
+
save_patchres=save_patchres, save_filebase=save_filebase, keep_patchres=keep_patchres)
|
|
528
|
+
# Catalog does not consist of patches
|
|
529
|
+
else:
|
|
530
|
+
# Prechecks
|
|
531
|
+
self._checkcats(cat, self.spins)
|
|
532
|
+
if not dotomo:
|
|
533
|
+
self.nbinsz = 1
|
|
534
|
+
old_zbins = cat.zbins[:]
|
|
535
|
+
cat.zbins = np.zeros(cat.ngal, dtype=np.int32)
|
|
536
|
+
self.nzcombis = 1
|
|
537
|
+
else:
|
|
538
|
+
self.nbinsz = cat.nbinsz
|
|
539
|
+
zbins = cat.zbins
|
|
540
|
+
self.nzcombis = self.nbinsz*self.nbinsz
|
|
541
|
+
|
|
542
|
+
z2r = self.nbinsz*self.nbinsz*self.nbinsr
|
|
543
|
+
sz2r = (self.nbinsz*self.nbinsz, self.nbinsr)
|
|
544
|
+
bin_centers = np.zeros(z2r).astype(np.float64)
|
|
545
|
+
xip = np.zeros(z2r).astype(np.complex128)
|
|
546
|
+
xim = np.zeros(z2r).astype(np.complex128)
|
|
547
|
+
norm = np.zeros(z2r).astype(np.float64)
|
|
548
|
+
npair = np.zeros(z2r).astype(np.int64)
|
|
549
|
+
|
|
550
|
+
cutfirst = np.int32(self.tree_resos[0]==0.)
|
|
551
|
+
mhash = cat.multihash(dpixs=self.tree_resos[cutfirst:], dpix_hash=self.tree_resos[-1],
|
|
552
|
+
shuffle=self.shuffle_pix, w2field=True, normed=True)
|
|
553
|
+
ngal_resos, pos1s, pos2s, weights, zbins, isinners, allfields, index_matchers, pixs_galind_bounds, pix_gals, dpixs1_true, dpixs2_true = mhash
|
|
554
|
+
weight_resos = np.concatenate(weights).astype(np.float64)
|
|
555
|
+
pos1_resos = np.concatenate(pos1s).astype(np.float64)
|
|
556
|
+
pos2_resos = np.concatenate(pos2s).astype(np.float64)
|
|
557
|
+
zbin_resos = np.concatenate(zbins).astype(np.int32)
|
|
558
|
+
isinner_resos = np.concatenate(isinners).astype(np.float64)
|
|
559
|
+
e1_resos = np.concatenate([allfields[i][0] for i in range(len(allfields))]).astype(np.float64)
|
|
560
|
+
e2_resos = np.concatenate([allfields[i][1] for i in range(len(allfields))]).astype(np.float64)
|
|
561
|
+
index_matcher = np.concatenate(index_matchers).astype(np.int32)
|
|
562
|
+
pixs_galind_bounds = np.concatenate(pixs_galind_bounds).astype(np.int32)
|
|
563
|
+
pix_gals = np.concatenate(pix_gals).astype(np.int32)
|
|
564
|
+
index_matcher_flat = np.argwhere(cat.index_matcher>-1).flatten()
|
|
565
|
+
nregions = len(index_matcher_flat)
|
|
566
|
+
|
|
567
|
+
args_treeresos = (np.int32(self.tree_nresos), np.int32(self.tree_nresos-cutfirst),
|
|
568
|
+
dpixs1_true.astype(np.float64), dpixs2_true.astype(np.float64), self.tree_redges,
|
|
569
|
+
np.int32(self.resoshift_leafs), np.int32(self.minresoind_leaf),
|
|
570
|
+
np.int32(self.maxresoind_leaf), np.array(ngal_resos, dtype=np.int32), )
|
|
571
|
+
args_resos = (isinner_resos, weight_resos, pos1_resos, pos2_resos, e1_resos, e2_resos, zbin_resos,
|
|
572
|
+
index_matcher, pixs_galind_bounds, pix_gals, )
|
|
573
|
+
args_hash = (np.float64(cat.pix1_start), np.float64(cat.pix1_d), np.int32(cat.pix1_n),
|
|
574
|
+
np.float64(cat.pix2_start), np.float64(cat.pix2_d), np.int32(cat.pix2_n),
|
|
575
|
+
np.int32(nregions), index_matcher_flat.astype(np.int32),)
|
|
576
|
+
args_binning = (np.float64(self.min_sep), np.float64(self.max_sep), np.int32(self.nbinsr), np.int32(do_dc))
|
|
577
|
+
args_output = (bin_centers, xip, xim, norm, npair, )
|
|
578
|
+
func = self.clib.alloc_xipm_doubletree
|
|
579
|
+
args = (*args_treeresos,
|
|
580
|
+
np.int32(self.nbinsz),
|
|
581
|
+
*args_resos,
|
|
582
|
+
*args_hash,
|
|
583
|
+
*args_binning,
|
|
584
|
+
np.int32(self.nthreads),
|
|
585
|
+
np.int32(self._verbose_c)+np.int32(self._verbose_debug),
|
|
586
|
+
*args_output)
|
|
587
|
+
|
|
588
|
+
func(*args)
|
|
589
|
+
|
|
590
|
+
self.bin_centers = bin_centers.reshape(sz2r)
|
|
591
|
+
self.bin_centers_mean = np.mean(self.bin_centers, axis=0)
|
|
592
|
+
self.npair = npair.reshape(sz2r)
|
|
593
|
+
self.norm = norm.reshape(sz2r)
|
|
594
|
+
self.xip = xip.reshape(sz2r)
|
|
595
|
+
self.xim = xim.reshape(sz2r)
|
|
596
|
+
self.projection = "xipm"
|
|
597
|
+
|
|
598
|
+
if not dotomo:
|
|
599
|
+
cat.zbins = old_zbins
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def computeMap2(self, radii, tofile=False):
|
|
603
|
+
""" Computes second-order aperture mass statistics given the shear correlation functions.
|
|
604
|
+
Uses the Crittenden 2002 filter.
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
Tp = lambda x: 1./128. * (x**4-16*x**2+32) * np.exp(-x**2/4.)
|
|
608
|
+
Tm = lambda x: 1./128. * (x**4) * np.exp(-x**2/4.)
|
|
609
|
+
result = np.zeros((4, self.nzcombis, len(radii)), dtype=float)
|
|
610
|
+
for elr, R in enumerate(radii):
|
|
611
|
+
thetared = self.bin_centers/R
|
|
612
|
+
pref = self.binsize*thetared**2/2.
|
|
613
|
+
t1 = np.sum(pref*(Tp(thetared)*self.xip + Tm(thetared)*self.xim), axis=1)
|
|
614
|
+
t2 = np.sum(pref*(Tp(thetared)*self.xip - Tm(thetared)*self.xim), axis=1)
|
|
615
|
+
result[0,:,elr] = t1.real # Map2
|
|
616
|
+
result[1,:,elr] = t1.imag # MapMx
|
|
617
|
+
result[2,:,elr] = t2.real # Mx2
|
|
618
|
+
result[3,:,elr] = t2.imag # MxMap (Difference from MapMx gives ~level of estimator uncertainty)
|
|
619
|
+
|
|
620
|
+
return result
|