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/direct.py
ADDED
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
import ctypes as ct
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
import glob
|
|
4
|
+
from math import factorial
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numpy.ctypeslib import ndpointer
|
|
7
|
+
import operator
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import sys
|
|
10
|
+
from .flat2dgrid import FlatPixelGrid_2D, FlatDataGrid_2D
|
|
11
|
+
from .catalog import Catalog, ScalarTracerCatalog, SpinTracerCatalog
|
|
12
|
+
|
|
13
|
+
__all__ = ["DirectEstimator", "Direct_MapnEqual", "Direct_NapnEqual", "MapCombinatorics"]
|
|
14
|
+
|
|
15
|
+
class DirectEstimator:
|
|
16
|
+
r"""
|
|
17
|
+
Class of aperture statistics up to nth order for various arbitrary tracer catalogs.
|
|
18
|
+
This class contains attributes and methods that can be used across any of its children.
|
|
19
|
+
|
|
20
|
+
Attributes
|
|
21
|
+
----------
|
|
22
|
+
Rmin : float
|
|
23
|
+
The smallest aperture radius for which the cumulants are computed.
|
|
24
|
+
|
|
25
|
+
Rmax : float
|
|
26
|
+
The largest aperture radius for which the cumulants are computed.
|
|
27
|
+
|
|
28
|
+
nbinsr : int, optional
|
|
29
|
+
The number of radial bins for the aperture radii. If set to
|
|
30
|
+
``None`` this attribute is inferred from the ``binsize`` attribute.
|
|
31
|
+
|
|
32
|
+
binsize : int, optional
|
|
33
|
+
The logarithmic size of the radial bins for the aperture radii. If set to
|
|
34
|
+
``None`` this attribute is inferred from the ``nbinsr`` attribute.
|
|
35
|
+
|
|
36
|
+
aperture_centers : str, optional
|
|
37
|
+
How to sample the apertures. Can be ``'grid'`` or ``'density'``.
|
|
38
|
+
|
|
39
|
+
accuracies : int or numpy.ndarray, optional
|
|
40
|
+
The sampling density of aperture centers.
|
|
41
|
+
|
|
42
|
+
* If ``aperture_centers`` is set to ``'grid'``, setting ``accuracy == x``
|
|
43
|
+
places the apertures on a regular grid with pixel size ``R_ap / x``.
|
|
44
|
+
* If ``aperture_centers`` is set to ``'density'``, randomly selects as many
|
|
45
|
+
galaxies as there would be aperture centers on the regular grid.
|
|
46
|
+
|
|
47
|
+
frac_covs : numpy.ndarray, optional
|
|
48
|
+
The different aperture coverage bins for which the statistics are evaluated. The first bin
|
|
49
|
+
only includes apertures with ``coverage <= frac_covs[0]`` while the other coverage bins include the
|
|
50
|
+
intervals between ``frac_covs[i]`` and ``frac_covs[i+1]``. Coverage is defined as the percentage of
|
|
51
|
+
the aperture area that is not within the survey area.
|
|
52
|
+
|
|
53
|
+
dpix_hash : float, optional
|
|
54
|
+
The pixel size of the spatial hash used to search through the catalog.
|
|
55
|
+
|
|
56
|
+
weight_outer : float, optional
|
|
57
|
+
The fractional weight applied to galaxies not contained within the interior of the catalog.
|
|
58
|
+
This only affects catalogs which are overlapping patches of a full-sky catalog.
|
|
59
|
+
|
|
60
|
+
weight_inpainted : float, optional
|
|
61
|
+
The fractional weight applied to virtual galaxies inpainted into the catalog. This only
|
|
62
|
+
affects catalogs which have objects in them that are labeled as inpainted.
|
|
63
|
+
|
|
64
|
+
method : str, optional
|
|
65
|
+
The method to be employed for the estimator. Defaults to ``Discrete``.
|
|
66
|
+
|
|
67
|
+
multicountcorr : bool, optional
|
|
68
|
+
Flag on whether to subtract multiplets in which the same tracer appears more
|
|
69
|
+
than once. Defaults to ``True``.
|
|
70
|
+
|
|
71
|
+
shuffle_pix : int, optional
|
|
72
|
+
Choice of how to define centers of the cells in the spatial hash structure.
|
|
73
|
+
Defaults to ``1``, i.e. random positioning.
|
|
74
|
+
|
|
75
|
+
tree_resos : list, optional
|
|
76
|
+
The cell sizes of the hierarchical spatial hash structure.
|
|
77
|
+
|
|
78
|
+
tree_redges : list, optional
|
|
79
|
+
Deprecated (possibly).
|
|
80
|
+
|
|
81
|
+
rmin_pixsize : int, optional
|
|
82
|
+
The limiting radial distance relative to the cell of the spatial hash
|
|
83
|
+
after which one switches to the next hash in the hierarchy. At the moment
|
|
84
|
+
does have no effect.Defaults to ``20``.
|
|
85
|
+
|
|
86
|
+
resoshift_leafs : int, optional
|
|
87
|
+
Allows for a difference in how the hierarchical spatial hash is traversed for
|
|
88
|
+
pixels at the base of the NPCF and pixels at leafs. Positive values indicate
|
|
89
|
+
that leafs will be evaluated at coarser resolutions than the base. At the moment
|
|
90
|
+
does have no effect. Defaults to ``0``.
|
|
91
|
+
|
|
92
|
+
minresoind_leaf : int, optional
|
|
93
|
+
Sets the smallest resolution in the spatial hash hierarchy which can be used to access
|
|
94
|
+
tracers at leaf positions. If set to ``None`` uses the smallest specified cell size.
|
|
95
|
+
At the moment does have no effect. Defaults to ``None``.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
maxresoind_leaf : int, optional
|
|
99
|
+
Sets the largest resolution in the spatial hash hierarchy which can be used to access
|
|
100
|
+
tracers at leaf positions. If set to ``None`` uses the largest specified cell size.
|
|
101
|
+
At the moment does have no effect. Defaults to ``None``.
|
|
102
|
+
|
|
103
|
+
nthreads : int, optional
|
|
104
|
+
The number of OpenMP threads used for the reduction procedure. Defaults to ``16``.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, Rmin, Rmax, nbinsr=None, binsize=None,
|
|
108
|
+
aperture_centers="grid", accuracies=2.,
|
|
109
|
+
frac_covs=[0.,0.1,0.3,0.5,1.], dpix_hash=1.,
|
|
110
|
+
weight_outer=1., weight_inpainted=0.,
|
|
111
|
+
method="Discrete", multicountcorr=True, shuffle_pix=1,
|
|
112
|
+
tree_resos=[0,0.25,0.5,1.,2.], tree_redges=None, rmin_pixsize=20,
|
|
113
|
+
resoshift_leafs=0, minresoind_leaf=None, maxresoind_leaf=None,nthreads=16):
|
|
114
|
+
|
|
115
|
+
self.Rmin = Rmin
|
|
116
|
+
self.Rmax = Rmax
|
|
117
|
+
self.method = method
|
|
118
|
+
self.multicountcorr = int(multicountcorr)
|
|
119
|
+
self.shuffle_pix = shuffle_pix
|
|
120
|
+
self.aperture_centers = aperture_centers
|
|
121
|
+
self.accuracies = accuracies
|
|
122
|
+
self.frac_covs = np.asarray(frac_covs, dtype=np.float64)
|
|
123
|
+
self.nfrac_covs = len(self.frac_covs)
|
|
124
|
+
self.dpix_hash = dpix_hash
|
|
125
|
+
self.weight_outer = weight_outer
|
|
126
|
+
self.weight_inpainted = weight_inpainted
|
|
127
|
+
self.methods_avail = ["Discrete", "Tree", "BaseTree", "DoubleTree", "FFT"]
|
|
128
|
+
self.tree_resos = np.asarray(tree_resos, dtype=np.float64)
|
|
129
|
+
self.tree_nresos = int(len(self.tree_resos))
|
|
130
|
+
self.tree_redges = tree_redges
|
|
131
|
+
self.rmin_pixsize = rmin_pixsize
|
|
132
|
+
self.resoshift_leafs = resoshift_leafs
|
|
133
|
+
self.minresoind_leaf = minresoind_leaf
|
|
134
|
+
self.maxresoind_leaf = maxresoind_leaf
|
|
135
|
+
self.nthreads = np.int32(max(1,nthreads))
|
|
136
|
+
|
|
137
|
+
self.combinatorics = None # Here we will store a dict to match tomobin indices for arbitrary orders
|
|
138
|
+
|
|
139
|
+
# Check types or arguments
|
|
140
|
+
assert(isinstance(self.Rmin, float))
|
|
141
|
+
assert(isinstance(self.Rmax, float))
|
|
142
|
+
assert(self.aperture_centers in ["grid","density"])
|
|
143
|
+
assert((self.weight_outer>=0.) and (self.weight_outer<=1.))
|
|
144
|
+
assert((self.weight_inpainted>=0.) and (self.weight_inpainted<=1.))
|
|
145
|
+
assert(self.method in self.methods_avail)
|
|
146
|
+
assert(isinstance(self.tree_resos, np.ndarray))
|
|
147
|
+
assert(isinstance(self.tree_resos[0], np.float64))
|
|
148
|
+
|
|
149
|
+
# Setup radial bins
|
|
150
|
+
# Note that we always have self.binsize <= binsize
|
|
151
|
+
assert((binsize!=None) or (nbinsr!=None))
|
|
152
|
+
if nbinsr != None:
|
|
153
|
+
self.nbinsr = int(nbinsr)
|
|
154
|
+
if binsize != None:
|
|
155
|
+
assert(isinstance(binsize, float))
|
|
156
|
+
self.nbinsr = int(np.ceil(np.log(self.Rmax/self.Rmin)/binsize))
|
|
157
|
+
assert(isinstance(self.nbinsr, int))
|
|
158
|
+
self.radii = np.geomspace(self.Rmin, self.Rmax, self.nbinsr)
|
|
159
|
+
# Setup variable for tree estimator
|
|
160
|
+
if self.tree_redges != None:
|
|
161
|
+
assert(isinstance(self.tree_redges, np.ndarray))
|
|
162
|
+
self.tree_redges = self.tree_redges.astype(np.float64)
|
|
163
|
+
assert(len(self.tree_redges)==self.tree_resos+1)
|
|
164
|
+
self.tree_redges = np.sort(self.tree_redges)
|
|
165
|
+
assert(self.tree_redges[0]==self.Rmin)
|
|
166
|
+
assert(self.tree_redges[-1]==self.Rmax)
|
|
167
|
+
else:
|
|
168
|
+
self.tree_redges = np.zeros(len(self.tree_resos)+1)
|
|
169
|
+
self.tree_redges[-1] = self.Rmin
|
|
170
|
+
for elreso, reso in enumerate(self.tree_resos):
|
|
171
|
+
self.tree_redges[elreso] = (reso==0.)*self.Rmax + (reso!=0.)*self.rmin_pixsize*reso
|
|
172
|
+
# Setup accuracies
|
|
173
|
+
if (isinstance(self.accuracies, int) or isinstance(self.accuracies, float)):
|
|
174
|
+
self.accuracies = self.accuracies*np.ones(self.nbinsr,dtype=np.float64)
|
|
175
|
+
self.accuracies = np.asarray(self.accuracies,dtype=np.float64)
|
|
176
|
+
assert(isinstance(self.accuracies,np.ndarray))
|
|
177
|
+
# Prepare leaf resolutions
|
|
178
|
+
if np.abs(self.resoshift_leafs)>=self.tree_nresos:
|
|
179
|
+
self.resoshift_leafs = np.int32((self.tree_nresos-1) * np.sign(self.resoshift_leafs))
|
|
180
|
+
print("Error: Parameter resoshift_leafs is out of bounds. Set to %i."%self.resoshift_leafs)
|
|
181
|
+
if self.minresoind_leaf is None:
|
|
182
|
+
self.minresoind_leaf=0
|
|
183
|
+
if self.maxresoind_leaf is None:
|
|
184
|
+
self.maxresoind_leaf=self.tree_nresos-1
|
|
185
|
+
if self.minresoind_leaf<0:
|
|
186
|
+
self.minresoind_leaf = 0
|
|
187
|
+
print("Error: Parameter minreso_leaf is out of bounds. Set to 0.")
|
|
188
|
+
if self.minresoind_leaf>=self.tree_nresos:
|
|
189
|
+
self.minresoind_leaf = self.tree_nresos-1
|
|
190
|
+
print("Error: Parameter minreso_leaf is out of bounds. Set to %i."%self.minresoint_leaf)
|
|
191
|
+
if self.maxresoind_leaf<0:
|
|
192
|
+
self.maxresoind_leaf = 0
|
|
193
|
+
print("Error: Parameter minreso_leaf is out of bounds. Set to 0.")
|
|
194
|
+
if self.maxresoind_leaf>=self.tree_nresos:
|
|
195
|
+
self.maxresoind_leaf = self.tree_nresos-1
|
|
196
|
+
print("Error: Parameter minreso_leaf is out of bounds. Set to %i."%self.maxresoint_leaf)
|
|
197
|
+
if self.maxresoind_leaf<self.minresoind_leaf:
|
|
198
|
+
print("Error: Parameter maxreso_leaf is smaller than minreso_leaf. Set to %i."%self.minreso_leaf)
|
|
199
|
+
|
|
200
|
+
#############################
|
|
201
|
+
## Link compiled libraries ##
|
|
202
|
+
#############################
|
|
203
|
+
# Method that works for LP
|
|
204
|
+
target_path = __import__('orpheus').__file__
|
|
205
|
+
self.library_path = str(Path(__import__('orpheus').__file__).parent.absolute())
|
|
206
|
+
self.clib = ct.CDLL(glob.glob(self.library_path+"/orpheus_clib*.so")[0])
|
|
207
|
+
|
|
208
|
+
# In case the environment is weird, compile code manually and load it here...
|
|
209
|
+
#self.clib = ct.CDLL("/vol/euclidraid4/data/lporth/HigherOrderLensing/Estimator/orpheus/orpheus/src/discrete.so")
|
|
210
|
+
|
|
211
|
+
# Method that works for RR (but not for LP with a local HPC install)
|
|
212
|
+
#self.clib = ct.CDLL(search_file_in_site_package(get_site_packages_dir(),"orpheus_clib"))
|
|
213
|
+
#self.library_path = str(Path(__import__('orpheus').__file__).parent.parent.absolute())
|
|
214
|
+
#print(self.library_path)
|
|
215
|
+
#print(self.clib)
|
|
216
|
+
p_c128 = ndpointer(complex, flags="C_CONTIGUOUS")
|
|
217
|
+
p_f64 = ndpointer(np.float64, flags="C_CONTIGUOUS")
|
|
218
|
+
p_f32 = ndpointer(np.float32, flags="C_CONTIGUOUS")
|
|
219
|
+
p_i32 = ndpointer(np.int32, flags="C_CONTIGUOUS")
|
|
220
|
+
p_f64_nof = ndpointer(np.float64)
|
|
221
|
+
|
|
222
|
+
def get_pixelization(self, cat, R_ap, accuracy, R_crop=None, mgrid=True):
|
|
223
|
+
""" Computes pixel grid on inner region of survey field.
|
|
224
|
+
|
|
225
|
+
Arguments:
|
|
226
|
+
----------
|
|
227
|
+
R_ap (float):
|
|
228
|
+
The radius of the aperture in pixel scale.
|
|
229
|
+
accuracy (float):
|
|
230
|
+
Accuracy parameter for the pixel grid.
|
|
231
|
+
A value of 0.5 results in a grid in which the apertures
|
|
232
|
+
are only touching each other - hence minimizing correlations.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
--------
|
|
236
|
+
grid_x (array of floats):
|
|
237
|
+
The grid cell centers for the x-coordinate.
|
|
238
|
+
grid_y (array of floats):
|
|
239
|
+
The grid cell centers for the y-coordinate.
|
|
240
|
+
|
|
241
|
+
Notes:
|
|
242
|
+
------
|
|
243
|
+
The grid covers the rectangel between the extremal x/y coordinates of
|
|
244
|
+
the galaxy catalogue.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
if float(accuracy)==-1.:
|
|
248
|
+
centers_1 = cat.pos1[cat.isinner>=0.5]
|
|
249
|
+
centers_2 = cat.pos2[cat.isinner>=0.5]
|
|
250
|
+
|
|
251
|
+
else:
|
|
252
|
+
start1 = cat.min1
|
|
253
|
+
start2 = cat.min2
|
|
254
|
+
end1 = cat.max1
|
|
255
|
+
end2 = cat.max2
|
|
256
|
+
if R_crop is not None:
|
|
257
|
+
start1 += R_crop
|
|
258
|
+
start2 += R_crop
|
|
259
|
+
end1 -= R_crop
|
|
260
|
+
end2 -= R_crop
|
|
261
|
+
|
|
262
|
+
len_1 = end1 - start1
|
|
263
|
+
len_2 = end2 - start2
|
|
264
|
+
|
|
265
|
+
npixels_1 = int(np.ceil(accuracy * len_1 / R_ap))
|
|
266
|
+
npixels_2 = int(np.ceil(accuracy * len_2 / R_ap))
|
|
267
|
+
|
|
268
|
+
stepsize_1 = len_1 / npixels_1
|
|
269
|
+
stepsize_2 = len_2 / npixels_2
|
|
270
|
+
|
|
271
|
+
_centers_1 = [start1 + npixel *
|
|
272
|
+
stepsize_1 for npixel in range(npixels_1 + 1)]
|
|
273
|
+
_centers_2 = [start2 + npixel *
|
|
274
|
+
stepsize_2 for npixel in range(npixels_2 + 1)]
|
|
275
|
+
|
|
276
|
+
if mgrid:
|
|
277
|
+
centers_1, centers_2 = np.meshgrid(_centers_1,_centers_2)
|
|
278
|
+
centers_1 = centers_1.flatten()
|
|
279
|
+
centers_2 = centers_2.flatten()
|
|
280
|
+
else:
|
|
281
|
+
centers_1 = np.asarray(_centers_1, dtype=np.float64)
|
|
282
|
+
centers_2 = np.asarray(_centers_2, dtype=np.float64)
|
|
283
|
+
|
|
284
|
+
return centers_1, centers_2
|
|
285
|
+
|
|
286
|
+
def __getmap(self, R, cat, dotomo, field, filter_form):
|
|
287
|
+
""" This simply computes an aperture mass map together with weights and coverages """
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class Direct_MapnEqual(DirectEstimator):
|
|
292
|
+
r"""
|
|
293
|
+
Compute direct estimator for equal-scale aperture mass statistics.
|
|
294
|
+
|
|
295
|
+
Attributes
|
|
296
|
+
----------
|
|
297
|
+
order_max : int
|
|
298
|
+
Maximum order of the statistics to be computed.
|
|
299
|
+
|
|
300
|
+
Rmin : float
|
|
301
|
+
Minimum aperture radius.
|
|
302
|
+
|
|
303
|
+
Rmax : float
|
|
304
|
+
Maximum aperture radius.
|
|
305
|
+
|
|
306
|
+
field : str, optional
|
|
307
|
+
Type of input field (``"scalar"`` or ``"polar"``).
|
|
308
|
+
|
|
309
|
+
filter_form : str, optional
|
|
310
|
+
Filter type used in the aperture function (``"S98"``, ``"C02"``, ``"Sch04"``, etc.).
|
|
311
|
+
|
|
312
|
+
ap_weights : str, optional
|
|
313
|
+
Aperture weighting strategy (``"Identity"``, ``"InvShot"``).
|
|
314
|
+
|
|
315
|
+
**kwargs : dict
|
|
316
|
+
Additional keyword arguments passed to :class:`DirectEstimator`.
|
|
317
|
+
|
|
318
|
+
Notes
|
|
319
|
+
-----
|
|
320
|
+
Inherits all other parameters and attributes from :class:`DirectEstimator`.
|
|
321
|
+
Additional child-specific parameters can be passed via ``kwargs``.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
def __init__(self, order_max, Rmin, Rmax, field="polar", filter_form="C02", ap_weights="InvShot", **kwargs):
|
|
325
|
+
|
|
326
|
+
super().__init__(Rmin=Rmin, Rmax=Rmax, **kwargs)
|
|
327
|
+
self.order_max = order_max
|
|
328
|
+
self.nbinsz = None
|
|
329
|
+
self.field = field
|
|
330
|
+
self.filter_form = filter_form
|
|
331
|
+
self.ap_weights = ap_weights
|
|
332
|
+
|
|
333
|
+
self.fields_avail = ["scalar", "polar"]
|
|
334
|
+
self.ap_weights_dict = {"Identity":0, "InvShot":1}
|
|
335
|
+
self.filters_dict = {"S98":0, "C02":1, "Sch04":2, "PolyExp":3}
|
|
336
|
+
self.ap_weights_avail = list(self.ap_weights_dict.keys())
|
|
337
|
+
self.filters_avail = list(self.filters_dict.keys())
|
|
338
|
+
assert(self.field in self.fields_avail)
|
|
339
|
+
assert(self.ap_weights in self.ap_weights_avail)
|
|
340
|
+
assert(self.filter_form in self.filters_avail)
|
|
341
|
+
|
|
342
|
+
# We do not need DoubleTree for equal-aperture estimator
|
|
343
|
+
if self.method=="DoubleTree":
|
|
344
|
+
self.method="Tree"
|
|
345
|
+
|
|
346
|
+
p_c128 = ndpointer(complex, flags="C_CONTIGUOUS")
|
|
347
|
+
p_f64 = ndpointer(np.float64, flags="C_CONTIGUOUS")
|
|
348
|
+
p_f32 = ndpointer(np.float32, flags="C_CONTIGUOUS")
|
|
349
|
+
p_i32 = ndpointer(np.int32, flags="C_CONTIGUOUS")
|
|
350
|
+
p_f64_nof = ndpointer(np.float64)
|
|
351
|
+
|
|
352
|
+
# Compute nth order equal-scale statistics using discrete estimator (E-Mode only!)
|
|
353
|
+
self.clib.MapnSingleEonlyDisc.restype = ct.c_void_p
|
|
354
|
+
self.clib.MapnSingleEonlyDisc.argtypes = [
|
|
355
|
+
ct.c_double, p_f64, p_f64, ct.c_int32,
|
|
356
|
+
ct.c_int32, ct.c_int32, ct.c_int32, ct.c_int32, ct.c_double, ct.c_double,
|
|
357
|
+
p_f64, p_f64, p_f64, p_f64, p_c128, p_i32, ct.c_int32, ct.c_int32,
|
|
358
|
+
p_f64, p_f64, ct.c_int32, ct.c_int32,
|
|
359
|
+
ct.c_double, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32,
|
|
360
|
+
p_i32, p_i32, p_i32,
|
|
361
|
+
ct.c_int32, p_f64, p_f64]
|
|
362
|
+
self.clib.singleAp_MapnSingleEonlyDisc.restype = ct.c_void_p
|
|
363
|
+
self.clib.singleAp_MapnSingleEonlyDisc.argtypes = [
|
|
364
|
+
ct.c_double, ct.c_double, ct.c_double,
|
|
365
|
+
ct.c_int32, ct.c_int32, ct.c_double, ct.c_double,
|
|
366
|
+
p_f64, p_f64, p_f64, p_f64, p_c128, p_i32, ct.c_int32, ct.c_int32,
|
|
367
|
+
p_f64,
|
|
368
|
+
ct.c_double, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32,
|
|
369
|
+
p_i32, p_i32, p_i32,
|
|
370
|
+
p_f64, p_f64, p_f64, p_f64, p_f64, p_f64]
|
|
371
|
+
|
|
372
|
+
# Compute aperture mass map for equal-scale stats
|
|
373
|
+
self.clib.ApertureMassMap_Equal.restype = ct.c_void_p
|
|
374
|
+
self.clib.ApertureMassMap_Equal.argtypes = [
|
|
375
|
+
ct.c_double, p_f64, p_f64, ct.c_int32, ct.c_int32,
|
|
376
|
+
ct.c_int32, ct.c_int32, ct.c_int32, ct.c_double, ct.c_double,
|
|
377
|
+
p_f64, p_f64, p_f64, p_f64, p_c128, p_i32, ct.c_int32, ct.c_int32,
|
|
378
|
+
p_f64,
|
|
379
|
+
ct.c_double, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32,
|
|
380
|
+
p_i32, p_i32, p_i32,
|
|
381
|
+
ct.c_int32, p_f64, p_f64, p_f64, p_f64, p_f64, p_f64]
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def process(self, cat, dotomo=True, Emodeonly=True, connected=True, dpix_innergrid=2.):
|
|
385
|
+
r"""
|
|
386
|
+
Computes aperture statistics on a catalog.
|
|
387
|
+
|
|
388
|
+
Parameters
|
|
389
|
+
----------
|
|
390
|
+
cat : orpheus.SpinTracerCatalog
|
|
391
|
+
The catalog instance to be processed.
|
|
392
|
+
dotomo : bool, optional
|
|
393
|
+
Whether to compute the statistics for all tomographic bin combinations.
|
|
394
|
+
Default is True.
|
|
395
|
+
Emodeonly : bool, optional
|
|
396
|
+
Currently does not have an impact.
|
|
397
|
+
Default is False.
|
|
398
|
+
connected : bool, optional
|
|
399
|
+
Whether to output only the connected part of the aperture mass statistics.
|
|
400
|
+
Does not have an impact at the moment.
|
|
401
|
+
Default is True.
|
|
402
|
+
dpix_innergrid : float, optional
|
|
403
|
+
Pixel size for a rough reconstruction of the angular mask. Used to preselect
|
|
404
|
+
aperture centers in the interior of the survey.
|
|
405
|
+
Default is 2.
|
|
406
|
+
|
|
407
|
+
Returns
|
|
408
|
+
-------
|
|
409
|
+
None
|
|
410
|
+
Currently does not return any value.
|
|
411
|
+
"""
|
|
412
|
+
|
|
413
|
+
nbinsz = cat.nbinsz
|
|
414
|
+
if not dotomo:
|
|
415
|
+
nbinsz = 1
|
|
416
|
+
self.nbinsz = nbinsz
|
|
417
|
+
|
|
418
|
+
nzcombis = self._nzcombis_tot(nbinsz,dotomo)
|
|
419
|
+
result_Mapn = np.zeros((self.nbinsr, self.nfrac_covs, nzcombis), dtype=np.float64)
|
|
420
|
+
result_wMapn = np.zeros((self.nbinsr, self.nfrac_covs, nzcombis), dtype=np.float64)
|
|
421
|
+
if (self.method in ["Discrete", "BaseTree"]) and Emodeonly:
|
|
422
|
+
func = self.clib.MapnSingleEonlyDisc
|
|
423
|
+
elif (self.method in ["Discrete", "BaseTree"]) and not Emodeonly:
|
|
424
|
+
raise NotImplementedError
|
|
425
|
+
else:
|
|
426
|
+
raise NotImplementedError
|
|
427
|
+
|
|
428
|
+
# Build a grid that only covers inner part of patch
|
|
429
|
+
# This will be used to preselelct aperture centers
|
|
430
|
+
args_innergrid = cat.togrid(fields=[cat.isinner], dpix=dpix_innergrid, method="NGP", normed=True, tomo=False)
|
|
431
|
+
|
|
432
|
+
for elr, R in enumerate(self.radii):
|
|
433
|
+
nextmap_out = np.zeros(nzcombis*self.nfrac_covs ,dtype=np.float64)
|
|
434
|
+
nextwmap_out = np.zeros(nzcombis*self.nfrac_covs ,dtype=np.float64)
|
|
435
|
+
args = self._buildargs(cat, args_innergrid, dotomo, elr, forfunc="Equal")
|
|
436
|
+
func(*args)
|
|
437
|
+
result_Mapn[elr] = args[-2].reshape((self.nfrac_covs, nzcombis))[:]
|
|
438
|
+
result_wMapn[elr] = args[-1].reshape((self.nfrac_covs, nzcombis))[:]
|
|
439
|
+
|
|
440
|
+
sys.stdout.write("\rDone %i/%i aperture radii"%(elr+1,self.nbinsr))
|
|
441
|
+
|
|
442
|
+
return result_Mapn, result_wMapn
|
|
443
|
+
|
|
444
|
+
def _getindex(self, order, mode, zcombi):
|
|
445
|
+
pass
|
|
446
|
+
|
|
447
|
+
def _buildargs(self, cat, args_innergrid, dotomo, indR, forfunc="Equal"):
|
|
448
|
+
|
|
449
|
+
assert(forfunc in ["Equal", "EqualGrid"])
|
|
450
|
+
|
|
451
|
+
if not dotomo:
|
|
452
|
+
nbinsz = 1
|
|
453
|
+
zbins = np.zeros(cat.ngal, dtype=np.int32)
|
|
454
|
+
else:
|
|
455
|
+
nbinsz = cat.nbinsz
|
|
456
|
+
zbins = cat.zbins.astype(np.int32)
|
|
457
|
+
|
|
458
|
+
# Initialize combinatorics instances for lateron
|
|
459
|
+
# TODO: Should find a better place where to put this...
|
|
460
|
+
self.combinatorics = {}
|
|
461
|
+
for order in range(1,self.order_max+1):
|
|
462
|
+
self.combinatorics[order] = MapCombinatorics(nbinsz,order_max=order)
|
|
463
|
+
|
|
464
|
+
# Parameters related to the aperture grid
|
|
465
|
+
if forfunc=="Equal":
|
|
466
|
+
# Get centers and check that they are in the interior of the inner catalog
|
|
467
|
+
centers_1, centers_2 = self.get_pixelization(cat, self.radii[indR], self.accuracies[indR], R_crop=0., mgrid=True)
|
|
468
|
+
_f, _s1, _s2, _dpixi, _, _, = args_innergrid
|
|
469
|
+
#pixs_c = (((centers_1-_s1)//_dpixi)*_f[0,1].shape[0] + (centers_2-_s2)//_dpixi).astype(int)
|
|
470
|
+
pixs_c = (((centers_2-_s2)//_dpixi)*_f[0,1].shape[1] + (centers_1-_s1)//_dpixi).astype(int)
|
|
471
|
+
|
|
472
|
+
sel_inner = _f[0,1]>0.
|
|
473
|
+
sel_centers = sel_inner.flatten()[pixs_c]
|
|
474
|
+
# For regular grid, select aperture centers within the interior of the survey
|
|
475
|
+
if self.aperture_centers=="grid":
|
|
476
|
+
centers_1 = centers_1[sel_centers]
|
|
477
|
+
centers_2 = centers_2[sel_centers]
|
|
478
|
+
ncenters = len(centers_1)
|
|
479
|
+
# For density-based grid, select fraction of galaxy positions as aperture centers
|
|
480
|
+
# Note that this will not bias the result as the galaxy residing at the center is
|
|
481
|
+
# not taken into account in the directestimator.c code.
|
|
482
|
+
elif self.aperture_centers=="density":
|
|
483
|
+
rng = np.random.RandomState(1234567890)
|
|
484
|
+
ncenters = max(len(centers_1),cat.ngal)
|
|
485
|
+
sel_centers = rng.random.choice(np.arange(cat.ngal), ncenters, replace=False).astype(np.int32)
|
|
486
|
+
centers_1 = cat.pos1[sel_centers]
|
|
487
|
+
centers_2 = cat.pos2[sel_centers]
|
|
488
|
+
elif forfunc=="EqualGrid":
|
|
489
|
+
# Get centers along each dimension
|
|
490
|
+
centers_1, centers_2 = self.get_pixelization(cat, self.radii[indR], self.accuracies[indR], R_crop=0., mgrid=False)
|
|
491
|
+
ncenters = len(centers_1)*len(centers_2)
|
|
492
|
+
|
|
493
|
+
cat.build_spatialhash(dpix=self.dpix_hash, extent=[None, None, None, None])
|
|
494
|
+
hashgrid = FlatPixelGrid_2D(cat.pix1_start, cat.pix2_start,
|
|
495
|
+
cat.pix1_n, cat.pix2_n, cat.pix1_d, cat.pix2_d)
|
|
496
|
+
regridded_mask = cat.mask.regrid(hashgrid).data.flatten().astype(np.float64)
|
|
497
|
+
|
|
498
|
+
len_out = self._nzcombis_tot(nbinsz,dotomo)*self.nfrac_covs
|
|
499
|
+
|
|
500
|
+
if forfunc=="Equal":
|
|
501
|
+
args_centers = (self.radii[indR], centers_1, centers_2, ncenters,)
|
|
502
|
+
elif forfunc=="EqualGrid":
|
|
503
|
+
args_centers = (self.radii[indR], centers_1, centers_2, len(centers_1), len(centers_2), )
|
|
504
|
+
if forfunc=="Equal":
|
|
505
|
+
args_ofw = (self.order_max, self.filters_dict[self.filter_form], self.ap_weights_dict[self.ap_weights],
|
|
506
|
+
np.int32(self.multicountcorr), np.float64(self.weight_outer), np.float64(self.weight_inpainted), )
|
|
507
|
+
elif forfunc=="EqualGrid":
|
|
508
|
+
args_ofw = (self.order_max, self.filters_dict[self.filter_form], self.ap_weights_dict[self.ap_weights],
|
|
509
|
+
np.float64(self.weight_outer), np.float64(self.weight_inpainted), )
|
|
510
|
+
args_cat = (cat.weight.astype(np.float64), cat.isinner.astype(np.float64),
|
|
511
|
+
cat.pos1.astype(np.float64), cat.pos2.astype(np.float64),
|
|
512
|
+
cat.tracer_1.astype(np.float64)+1j*cat.tracer_2.astype(np.float64),
|
|
513
|
+
zbins, np.int32(nbinsz), np.int32(cat.ngal), )
|
|
514
|
+
if forfunc=="Equal":
|
|
515
|
+
args_mask = (regridded_mask, self.frac_covs, self.nfrac_covs, 1, )
|
|
516
|
+
elif forfunc=="EqualGrid":
|
|
517
|
+
args_mask = (regridded_mask, )
|
|
518
|
+
args_hash = (np.float64(cat.pix1_start), np.float64(cat.pix2_start),
|
|
519
|
+
np.float64(cat.pix1_d), np.float64(cat.pix2_d),
|
|
520
|
+
np.int32(cat.pix1_n), np.int32(cat.pix2_n),
|
|
521
|
+
cat.index_matcher.astype(np.int32), cat.pixs_galind_bounds.astype(np.int32),
|
|
522
|
+
cat.pix_gals.astype(np.int32)
|
|
523
|
+
)
|
|
524
|
+
if forfunc=="Equal":
|
|
525
|
+
args_out = (np.zeros(len_out).astype(np.float64), np.zeros(len_out).astype(np.float64))
|
|
526
|
+
elif forfunc=="EqualGrid":
|
|
527
|
+
args_out = (np.zeros(3*nbinsz*ncenters).astype(np.float64),
|
|
528
|
+
np.zeros(2*ncenters).astype(np.float64),
|
|
529
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64),
|
|
530
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64),
|
|
531
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64),
|
|
532
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64), )
|
|
533
|
+
# Return the parameters for the Map computation
|
|
534
|
+
args = (*args_centers,
|
|
535
|
+
*args_ofw,
|
|
536
|
+
*args_cat,
|
|
537
|
+
*args_mask,
|
|
538
|
+
*args_hash,
|
|
539
|
+
np.int32(self.nthreads),
|
|
540
|
+
*args_out)
|
|
541
|
+
if False:
|
|
542
|
+
for elarg, arg in enumerate(args):
|
|
543
|
+
func = self.clib.MapnSingleEonlyDisc
|
|
544
|
+
toprint = (elarg, type(arg),)
|
|
545
|
+
if isinstance(arg, np.ndarray):
|
|
546
|
+
toprint += (type(arg[0]), arg.shape)
|
|
547
|
+
try:
|
|
548
|
+
toprint += (func.argtypes[elarg], )
|
|
549
|
+
print(toprint)
|
|
550
|
+
print(arg)
|
|
551
|
+
except:
|
|
552
|
+
print("We did have a problem for arg %i"%elarg)
|
|
553
|
+
|
|
554
|
+
return args
|
|
555
|
+
|
|
556
|
+
def _nzcombis_tot(self, nbinsz, dotomo):
|
|
557
|
+
res = 0
|
|
558
|
+
for order in range(1, self.order_max+1):
|
|
559
|
+
res += self._nzcombis_order(order, nbinsz, dotomo)
|
|
560
|
+
return res
|
|
561
|
+
|
|
562
|
+
def _nzcombis_order(self, order, nbinsz, dotomo):
|
|
563
|
+
if not dotomo:
|
|
564
|
+
return 1
|
|
565
|
+
else:
|
|
566
|
+
return int(nbinsz*factorial(nbinsz+order-1)/(factorial(nbinsz)*factorial(order)))
|
|
567
|
+
|
|
568
|
+
def _cumnzcombis_order(self, order, nbinsz, dotomo):
|
|
569
|
+
res = 0
|
|
570
|
+
for order in range(1, order+1):
|
|
571
|
+
res += self._nzcombis_order(order, nbinsz, dotomo)
|
|
572
|
+
return res
|
|
573
|
+
|
|
574
|
+
def genzcombi(self, zs, nbinsz=None):
|
|
575
|
+
""" Returns index of tomographic bin combination of Map^n output.
|
|
576
|
+
|
|
577
|
+
Arguments:
|
|
578
|
+
----------
|
|
579
|
+
zs: list of integers
|
|
580
|
+
Target combination of tomographic redshifts ([z1, ..., zk]).
|
|
581
|
+
nbinsz: int, optional
|
|
582
|
+
The number of tomographic bins in the computation of Map^n. If not set,
|
|
583
|
+
reverts to corresponding class attribute.
|
|
584
|
+
|
|
585
|
+
Returns
|
|
586
|
+
-------
|
|
587
|
+
zind_flat: int
|
|
588
|
+
Index of flattened Map^k(z1,...,zk) datavector in global output.
|
|
589
|
+
"""
|
|
590
|
+
|
|
591
|
+
if self.combinatorics is None:
|
|
592
|
+
self.combinatorics = {}
|
|
593
|
+
for order in range(1,self.order_max+1):
|
|
594
|
+
self.combinatorics[order] = MapCombinatorics(nbinsz,order_max=order)
|
|
595
|
+
|
|
596
|
+
if nbinsz is None:
|
|
597
|
+
nbinsz = self.nbinsz
|
|
598
|
+
if nbinsz is None:
|
|
599
|
+
raise ValueError("No value for `nbinsz` has been allocated yet.")
|
|
600
|
+
if len(zs)>self.order_max:
|
|
601
|
+
raise ValueError("We only computed the statistics up to order %i."%self.order_max)
|
|
602
|
+
if max(zs) >= nbinsz:
|
|
603
|
+
raise ValueError("We only have %i tomographic bins available."%nbinsz)
|
|
604
|
+
|
|
605
|
+
order = len(zs)
|
|
606
|
+
zind_flat = self._cumnzcombis_order(order-1,nbinsz,True) + self.combinatorics[order].sel2ind(zs)
|
|
607
|
+
|
|
608
|
+
return zind_flat
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def getmap(self, indR, cat, dotomo=True):
|
|
612
|
+
""" Computes various maps that are part of the basis of the Map^n estimator.
|
|
613
|
+
|
|
614
|
+
Arguments:
|
|
615
|
+
----------
|
|
616
|
+
indR: int
|
|
617
|
+
Index of aperture radius for which maps are computed
|
|
618
|
+
cat: orpheus.SpinTracerCatalog
|
|
619
|
+
The catalog instance to be processed
|
|
620
|
+
dotomo: bool, optional
|
|
621
|
+
Whether the tomographic information in `cat` should be
|
|
622
|
+
used for the map construction
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
--------
|
|
626
|
+
counts: ndarray
|
|
627
|
+
Aperture number counts
|
|
628
|
+
|
|
629
|
+
"""
|
|
630
|
+
|
|
631
|
+
nbinsz = cat.nbinsz
|
|
632
|
+
if not dotomo:
|
|
633
|
+
nbinsz = 1
|
|
634
|
+
|
|
635
|
+
args = self._buildargs(cat, None, dotomo, indR, forfunc="EqualGrid")
|
|
636
|
+
ncenters_1 = args[3]
|
|
637
|
+
ncenters_2 = args[4]
|
|
638
|
+
self.clib.ApertureMassMap_Equal(*args)
|
|
639
|
+
|
|
640
|
+
counts = args[-6].reshape((nbinsz, 3, ncenters_2, ncenters_1))
|
|
641
|
+
covs = args[-5].reshape((2, ncenters_2, ncenters_1))
|
|
642
|
+
Msn = args[-4].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
643
|
+
Sn = args[-3].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
644
|
+
Mapn = args[-2].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
645
|
+
Mapn_var = args[-1].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
646
|
+
|
|
647
|
+
return counts, covs, Msn, Sn, Mapn, Mapn_var
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
class Direct_NapnEqual(DirectEstimator):
|
|
651
|
+
r"""
|
|
652
|
+
Compute direct estimator for equal-scale aperture counts statistics.
|
|
653
|
+
|
|
654
|
+
Attributes
|
|
655
|
+
----------
|
|
656
|
+
order_max : int
|
|
657
|
+
Maximum order of the statistics to be computed.
|
|
658
|
+
|
|
659
|
+
Rmin : float
|
|
660
|
+
Minimum aperture radius.
|
|
661
|
+
|
|
662
|
+
Rmax : float
|
|
663
|
+
Maximum aperture radius.
|
|
664
|
+
|
|
665
|
+
field : str, optional
|
|
666
|
+
Type of input field (``"scalar"`` or ``"polar"``).
|
|
667
|
+
|
|
668
|
+
filter_form : str, optional
|
|
669
|
+
Filter type used in the aperture function (``"S98"``, ``"C02"``, ``"Sch04"``, etc.).
|
|
670
|
+
|
|
671
|
+
ap_weights : str, optional
|
|
672
|
+
Aperture weighting strategy (``"Identity"``, ``"InvShot"``).
|
|
673
|
+
|
|
674
|
+
**kwargs : dict
|
|
675
|
+
Additional keyword arguments passed to :class:`DirectEstimator`.
|
|
676
|
+
|
|
677
|
+
Notes
|
|
678
|
+
-----
|
|
679
|
+
Inherits all other parameters and attributes from :class:`DirectEstimator`.
|
|
680
|
+
Additional child-specific parameters can be passed via ``kwargs``.
|
|
681
|
+
"""
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def __init__(self, order_max, Rmin, Rmax, field="scalar", filter_form="C02", ap_weights="Identity", **kwargs):
|
|
685
|
+
super().__init__(Rmin=Rmin, Rmax=Rmax, **kwargs)
|
|
686
|
+
self.order_max = order_max
|
|
687
|
+
self.nbinsz = None
|
|
688
|
+
self.field = field
|
|
689
|
+
self.filter_form = filter_form
|
|
690
|
+
self.ap_weights = ap_weights
|
|
691
|
+
|
|
692
|
+
self.fields_avail = ["scalar", "polar"]
|
|
693
|
+
self.ap_weights_dict = {"Identity":0, "InvShot":1}
|
|
694
|
+
self.filters_dict = {"S98":0, "C02":1, "Sch04":2, "PolyExp":3}
|
|
695
|
+
self.ap_weights_avail = list(self.ap_weights_dict.keys())
|
|
696
|
+
self.filters_avail = list(self.filters_dict.keys())
|
|
697
|
+
assert(self.field in self.fields_avail)
|
|
698
|
+
assert(self.ap_weights in self.ap_weights_avail)
|
|
699
|
+
assert(self.filter_form in self.filters_avail)
|
|
700
|
+
|
|
701
|
+
# We do not need DoubleTree for equal-aperture estimator
|
|
702
|
+
if self.method=="DoubleTree":
|
|
703
|
+
self.method="Tree"
|
|
704
|
+
|
|
705
|
+
p_c128 = ndpointer(complex, flags="C_CONTIGUOUS")
|
|
706
|
+
p_f64 = ndpointer(np.float64, flags="C_CONTIGUOUS")
|
|
707
|
+
p_f32 = ndpointer(np.float32, flags="C_CONTIGUOUS")
|
|
708
|
+
p_i32 = ndpointer(np.int32, flags="C_CONTIGUOUS")
|
|
709
|
+
p_f64_nof = ndpointer(np.float64)
|
|
710
|
+
|
|
711
|
+
# Compute aperture counts map for equal-scale stats
|
|
712
|
+
self.clib.ApertureCountsMap_Equal.restype = ct.c_void_p
|
|
713
|
+
self.clib.ApertureCountsMap_Equal.argtypes = [
|
|
714
|
+
ct.c_double, p_f64, p_f64, ct.c_int32, ct.c_int32,
|
|
715
|
+
ct.c_int32, ct.c_int32, ct.c_int32, ct.c_int32, ct.c_double, ct.c_double,
|
|
716
|
+
p_f64, p_f64, p_f64, p_f64, p_f64, p_i32, ct.c_int32, ct.c_int32,
|
|
717
|
+
p_f64,
|
|
718
|
+
ct.c_double, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32,
|
|
719
|
+
p_i32, p_i32, p_i32,
|
|
720
|
+
ct.c_int32, p_f64, p_f64, p_f64, p_f64, p_f64, p_f64]
|
|
721
|
+
|
|
722
|
+
# Compute nth order equal-scale statistics using discrete estimator (E-Mode only!)
|
|
723
|
+
self.clib.NapnSingleDisc.restype = ct.c_void_p
|
|
724
|
+
self.clib.NapnSingleDisc.argtypes = [
|
|
725
|
+
ct.c_double, p_f64, p_f64, ct.c_int32,
|
|
726
|
+
ct.c_int32, ct.c_int32, ct.c_int32, ct.c_int32, ct.c_double, ct.c_double,
|
|
727
|
+
p_f64, p_f64, p_f64, p_f64, p_f64, p_i32, ct.c_int32, ct.c_int32,
|
|
728
|
+
p_f64, p_f64, ct.c_int32, ct.c_int32,
|
|
729
|
+
ct.c_double, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32,
|
|
730
|
+
p_i32, p_i32, p_i32,
|
|
731
|
+
ct.c_int32, p_f64, p_f64]
|
|
732
|
+
|
|
733
|
+
# Compute nth order equal-scale statistics using discrete estimator (E-Mode only!)
|
|
734
|
+
self.clib.singleAp_NapnSingleDisc.restype = ct.c_void_p
|
|
735
|
+
self.clib.singleAp_NapnSingleDisc.argtypes = [
|
|
736
|
+
ct.c_double, ct.c_double, ct.c_double,
|
|
737
|
+
ct.c_int32, ct.c_int32, ct.c_int32, ct.c_double, ct.c_double,
|
|
738
|
+
p_f64, p_f64, p_f64, p_f64, p_f64, p_i32, ct.c_int32, ct.c_int32,
|
|
739
|
+
p_f64,
|
|
740
|
+
ct.c_double, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32,
|
|
741
|
+
p_i32, p_i32, p_i32,
|
|
742
|
+
p_f64, p_f64, p_f64, p_f64]
|
|
743
|
+
|
|
744
|
+
def process(self, cat, dotomo=True, Nbar_policy=1, connected=True, dpix_innergrid=2.):
|
|
745
|
+
r"""
|
|
746
|
+
Computes aperture statistics on a catalog.
|
|
747
|
+
|
|
748
|
+
Parameters
|
|
749
|
+
----------
|
|
750
|
+
cat : orpheus.SpinTracerCatalog
|
|
751
|
+
The catalog instance to be processed.
|
|
752
|
+
dotomo : bool, optional
|
|
753
|
+
Whether to compute the statistics for all tomographic bin combinations.
|
|
754
|
+
Default is True.
|
|
755
|
+
Nbar_policy : int, optional
|
|
756
|
+
What normalization to use:
|
|
757
|
+
|
|
758
|
+
0 : Use local Nbar for normalization
|
|
759
|
+
1 : Use global Nbar for normalization
|
|
760
|
+
2 : No Nbar for normalization
|
|
761
|
+
|
|
762
|
+
Default is 1.
|
|
763
|
+
connected : bool, optional
|
|
764
|
+
Whether to output only the connected part of the aperture mass statistics.
|
|
765
|
+
Does not have an impact at the moment.
|
|
766
|
+
dpix_innergrid : float, optional
|
|
767
|
+
Pixel size for a rough reconstruction of the angular mask. Used to preselect
|
|
768
|
+
aperture centers in the interior of the survey.
|
|
769
|
+
Default is 2.
|
|
770
|
+
|
|
771
|
+
Returns
|
|
772
|
+
-------
|
|
773
|
+
None
|
|
774
|
+
Currently does not return any value.
|
|
775
|
+
"""
|
|
776
|
+
|
|
777
|
+
assert(isinstance(cat, ScalarTracerCatalog))
|
|
778
|
+
|
|
779
|
+
nbinsz = cat.nbinsz
|
|
780
|
+
if not dotomo:
|
|
781
|
+
nbinsz = 1
|
|
782
|
+
self.nbinsz = nbinsz
|
|
783
|
+
|
|
784
|
+
nzcombis = self._nzcombis_tot(nbinsz,dotomo)
|
|
785
|
+
result_Napn = np.zeros((self.nbinsr, self.nfrac_covs, nzcombis), dtype=np.float64)
|
|
786
|
+
result_wNapn = np.zeros((self.nbinsr, self.nfrac_covs, nzcombis), dtype=np.float64)
|
|
787
|
+
if (self.method in ["Discrete", "BaseTree"]):
|
|
788
|
+
func = self.clib.NapnSingleDisc
|
|
789
|
+
elif (self.method in ["Discrete", "BaseTree"]):
|
|
790
|
+
raise NotImplementedError
|
|
791
|
+
else:
|
|
792
|
+
raise NotImplementedError
|
|
793
|
+
|
|
794
|
+
# Build a grid that only covers inner part of patch
|
|
795
|
+
# This will be used to preselelct aperture centers
|
|
796
|
+
args_innergrid = cat.togrid(fields=[cat.isinner], dpix=dpix_innergrid, method="NGP", normed=True, tomo=False)
|
|
797
|
+
|
|
798
|
+
for elr, R in enumerate(self.radii):
|
|
799
|
+
nextnap_out = np.zeros(nzcombis*self.nfrac_covs ,dtype=np.float64)
|
|
800
|
+
nextwnap_out = np.zeros(nzcombis*self.nfrac_covs ,dtype=np.float64)
|
|
801
|
+
args = self._buildargs(cat, args_innergrid, dotomo, Nbar_policy, elr, forfunc="Equal")
|
|
802
|
+
func(*args)
|
|
803
|
+
result_Napn[elr] = args[-2].reshape((self.nfrac_covs, nzcombis))[:]
|
|
804
|
+
result_wNapn[elr] = args[-1].reshape((self.nfrac_covs, nzcombis))[:]
|
|
805
|
+
|
|
806
|
+
sys.stdout.write("\rDone %i/%i aperture radii"%(elr+1,self.nbinsr))
|
|
807
|
+
|
|
808
|
+
return result_Napn, result_wNapn
|
|
809
|
+
|
|
810
|
+
def _getindex(self, order, mode, zcombi):
|
|
811
|
+
pass
|
|
812
|
+
|
|
813
|
+
def _buildargs(self, cat, args_innergrid, dotomo, Nbar_policy, indR, forfunc="Equal"):
|
|
814
|
+
|
|
815
|
+
assert(forfunc in ["Equal", "EqualGrid"])
|
|
816
|
+
|
|
817
|
+
if not dotomo:
|
|
818
|
+
nbinsz = 1
|
|
819
|
+
zbins = np.zeros(cat.ngal, dtype=np.int32)
|
|
820
|
+
else:
|
|
821
|
+
nbinsz = cat.nbinsz
|
|
822
|
+
zbins = cat.zbins.astype(np.int32)
|
|
823
|
+
|
|
824
|
+
# Initialize combinatorics instances for lateron
|
|
825
|
+
# TODO: Should find a better place where to put this...
|
|
826
|
+
self.combinatorics = {}
|
|
827
|
+
for order in range(1,self.order_max+1):
|
|
828
|
+
self.combinatorics[order] = MapCombinatorics(nbinsz,order_max=order)
|
|
829
|
+
|
|
830
|
+
# Parameters related to the aperture grid
|
|
831
|
+
if forfunc=="Equal":
|
|
832
|
+
# Get centers and check that they are in the interior of the inner catalog
|
|
833
|
+
centers_1, centers_2 = self.get_pixelization(cat, self.radii[indR], self.accuracies[indR], R_crop=0., mgrid=True)
|
|
834
|
+
_f, _s1, _s2, _dpixi, _, _, = args_innergrid
|
|
835
|
+
pixs_c = (((centers_2-_s2)//_dpixi)*_f[0,1].shape[1] + (centers_1-_s1)//_dpixi).astype(int)
|
|
836
|
+
sel_inner = _f[0,1]>0.
|
|
837
|
+
sel_centers = sel_inner.flatten()[pixs_c]
|
|
838
|
+
centers_1 = centers_1[sel_centers]
|
|
839
|
+
centers_2 = centers_2[sel_centers]
|
|
840
|
+
ncenters = len(centers_1)
|
|
841
|
+
elif forfunc=="EqualGrid":
|
|
842
|
+
# Get centers along each dimension
|
|
843
|
+
centers_1, centers_2 = self.get_pixelization(cat, self.radii[indR], self.accuracies[indR], R_crop=0., mgrid=False)
|
|
844
|
+
ncenters = len(centers_1)*len(centers_2)
|
|
845
|
+
|
|
846
|
+
#self.dpix_hash = max(0.25, self.radii[indR])
|
|
847
|
+
cat.build_spatialhash(dpix=self.dpix_hash, extent=[None, None, None, None])
|
|
848
|
+
hashgrid = FlatPixelGrid_2D(cat.pix1_start, cat.pix2_start,
|
|
849
|
+
cat.pix1_n, cat.pix2_n, cat.pix1_d, cat.pix2_d)
|
|
850
|
+
regridded_mask = cat.mask.regrid(hashgrid).data.flatten().astype(np.float64)
|
|
851
|
+
|
|
852
|
+
if forfunc=="Equal":
|
|
853
|
+
args_centers = (self.radii[indR], centers_1, centers_2, ncenters,)
|
|
854
|
+
elif forfunc=="EqualGrid":
|
|
855
|
+
args_centers = (self.radii[indR], centers_1, centers_2, len(centers_1), len(centers_2), )
|
|
856
|
+
if forfunc=="Equal":
|
|
857
|
+
args_ofw = (self.order_max, self.filters_dict[self.filter_form],
|
|
858
|
+
np.int32(self.multicountcorr), np.int32(Nbar_policy), np.float64(self.weight_outer), np.float64(self.weight_inpainted), )
|
|
859
|
+
elif forfunc=="EqualGrid":
|
|
860
|
+
args_ofw = (self.order_max, self.filters_dict[self.filter_form], self.ap_weights_dict[self.ap_weights],
|
|
861
|
+
np.float64(self.weight_outer), np.float64(self.weight_inpainted), )
|
|
862
|
+
args_cat = (cat.weight.astype(np.float64), cat.isinner.astype(np.float64),
|
|
863
|
+
cat.pos1.astype(np.float64), cat.pos2.astype(np.float64), cat.tracer.astype(np.float64),
|
|
864
|
+
zbins, np.int32(nbinsz), np.int32(cat.ngal), )
|
|
865
|
+
if forfunc=="Equal":
|
|
866
|
+
args_mask = (regridded_mask, self.frac_covs, self.nfrac_covs, 0, )
|
|
867
|
+
elif forfunc=="EqualGrid":
|
|
868
|
+
args_mask = (regridded_mask, )
|
|
869
|
+
args_hash = (np.float64(cat.pix1_start), np.float64(cat.pix2_start),
|
|
870
|
+
np.float64(cat.pix1_d), np.float64(cat.pix2_d),
|
|
871
|
+
np.int32(cat.pix1_n), np.int32(cat.pix2_n),
|
|
872
|
+
cat.index_matcher.astype(np.int32), cat.pixs_galind_bounds.astype(np.int32),
|
|
873
|
+
cat.pix_gals.astype(np.int32)
|
|
874
|
+
)
|
|
875
|
+
if forfunc=="Equal":
|
|
876
|
+
len_out = self._nzcombis_tot(nbinsz,dotomo)*self.nfrac_covs
|
|
877
|
+
args_out = (np.zeros(len_out).astype(np.float64), np.zeros(len_out).astype(np.float64))
|
|
878
|
+
elif forfunc=="EqualGrid":
|
|
879
|
+
args_out = (np.zeros(3*nbinsz*ncenters).astype(np.float64),
|
|
880
|
+
np.zeros(2*ncenters).astype(np.float64),
|
|
881
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64),
|
|
882
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64),
|
|
883
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64),
|
|
884
|
+
np.zeros(self.order_max*nbinsz*ncenters).astype(np.float64), )
|
|
885
|
+
# Return the parameters for the Nap computation
|
|
886
|
+
args = (*args_centers,
|
|
887
|
+
*args_ofw,
|
|
888
|
+
*args_cat,
|
|
889
|
+
*args_mask,
|
|
890
|
+
*args_hash,
|
|
891
|
+
np.int32(self.nthreads),
|
|
892
|
+
*args_out)
|
|
893
|
+
if False:
|
|
894
|
+
if forfunc=="Equal":func = self.clib.NapnSingleDisc
|
|
895
|
+
if forfunc=="EqualGrid":func = self.clib.ApertureCountsMap_Equal
|
|
896
|
+
|
|
897
|
+
for elarg, arg in enumerate(args):
|
|
898
|
+
toprint = (elarg, type(arg),)
|
|
899
|
+
if isinstance(arg, np.ndarray):
|
|
900
|
+
toprint += (type(arg[0]), arg.shape)
|
|
901
|
+
#try:
|
|
902
|
+
toprint += (func.argtypes[elarg], )
|
|
903
|
+
print(toprint)
|
|
904
|
+
print(arg)
|
|
905
|
+
#except:
|
|
906
|
+
# print("We did have a problem for arg %i"%elarg)
|
|
907
|
+
|
|
908
|
+
return args
|
|
909
|
+
|
|
910
|
+
def _nzcombis_tot(self, nbinsz, dotomo):
|
|
911
|
+
res = 0
|
|
912
|
+
for order in range(1, self.order_max+1):
|
|
913
|
+
res += self._nzcombis_order(order, nbinsz, dotomo)
|
|
914
|
+
return res
|
|
915
|
+
|
|
916
|
+
def _nzcombis_order(self, order, nbinsz, dotomo):
|
|
917
|
+
if not dotomo:
|
|
918
|
+
return 1
|
|
919
|
+
else:
|
|
920
|
+
return int(nbinsz*factorial(nbinsz+order-1)/(factorial(nbinsz)*factorial(order)))
|
|
921
|
+
|
|
922
|
+
def _cumnzcombis_order(self, order, nbinsz, dotomo):
|
|
923
|
+
res = 0
|
|
924
|
+
for order in range(1, order+1):
|
|
925
|
+
res += self._nzcombis_order(order, nbinsz, dotomo)
|
|
926
|
+
return res
|
|
927
|
+
|
|
928
|
+
def genzcombi(self, zs, nbinsz=None):
|
|
929
|
+
if nbinsz is None:
|
|
930
|
+
nbinsz = self.nbinsz
|
|
931
|
+
if nbinsz is None:
|
|
932
|
+
raise ValueError("No value for `nbinsz` has been allocated yet.")
|
|
933
|
+
if len(zs)>self.order_max:
|
|
934
|
+
raise ValueError("We only computed the statistics up to order %i."%self.order_max)
|
|
935
|
+
if max(zs) >= nbinsz:
|
|
936
|
+
raise ValueError("We only have %i tomographic bins available."%nbinsz)
|
|
937
|
+
|
|
938
|
+
order = len(zs)
|
|
939
|
+
return self._cumnzcombis_order(order-1,nbinsz,True) + self.combinatorics[order].sel2ind(zs)
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
def getnap(self, indR, cat, dotomo=True):
|
|
943
|
+
""" This simply computes an aperture mass map together with weights and coverages """
|
|
944
|
+
nbinsz = cat.nbinsz
|
|
945
|
+
if not dotomo:
|
|
946
|
+
nbinsz = 1
|
|
947
|
+
|
|
948
|
+
args = self._buildargs(cat, None, dotomo, indR, forfunc="EqualGrid")
|
|
949
|
+
ncenters_1 = args[3]
|
|
950
|
+
ncenters_2 = args[4]
|
|
951
|
+
self.clib.ApertureCountsMap_Equal(*args)
|
|
952
|
+
|
|
953
|
+
counts = args[-6].reshape((nbinsz, 3, ncenters_2, ncenters_1))
|
|
954
|
+
covs = args[-5].reshape((2, ncenters_2, ncenters_1))
|
|
955
|
+
Msn = args[-4].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
956
|
+
Sn = args[-3].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
957
|
+
Napn = args[-2].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
958
|
+
Napn_counts = args[-1].reshape((nbinsz, self.order_max, ncenters_2, ncenters_1))
|
|
959
|
+
|
|
960
|
+
return counts, covs, Msn, Sn, Napn, Napn_counts
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
class MapCombinatorics:
|
|
964
|
+
|
|
965
|
+
def __init__(self, nradii, order_max):
|
|
966
|
+
self.nradii = nradii
|
|
967
|
+
self.order_max = order_max
|
|
968
|
+
self.psummem = None
|
|
969
|
+
self.nindices = self.psumtot(order_max+1, nradii)
|
|
970
|
+
|
|
971
|
+
def psumtot(self, n, m):
|
|
972
|
+
""" Calls to (n-1)-fold nested loop over m indicices
|
|
973
|
+
where i1 <= i2 <= ... <= in. This is equivalent to the
|
|
974
|
+
number of independent Map^i components over a range of
|
|
975
|
+
m radii (0<i<=n) as well as to the size of the multivariate
|
|
976
|
+
power sum set generating those multivariate cumulants.
|
|
977
|
+
|
|
978
|
+
It can alternatively be used do count through the flattened
|
|
979
|
+
redshift indices for a single-scale computation up until a
|
|
980
|
+
maximum order.
|
|
981
|
+
|
|
982
|
+
Example:
|
|
983
|
+
psumtot(m=10,n=4) gives the same result as the code
|
|
984
|
+
>>> res = 0
|
|
985
|
+
>>> for i1 in range(10):
|
|
986
|
+
>>> for i2 in range(i1,10):
|
|
987
|
+
>>> for i3 in range(i2,10):
|
|
988
|
+
>>> res += 1
|
|
989
|
+
>>> print(res)
|
|
990
|
+
|
|
991
|
+
Notes:
|
|
992
|
+
* The recursion reads as follows:
|
|
993
|
+
s(m,0) = 1
|
|
994
|
+
s(m,n) = sum_{i=1}^{m-1} s(m-1,n-1)
|
|
995
|
+
[Have not formally proved that but checked with pen and paper
|
|
996
|
+
up until n=3 on examples and the underlying geometry does make
|
|
997
|
+
sense. Testing against nested loops also works as long as the
|
|
998
|
+
loops can be computed in a sensible amount of time]
|
|
999
|
+
* As the solution is recusive and therefore might take long to
|
|
1000
|
+
compute we use a memoization technique to get rid of all of
|
|
1001
|
+
the unneccessary nested calls.
|
|
1002
|
+
"""
|
|
1003
|
+
|
|
1004
|
+
assert(m<=self.nradii)
|
|
1005
|
+
assert(n<=self.order_max+1)
|
|
1006
|
+
|
|
1007
|
+
# For initial call allocate memo
|
|
1008
|
+
if self.psummem is None:
|
|
1009
|
+
self.psummem = np.zeros((n,m))#, dtype=np.int)
|
|
1010
|
+
self.psummem[0] = np.ones(m)
|
|
1011
|
+
# Base case
|
|
1012
|
+
if m<=0 or n<=0:
|
|
1013
|
+
return self.psummem[n,m]
|
|
1014
|
+
# Recover from memo
|
|
1015
|
+
if self.psummem[n-1,m-1] != 0:
|
|
1016
|
+
return self.psummem[n-1,m-1]
|
|
1017
|
+
# Add to memo
|
|
1018
|
+
else:
|
|
1019
|
+
res = 0
|
|
1020
|
+
for i in range(m):
|
|
1021
|
+
res += self.psumtot(n-1,m-i)
|
|
1022
|
+
self.psummem[n-1,m-1] = res
|
|
1023
|
+
return int(self.psummem[n-1,m-1])
|
|
1024
|
+
|
|
1025
|
+
def sel2ind(self, sel):
|
|
1026
|
+
"""
|
|
1027
|
+
Assignes unique index to given selection in powr sum set
|
|
1028
|
+
Note that sel[0] <= sel[1] <= ... <= sel[self.nradii-1] is required!
|
|
1029
|
+
"""
|
|
1030
|
+
# Check validity
|
|
1031
|
+
#assert(len(sel)==n-1)
|
|
1032
|
+
#for el in range(len(sel)-1):
|
|
1033
|
+
# assert(sel[el+1] >= sel[el])
|
|
1034
|
+
#assert(sel[-1] <= m)
|
|
1035
|
+
|
|
1036
|
+
i = 0
|
|
1037
|
+
ind = 0
|
|
1038
|
+
ind_sel = 0
|
|
1039
|
+
lsel = len(sel)
|
|
1040
|
+
while True:
|
|
1041
|
+
while i >= sel[ind_sel]:
|
|
1042
|
+
#print(i,ind_sel)
|
|
1043
|
+
ind_sel += 1
|
|
1044
|
+
if ind_sel >= lsel:
|
|
1045
|
+
return int(ind)
|
|
1046
|
+
ind += self.psummem[self.order_max-1-ind_sel, self.nradii-1-i]
|
|
1047
|
+
i += 1
|
|
1048
|
+
|
|
1049
|
+
return int(ind)
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def ind2sel(self, ind):
|
|
1053
|
+
""" Inverse of sel2ind...NOT WORKING YET """
|
|
1054
|
+
|
|
1055
|
+
sel = np.zeros(self.order_max)#, dtype=np.int)
|
|
1056
|
+
# Edge cases
|
|
1057
|
+
if ind==0:
|
|
1058
|
+
return sel.astype(np.int)
|
|
1059
|
+
if ind==1:
|
|
1060
|
+
sel[-1] = 1
|
|
1061
|
+
return sel.astype(np.int)
|
|
1062
|
+
if ind==self.nindices-1:
|
|
1063
|
+
return (self.nradii-1)*np.zeros(self.order_max, dtype=np.int)
|
|
1064
|
+
|
|
1065
|
+
tmpind = ind # Remainder of index in psum
|
|
1066
|
+
nextind_ax0 = self.order_max-1 # Value of i_k
|
|
1067
|
+
nextind_ax1 = self.nradii-1 # Helper
|
|
1068
|
+
tmpsel = 0 # Value of i_k
|
|
1069
|
+
indsel = 0 # Index in selection
|
|
1070
|
+
while True:
|
|
1071
|
+
nextsubs = 0
|
|
1072
|
+
while True:
|
|
1073
|
+
tmpsubs = self.psummem[nextind_ax0, nextind_ax1]
|
|
1074
|
+
#print(tmpind, nextsubs, tmpsubs, sel)
|
|
1075
|
+
if tmpind > nextsubs + tmpsubs:
|
|
1076
|
+
nextind_ax1 -= 1
|
|
1077
|
+
tmpsel += 1
|
|
1078
|
+
nextsubs += tmpsubs
|
|
1079
|
+
elif tmpind < nextsubs + tmpsubs:
|
|
1080
|
+
nextind_ax0 -= 1
|
|
1081
|
+
tmpind -= nextsubs
|
|
1082
|
+
sel[indsel] = tmpsel
|
|
1083
|
+
indsel += 1
|
|
1084
|
+
break
|
|
1085
|
+
else:
|
|
1086
|
+
sel[indsel:] = tmpsel + 1
|
|
1087
|
+
return sel.astype(np.int)
|
|
1088
|
+
if sel[-2] != 0:
|
|
1089
|
+
sel[-1] = sel[-2] + tmpind
|
|
1090
|
+
if sel[-1] != 0:
|
|
1091
|
+
return sel.astype(np.int)
|