orpheus-npcf 0.1.14__cp314-cp314-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.

Potentially problematic release.


This version of orpheus-npcf might be problematic. Click here for more details.

orpheus/catalog.py ADDED
@@ -0,0 +1,1202 @@
1
+ # TODO Reactivate gridded catalog instances?
2
+
3
+ import ctypes as ct
4
+ import numpy as np
5
+ from numpy.ctypeslib import ndpointer
6
+ from pathlib import Path
7
+ import glob
8
+ from .utils import get_site_packages_dir, search_file_in_site_package, convertunits
9
+ from .flat2dgrid import FlatPixelGrid_2D, FlatDataGrid_2D
10
+ from .patchutils import gen_cat_patchindices, frompatchindices_preparerot
11
+ import sys
12
+ import time
13
+
14
+
15
+ __all__ = ["Catalog", "ScalarTracerCatalog", "SpinTracerCatalog"]
16
+
17
+
18
+ ##############################################
19
+ ## Classes that deal with discrete catalogs ##
20
+ ##############################################
21
+ class Catalog:
22
+
23
+ r"""Class containing variables and methods of a catalog of tracers.
24
+ Attributes
25
+ ----------
26
+ pos1: numpy.ndarray
27
+ The :math:`x`-positions of the tracer objects
28
+ pos2: numpy.ndarray
29
+ The :math:`y`-positions of the tracer objects
30
+ weight: numpy.ndarray, optional, defaults to ``None``
31
+ The weights of the tracer objects. If set to ``None`` all weights are assumed to be unity.
32
+ zbins: numpy.ndarray, optional, defaults to ``None``
33
+ The tomographic redshift bins of the tracer objects. If set to ``None`` all zbins are assumed to be zero.
34
+ nbinsz: int
35
+ The number of tomographic bins
36
+ isinner: numpy.ndarray
37
+ A flag signaling wheter a tracer is within the interior part of the footprint
38
+ units_pos1: string, defaults to ``None``
39
+ The unit of the :math:`x`-positions, should be in [None, 'rad', 'deg', 'arcmin'].
40
+ For non-spherical catalogs we auto-set this to None. Spherical catalogs are internally transformed to units of degrees.
41
+ units_pos2: string, defaults to ``None``
42
+ The unit of the :math:`y`-positions, should be in [None, 'rad', 'deg', 'arcmin'].
43
+ For non-spherical catalogs we auto-set this to None. Spherical catalogs are internally transformed to units of degrees.
44
+ geometry: string, defualts to ``'flat2d'``
45
+ Specifies the topology of the space the points are located in. Should be in ['flat2d', 'spherical'].
46
+ min1: float
47
+ The smallest :math:`x`-value appearing in the catalog
48
+ max1: float
49
+ The largest :math:`x`-value appearing in the catalog
50
+ min2: float
51
+ The smallest :math:`y`-value appearing in the catalog
52
+ max2: float
53
+ The largest :math:`y`-value appearing in the catalog
54
+ len1: float
55
+ The extent of the catalog in :math:`x`-direction.
56
+ len2: float
57
+ The extent of the catalog in :math:`y`-direction.
58
+ hasspatialhash: bool
59
+ Flag on wheter a spatial hash structure has been allocated for the catalog
60
+ index_matcher: numpy.ndarray
61
+ Indicates on whether there is a tracer in each of the pixels in the spatial hash.
62
+
63
+
64
+ .. note::
65
+
66
+ The ``zbins`` parameter can also be used for other characteristics of the tracers (i.e. color cuts).
67
+ """
68
+
69
+ def __init__(self, pos1, pos2, weight=None, zbins=None, isinner=None,
70
+ units_pos1=None, units_pos2=None, geometry='flat2d',
71
+ mask=None, zbins_mean=None, zbins_std=None):
72
+
73
+ self.pos1 = pos1.astype(np.float64)
74
+ self.pos2 = pos2.astype(np.float64)
75
+ self.weight = weight
76
+ self.zbins = zbins
77
+ self.ngal = len(self.pos1)
78
+ # Allocate weights
79
+ if self.weight is None:
80
+ self.weight = np.ones(self.ngal)
81
+ self.weight = self.weight.astype(np.float64)
82
+ #self.weight /= np.mean(self.weight)
83
+ # Require zbins to only contain elements in {0, 1, ..., nbinsz-1}
84
+ if self.zbins is None:
85
+ self.zbins = np.zeros(self.ngal)
86
+ self.zbins = self.zbins.astype(np.int32)
87
+ self.nbinsz = len(np.unique(self.zbins))
88
+ assert(np.max(self.zbins)-np.min(self.zbins)==self.nbinsz-1)
89
+ self.zbins -= (np.min( self.zbins))
90
+ if isinner is None:
91
+ isinner = np.ones(self.ngal, dtype=np.float64)
92
+ self.isinner = np.asarray(isinner, dtype=np.float64)
93
+ self.units_pos1 = units_pos1
94
+ self.units_pos2 = units_pos2
95
+ self.geometry = geometry
96
+ assert(self.geometry in ['flat2d','spherical'])
97
+ if self.geometry == 'flat2d':
98
+ self.units_pos1 = None
99
+ self.units_pos2 = None
100
+ if self.geometry == 'spherical':
101
+ assert(self.units_pos1 in ['rad', 'deg', 'arcmin'])
102
+ assert(self.units_pos2 in ['rad', 'deg', 'arcmin'])
103
+ self.pos1 *= convertunits(self.units_pos1, 'deg')
104
+ self.pos2 *= convertunits(self.units_pos2, 'deg')
105
+ self.units_pos1 = 'deg'
106
+ self.units_pos2 = 'deg'
107
+ # Make sure that footprint is contiguous
108
+ # 1) Compute internal distance between tracers
109
+ # 2) Compute distance around the origin
110
+ # 3) If largest distance is internal, i.e. catalog not contiguous
111
+ # split catalog at this boundary and shift one side by 360 deg
112
+ # Note that this algorithm only works for truly contiguous fields,
113
+ # but might fail for catalogues consisting of multiple disconnected
114
+ # (yet contiguous) patches covering the whole range of ra...
115
+ ra_sorted = np.sort(self.pos1)
116
+ diffs = np.diff(ra_sorted)
117
+ wrap_diff = (360.0 - ra_sorted[-1]) + ra_sorted[0]
118
+ if wrap_diff <= np.max(diffs):
119
+ max_gap_idx = np.argmax(diffs)
120
+ split_value = ra_sorted[max_gap_idx]
121
+ self.pos1[self.pos1 > split_value] -= 360
122
+ print('NOTE: Catalog not contiguous, shifted RA coordinates > %.2f deg by -360 deg.'%split_value)
123
+
124
+ self.mask = mask
125
+ assert(isinstance(self.mask, FlatDataGrid_2D) or self.mask is None)
126
+ if isinstance(self.mask, FlatDataGrid_2D):
127
+ self.__checkmask()
128
+ assert(np.min(self.isinner) >= 0.)
129
+ assert(np.max(self.isinner) <= 1.)
130
+ assert(len(self.isinner)==self.ngal)
131
+ assert(len(self.pos2)==self.ngal)
132
+ assert(len(self.weight)==self.ngal)
133
+ assert(len(self.zbins)==self.ngal)
134
+ assert(np.min(self.weight)>0.)
135
+
136
+ self.zbins_mean = zbins_mean
137
+ self.zbins_std = zbins_std
138
+ for _ in [self.zbins_mean, self.zbins_mean]:
139
+ if _ is not None:
140
+ assert(isinstance(_,np.ndarray))
141
+ assert(len(_)==self.nbinsz)
142
+
143
+ self.min1 = np.min(self.pos1)
144
+ self.min2 = np.min(self.pos2)
145
+ self.max1 = np.max(self.pos1)
146
+ self.max2 = np.max(self.pos2)
147
+ self.len1 = self.max1-self.min1
148
+ self.len2 = self.max2-self.min2
149
+
150
+ self.spatialhash = None # Check whether needed not in docs
151
+ self.hasspatialhash = False
152
+ self.index_matcher = None
153
+ self.pixs_galind_bounds = None
154
+ self.pix_gals = None
155
+ self.pix1_start = None
156
+ self.pix1_d = None
157
+ self.pix1_n = None
158
+ self.pix2_start = None
159
+ self.pix2_d = None
160
+ self.pix2_n = None
161
+
162
+ self.patchinds = None
163
+
164
+ self.assign_methods = {"NGP":0, "CIC":1, "TSC":2}
165
+
166
+ ## Link compiled libraries ##
167
+ # Method that works for LP
168
+ target_path = __import__('orpheus').__file__
169
+ self.library_path = str(Path(__import__('orpheus').__file__).parent.absolute())
170
+ self.clib = ct.CDLL(glob.glob(self.library_path+"/orpheus_clib*.so")[0])
171
+ # Method that works for RR (but not for LP with a local HPC install)
172
+ #self.clib = ct.CDLL(search_file_in_site_package(get_site_packages_dir(),"orpheus_clib"))
173
+ #self.library_path = str(Path(__import__('orpheus').__file__).parent.parent.absolute())
174
+ #print(self.library_path)
175
+ #self.clib = ct.CDLL(glob.glob(self.library_path+"/orpheus_clib*.so")[0])
176
+ #self.library_path = str(Path(__file__).parent.absolute()) + "/src/"
177
+ #self.clib = ct.CDLL(self.library_path + "clibrary.so")
178
+ p_c128 = ndpointer(np.complex128, flags="C_CONTIGUOUS")
179
+ p_f64 = ndpointer(np.float64, flags="C_CONTIGUOUS")
180
+ p_f32 = ndpointer(np.float32, flags="C_CONTIGUOUS")
181
+ p_i32 = ndpointer(np.int32, flags="C_CONTIGUOUS")
182
+ p_f64_nof = ndpointer(np.float64)
183
+
184
+ # Assigns a set of tomographic fields over a grid
185
+ # Safely called within 'togrid' function
186
+ self.clib.assign_fields.restype = ct.c_void_p
187
+ self.clib.assign_fields.argtypes = [
188
+ p_f64, p_f64, p_i32, p_f64, p_f64, ct.c_int32, ct.c_int32, ct.c_int32,
189
+ ct.c_int32, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32,
190
+ ct.c_int32, np.ctypeslib.ndpointer(dtype=np.float64)]
191
+
192
+ # Assigns a set of tomographic fields over a grid
193
+ # Safely called within 'togrid' function
194
+ self.clib.gen_weightgrid2d.restype = ct.c_void_p
195
+ self.clib.gen_weightgrid2d.argtypes = [
196
+ p_f64, p_f64, ct.c_int32, ct.c_int32,
197
+ ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32, ct.c_int32,
198
+ np.ctypeslib.ndpointer(dtype=np.int32),
199
+ np.ctypeslib.ndpointer(dtype=np.float64)]
200
+
201
+ # Generate pixel --> galaxy mapping
202
+ # Safely called within other wrapped functions
203
+ self.clib.build_spatialhash.restype = ct.c_void_p
204
+ self.clib.build_spatialhash.argtypes = [
205
+ p_f64, p_f64, ct.c_int32, ct.c_double, ct.c_double, ct.c_double, ct.c_double,
206
+ ct.c_int32, ct.c_int32,
207
+ np.ctypeslib.ndpointer(dtype=np.int32)]
208
+
209
+ self.clib.reducecat.restype = ct.c_void_p
210
+ self.clib.reducecat.argtypes = [
211
+ p_f64, p_f64, p_f64, p_f64, p_f64, ct.c_int32, ct.c_int32, ct.c_int32,
212
+ ct.c_double, ct.c_double, ct.c_double, ct.c_double, ct.c_int32, ct.c_int32, ct.c_int32,
213
+ p_f64_nof, p_f64_nof, p_f64_nof, p_f64_nof, p_f64_nof,ct.c_int32]
214
+
215
+ def topatches(self, npatches=None, area_patch_deg2_target=None, patchextend_deg=2.,other_cats=None,
216
+ nside_hash=128, verbose=False, method='kmeans_healpix',
217
+ kmeanshp_maxiter=1000, kmeanshp_tol=1e-10, kmeanshp_randomstate=42,healpix_nside=8):
218
+ r""" Decomposes a full-sky catalog into patches.
219
+ """
220
+
221
+ # We are only dealing with a single catalog
222
+ if other_cats is None:
223
+ assert(self.geometry in ['spherical'])
224
+ self.patchinds = gen_cat_patchindices(ra_deg=self.pos1,
225
+ dec_deg=self.pos2,
226
+ npatches=npatches,
227
+ patchextend_arcmin=patchextend_deg*60.,
228
+ nside_hash=nside_hash,
229
+ verbose=verbose,
230
+ method=method,
231
+ kmeanshp_maxiter=kmeanshp_maxiter,
232
+ kmeanshp_tol=kmeanshp_tol,
233
+ kmeanshp_randomstate=kmeanshp_randomstate,
234
+ healpix_nside=healpix_nside
235
+ )
236
+ if method=='healpix':
237
+ self.npatches = 12*healpix_nside*healpix_nside
238
+ else:
239
+ self.npatches = npatches
240
+
241
+
242
+ # We want to create equivalent patches for multiple catalogs
243
+ else:
244
+ # Make sure that each catalog is a child of Catalog and has the same geometry
245
+ # As each spherical catalog per definition has ra/dec in units of degrees, this is sufficient.
246
+ ntracer_tot = self.ngal
247
+ cumngals = np.zeros(2+len(other_cats),dtype=int)
248
+ cumngals[1] = self.ngal
249
+ for elcat, cat in enumerate(other_cats):
250
+ if not isinstance(cat, Catalog):
251
+ raise ValueError('Each catalog should be inherited from orpheus.Catalog class.')
252
+ if not cat.geometry=='spherical':
253
+ raise ValueError('Patch decomposition only available for spherical catlogs')
254
+ ntracer_tot += cat.ngal
255
+ cumngals[elcat+2] = ntracer_tot
256
+
257
+ # Build a joint catalog collecting all positions of the different catalogs
258
+ jointpos1 = np.zeros(ntracer_tot)
259
+ jointpos2 = np.zeros(ntracer_tot)
260
+ jointweight = np.zeros(ntracer_tot)
261
+ jointpos1[:cumngals[1]] += self.pos1
262
+ jointpos2[:cumngals[1]] += self.pos2
263
+ jointweight[:cumngals[1]] += self.weight
264
+ for elcat, cat in enumerate(other_cats):
265
+ jointpos1[cumngals[elcat+1]:cumngals[elcat+2]] += cat.pos1
266
+ jointpos2[cumngals[elcat+1]:cumngals[elcat+2]] += cat.pos2
267
+ jointweight[cumngals[elcat+1]:cumngals[elcat+2]] += cat.weight
268
+ jointcat = Catalog(pos1=jointpos1, pos2=jointpos2, weight=jointweight,
269
+ geometry='spherical', units_pos1='deg', units_pos2='deg')
270
+
271
+ # Build patches of joint catalog
272
+ jointcat.topatches(npatches=npatches,
273
+ patchextend_deg=patchextend_deg,
274
+ other_cats=None,
275
+ nside_hash=nside_hash,
276
+ verbose=verbose,
277
+ method=method,
278
+ kmeanshp_maxiter=kmeanshp_maxiter,
279
+ kmeanshp_tol=kmeanshp_tol,
280
+ kmeanshp_randomstate=kmeanshp_randomstate)
281
+
282
+ # Distribute the patchindices of the joint catalog to the individual instances
283
+ self.patchinds = {}
284
+ self.patchinds['info'] = {}
285
+ self.patchinds['info']['patchcenters'] = jointcat.patchinds['info']['patchcenters']
286
+ self.patchinds['info']['patchareas'] = jointcat.patchinds['info']['patchareas']
287
+ self.patchinds['info']['patch_ngalsinner'] = np.zeros(jointcat.npatches)
288
+ self.patchinds['info']['patch_ngalsouter'] = np.zeros(jointcat.npatches)
289
+ self.patchinds['patches'] = {}
290
+ for elp in range(jointcat.npatches):
291
+ _inds = jointcat.patchinds['patches'][elp]
292
+ seli = (_inds['inner']>=cumngals[0])*(_inds['inner']<cumngals[1])
293
+ selo = (_inds['outer']>=cumngals[0])*(_inds['outer']<cumngals[1])
294
+ self.patchinds['info']['patch_ngalsinner'][elp] = np.sum(seli)
295
+ self.patchinds['info']['patch_ngalsouter'][elp] = np.sum(selo)
296
+ self.patchinds['patches'][elp] = {}
297
+ self.patchinds['patches'][elp]['inner'] = _inds['inner'][seli]
298
+ self.patchinds['patches'][elp]['outer'] = _inds['outer'][selo]
299
+ for elcat, cat in enumerate(other_cats):
300
+ cat.patchinds = {}
301
+ cat.patchinds['info'] = {}
302
+ cat.patchinds['info']['patchcenters'] = jointcat.patchinds['info']['patchcenters']
303
+ cat.patchinds['info']['patchareas'] = jointcat.patchinds['info']['patchareas']
304
+ cat.patchinds['info']['patch_ngalsinner'] = np.zeros(jointcat.npatches)
305
+ cat.patchinds['info']['patch_ngalsouter'] = np.zeros(jointcat.npatches)
306
+ cat.patchinds['patches'] = {}
307
+ for elp in range(jointcat.npatches):
308
+ _inds = jointcat.patchinds['patches'][elp]
309
+ seli = (_inds['inner']>=cumngals[elcat+1])*(_inds['inner']<cumngals[elcat+2])
310
+ selo = (_inds['outer']>=cumngals[elcat+1])*(_inds['outer']<cumngals[elcat+2])
311
+ cat.patchinds['info']['patch_ngalsinner'][elp] = np.sum(seli)
312
+ cat.patchinds['info']['patch_ngalsouter'][elp] = np.sum(selo)
313
+ cat.patchinds['patches'][elp] = {}
314
+ cat.patchinds['patches'][elp]['inner'] = _inds['inner'][seli]-cumngals[elcat+1]
315
+ cat.patchinds['patches'][elp]['outer'] = _inds['outer'][selo]-cumngals[elcat+1]
316
+
317
+ # Finalize setting attributes for all instances
318
+ self.npatches = npatches
319
+ for cat in other_cats:
320
+ cat.npatches = npatches
321
+
322
+ def _patchind_preparerot(self, index, rotsignflip=False):
323
+
324
+ assert(self.patchinds is not None)
325
+ assert(self.geometry in ['spherical'])
326
+
327
+ return frompatchindices_preparerot(index, self.patchinds, self.pos1, self.pos2, rotsignflip)
328
+
329
+ # Reduces catalog to smaller catalog where positions & quantities are
330
+ # averaged over regular grid
331
+ def _reduce(self, fields, dpix, dpix2=None, relative_to_hash=None, normed=True, shuffle=0,
332
+ extent=[None,None,None,None], forcedivide=1,
333
+ ret_inst=False):
334
+ r"""Paints a catalog onto a grid with equal-area cells
335
+
336
+ Parameters
337
+ ----------
338
+ fields: list
339
+ The fields to be painted to the grid. Each field is given as a 1D array of float.
340
+ dpix: float
341
+ The sidelength of a grid cell.
342
+ dpix2: float, optional
343
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
344
+ If set to ``None`` the pixels are assumed to be squares.
345
+ relative_to_hash: int, optional
346
+ Forces the cell size to be an integer multiple of the cell size of the spatial hash.
347
+ Defaults to ``None``. If set to ``None`` the pixelsize is unrelated to the cell
348
+ size of the spatial hash.
349
+ normed: bool, optional
350
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
351
+ shuffle: int, optional
352
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
353
+ extent: list, optional
354
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
355
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
356
+ fully containing the discrete field tracers.
357
+ forcedivide: int, optional
358
+ Forces the number of cells in each dimensions to be divisible by some number.
359
+ Defaults to ``1``.
360
+ ret_inst: bool, optional
361
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
362
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
363
+ """
364
+
365
+ # Initialize grid
366
+ if relative_to_hash is None:
367
+ if dpix2 is None:
368
+ dpix2 = dpix
369
+ start1, start2, n1, n2 = self._gengridprops(dpix, dpix2, forcedivide, extent)
370
+ else:
371
+ assert(self.hasspatialhash)
372
+ assert(isinstance(relative_to_hash,np.int32))
373
+ start1 = self.pix1_start
374
+ start2 = self.pix2_start
375
+ dpix = self.pix1_d/np.float64(relative_to_hash)
376
+ dpix2 = self.pix2_d/np.float64(relative_to_hash)
377
+ n1 = self.pix1_n*relative_to_hash
378
+ n2 = self.pix2_n*relative_to_hash
379
+
380
+ # Prepare arguments
381
+ zbinarr = self.zbins.astype(np.int32)
382
+ nbinsz = len(np.unique(zbinarr))
383
+ ncompfields = []
384
+ scalarquants = []
385
+ nfields = 0
386
+ for field in fields:
387
+ if type(field[0].item()) is float:
388
+ scalarquants.append(field)
389
+ nfields += 1
390
+ ncompfields.append(1)
391
+ if type(field[0].item()) is complex:
392
+ scalarquants.append(field.real)
393
+ scalarquants.append(field.imag)
394
+ nfields += 2
395
+ ncompfields.append(2)
396
+ scalarquants = np.asarray(scalarquants)
397
+
398
+ # Compute reduction (individually for each zbin)
399
+ assert(shuffle in [True, False, 0, 1, 2, 3, 4])
400
+ isinner_red = np.zeros(self.ngal, dtype=np.float64)
401
+ w_red = np.zeros(self.ngal, dtype=np.float64)
402
+ pos1_red = np.zeros(self.ngal, dtype=np.float64)
403
+ pos2_red = np.zeros(self.ngal, dtype=np.float64)
404
+ zbins_red = np.zeros(self.ngal, dtype=np.int32)
405
+ scalarquants_red = np.zeros((nfields, self.ngal), dtype=np.float64)
406
+ ind_start = 0
407
+ for elz in range(nbinsz):
408
+ sel_z = zbinarr==elz
409
+ ngal_z = np.sum(sel_z)
410
+ ngal_red_z = 0
411
+ red_shape = (len(fields), ngal_z)
412
+ isinner_red_z = np.zeros(ngal_z, dtype=np.float64)
413
+ w_red_z = np.zeros(ngal_z, dtype=np.float64)
414
+ pos1_red_z = np.zeros(ngal_z, dtype=np.float64)
415
+ pos2_red_z = np.zeros(ngal_z, dtype=np.float64)
416
+ scalarquants_red_z = np.zeros(nfields*ngal_z, dtype=np.float64)
417
+ self.clib.reducecat(self.isinner[sel_z].astype(np.float64),
418
+ self.weight[sel_z].astype(np.float64),
419
+ self.pos1[sel_z].astype(np.float64),
420
+ self.pos2[sel_z].astype(np.float64),
421
+ scalarquants[:,sel_z].flatten().astype(np.float64),
422
+ ngal_z, nfields, np.int32(normed),
423
+ dpix, dpix2, start1, start2, n1, n2, np.int32(shuffle),
424
+ isinner_red_z, w_red_z, pos1_red_z, pos2_red_z, scalarquants_red_z, ngal_red_z)
425
+ isinner_red[ind_start:ind_start+ngal_z] = isinner_red_z
426
+ w_red[ind_start:ind_start+ngal_z] = w_red_z
427
+ pos1_red[ind_start:ind_start+ngal_z] = pos1_red_z
428
+ pos2_red[ind_start:ind_start+ngal_z] = pos2_red_z
429
+ zbins_red[ind_start:ind_start+ngal_z] = elz*np.ones(ngal_z, dtype=np.int32)
430
+ scalarquants_red[:,ind_start:ind_start+ngal_z] = scalarquants_red_z.reshape((nfields, ngal_z))
431
+ ind_start += ngal_z
432
+
433
+ # Accumulate reduced atalog
434
+ sel_nonzero = w_red>0
435
+ isinner_red = isinner_red[sel_nonzero]
436
+ w_red = w_red[sel_nonzero]
437
+ pos1_red = pos1_red[sel_nonzero]
438
+ pos2_red = pos2_red[sel_nonzero]
439
+ zbins_red = zbins_red[sel_nonzero]
440
+ scalarquants_red = scalarquants_red[:,sel_nonzero]
441
+ fields_red = []
442
+ tmpcomp = 0
443
+ for elf in range(len(fields)):
444
+ if ncompfields[elf]==1:
445
+ fields_red.append(scalarquants_red[tmpcomp])
446
+ if ncompfields[elf]==2:
447
+ fields_red.append(scalarquants_red[tmpcomp]+1J*scalarquants_red[tmpcomp+1])
448
+ tmpcomp += ncompfields[elf]
449
+ #isinner_red[isinner_red<0.5] = 0
450
+ #isinner_red[isinner_red>=0.5] = 1
451
+ if ret_inst:
452
+ return Catalog(pos1=pos1_red, pos2=pos2_red, weight=w_red, zbins=zbins_red,
453
+ isinner=isinner_red.astype(np.float64)), fields_red
454
+
455
+ return w_red, pos1_red, pos2_red, zbins_red, isinner_red, fields_red
456
+
457
+ def _multihash(self, dpixs, fields, dpix_hash=None, normed=True, shuffle=0,
458
+ extent=[None,None,None,None], forcedivide=1):
459
+ r"""Builds spatialhash for a base catalog and its reductions.
460
+
461
+ Parameters
462
+ ----------
463
+ dpixs: list
464
+ The pixel sizes on which the hierarchy of reduced catalogs is constructed.
465
+ fields: list
466
+ The fields for which the multihash is constructed. Each field is given as a 1D array of float.
467
+ dpix_hash: float, optional
468
+ The size of the pixels used for the spatial hash of the hierarchy of catalogs. Defaults
469
+ to ``None``. If set to ``None`` uses the largest value of ``dpixs``.
470
+ normed: bool, optional
471
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
472
+ shuffle: int, optional
473
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
474
+ extent: list, optional
475
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
476
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
477
+ fully containing the discrete field tracers.
478
+ forcedivide: int, optional
479
+ Forces the number of cells in each dimensions to be divisible by some number.
480
+ Defaults to ``1``.
481
+
482
+ Returns
483
+ -------
484
+ ngals: list
485
+ Contains the number of galaxies for each of the catalogs in the hierarchy.
486
+ pos1s: list
487
+ Contains the :math:`x`-positions for each of the catalogs in the hierarchy.
488
+ pos2s: list
489
+ Contains the :math:`y`-positions for each of the catalogs in the hierarchy.
490
+ weights: list
491
+ Contains the tracer weights for each of the catalogs in the hierarchy.
492
+ zbins: list
493
+ Contains the tomographic redshift bins for each of the catalogs in the hierarchy.
494
+ isinners: list
495
+ Contains the flag on wheter a tracer is within the interior part of the footprint
496
+ for each of the catalogs in the hierarchy.
497
+ allfields: list
498
+ Contains the tracer fields for each of the catalogs in the hierarchy.
499
+ index_matchers: list
500
+ Contains the ``index_matchers`` arrays for each of the catalogs in the hierarchy.
501
+ See the ```index_matcher`` attribute for more information.
502
+ pixs_galind_bounds: list
503
+ Contains the ``pixs_galind_bounds`` arrays for each of the catalogs in the hierarchy.
504
+ See the ```pixs_galind_bounds`` attribute for more information.
505
+ pix_gals: list
506
+ Contains the ``pix_gals`` arrays for each of the catalogs in the hierarchy.
507
+ See the ```pix_gals`` attribute for more information.
508
+ dpixs1_true: list
509
+ Contains final values of the pixel sidelength along the :math:`x`-direction for each
510
+ of the catalogs in the hierarchy.
511
+ dpixs2_true: list
512
+ Contains final values of the pixel sidelength along the :math:`y`-direction for each
513
+ of the catalogs in the hierarchy.
514
+ """
515
+
516
+ dpixs = sorted(dpixs)
517
+ if dpix_hash is None:
518
+ dpix_hash = dpixs[-1]
519
+ if extent[0] is None:
520
+ extent = [self.min1-dpix_hash, self.max1+dpix_hash, self.min2-dpix_hash, self.max2+dpix_hash]
521
+
522
+
523
+ # Initialize spatial hash for discrete catalog
524
+ self.build_spatialhash(dpix=dpix_hash, extent=extent)
525
+ ngals = [self.ngal]
526
+ isinners = [self.isinner]
527
+ pos1s = [self.pos1]
528
+ pos2s = [self.pos2]
529
+ weights = [self.weight]
530
+ zbins = [self.zbins]
531
+ allfields = [fields]
532
+ if not normed:
533
+ allfields[0] *= self.weight
534
+ index_matchers = [self.index_matcher]
535
+ pixs_galind_bounds = [self.pixs_galind_bounds]
536
+ pix_gals = [self.pix_gals]
537
+
538
+ # Build spatial hashes for reduced catalogs
539
+ fac_pix1 = self.pix1_d/dpix_hash
540
+ fac_pix2 = self.pix2_d/dpix_hash
541
+ dpixs1_true = np.zeros_like(np.asarray(dpixs))
542
+ dpixs2_true = np.zeros_like(np.asarray(dpixs))
543
+ #print(len(fields),fields)
544
+ for elreso in range(len(dpixs)):
545
+ #print("Doing reso %i"%elreso)
546
+ dpixs1_true[elreso]=fac_pix1*dpixs[elreso]
547
+ dpixs2_true[elreso]=fac_pix2*dpixs[elreso]
548
+ #print(dpixs[elreso], dpixs1_true[elreso], dpixs2_true[elreso], len(self.pos1))
549
+ nextcat, fields_red = self._reduce(fields=fields,
550
+ dpix=dpixs1_true[elreso],
551
+ dpix2=dpixs2_true[elreso],
552
+ relative_to_hash=np.int32(2**(len(dpixs)-elreso-1)),
553
+ #relative_to_hash=None,
554
+ normed=normed,
555
+ shuffle=shuffle,
556
+ extent=extent,
557
+ forcedivide=forcedivide,
558
+ ret_inst=True)
559
+ nextcat.build_spatialhash(dpix=dpix_hash, extent=extent)
560
+ ngals.append(nextcat.ngal)
561
+ isinners.append(nextcat.isinner)
562
+ pos1s.append(nextcat.pos1)
563
+ pos2s.append(nextcat.pos2)
564
+ weights.append(nextcat.weight)
565
+ zbins.append(nextcat.zbins)
566
+ allfields.append(fields_red)
567
+ index_matchers.append(nextcat.index_matcher)
568
+ pixs_galind_bounds.append(nextcat.pixs_galind_bounds)
569
+ pix_gals.append(nextcat.pix_gals)
570
+
571
+ return ngals, pos1s, pos2s, weights, zbins, isinners, allfields, index_matchers, pixs_galind_bounds, pix_gals, dpixs1_true, dpixs2_true
572
+
573
+ def _jointextent(self, others, extend=0):
574
+ r"""Draws largest possible rectangle over set of catalogs.
575
+
576
+ Parameters
577
+ ----------
578
+ others: list
579
+ Contains ``Catalog`` instances over which the joint extent will
580
+ be drawn
581
+ extend: float, optional
582
+ Include an additional boundary layer around the joint extent
583
+ of the catalogs. Defaults to ``0`` (no extension).
584
+
585
+ Returns
586
+ -------
587
+ xlo: float
588
+ The lower ``x``-boundary of the joint extent.
589
+ xhi: float
590
+ The upper ``x``-boundary of the joint extent.
591
+ ylo: float
592
+ The lower ``y``-boundary of the joint extent.
593
+ yhi: float
594
+ The upper ``y``-boundary of the joint extent.
595
+
596
+ """
597
+ for other in others:
598
+ assert(isinstance(other, Catalog))
599
+
600
+ xlo = self.min1
601
+ xhi = self.max1
602
+ ylo = self.min2
603
+ yhi = self.max2
604
+ for other in others:
605
+ xlo = min(xlo, other.min1)
606
+ xhi = max(xhi, other.max1)
607
+ ylo = min(ylo, other.min2)
608
+ yhi = max(yhi, other.max2)
609
+
610
+ return (xlo-extend, xhi+extend, ylo-extend, yhi+extend)
611
+
612
+
613
+ def create_mask(self, method="Basic", pixsize=1., apply=False, extend=0.):
614
+
615
+ assert(method in ["Basic", "Density", "Random"])
616
+
617
+ if method=="Basic":
618
+ npix_1 = int(np.ceil((self.max1-self.min1)/pixsize))
619
+ npix_2 = int(np.ceil((self.max2-self.min2)/pixsize))
620
+ self.mask = FlatDataGrid_2D(np.zeros((npix_2,npix_1), dtype=np.float64),
621
+ self.min1, self.min2, pixsize, pixsize)
622
+ if method=="Density":
623
+ start1, start2, n1, n2 = self._gengridprops(pixsize, pixsize)
624
+ reduced = self.togrid(dpix=pixsize,method="NGP",fields=[], tomo=False)
625
+ mask = (reduced[0].reshape((n2,n1))==0).astype(np.float64)
626
+ self.mask = FlatDataGrid_2D(mask, start1, start2, pixsize, pixsize)
627
+
628
+ # Add a masked buffer region around enclosing rectangle
629
+ if extend>0.:
630
+ npix_ext = int(np.ceil(extend/pixsize))
631
+ extstart1 = self.mask.start_1 - npix_ext*pixsize
632
+ extstart2 = self.mask.start_2 - npix_ext*pixsize
633
+ extmask = np.ones((self.mask.npix_2+2*npix_ext, self.mask.npix_1+2*npix_ext))
634
+ extmask[npix_ext:-npix_ext,npix_ext:-npix_ext] = self.mask.data
635
+ self.mask = FlatDataGrid_2D(extmask, extstart1, extstart2, pixsize, pixsize)
636
+
637
+ self. __checkmask()
638
+
639
+ self. __applymask(apply)
640
+
641
+ def __checkmask(self):
642
+ assert(self.mask.start_1 <= self.min1)
643
+ assert(self.mask.start_2 <= self.min2)
644
+ assert(self.mask.pix1_lbounds[-1] >= self.max1-self.mask.dpix_1)
645
+ assert(self.mask.pix2_lbounds[-1] >= self.max2-self.mask.dpix_2)
646
+
647
+ def __applymask(self, method):
648
+ assert(method in [False, True, "WeightsOnly"])
649
+
650
+
651
+
652
+ # Maps catalog to grid
653
+ def togrid(self, fields, dpix, normed=False, weighted=True, tomo=True,
654
+ extent=[None,None,None,None], method="CIC", forcedivide=1,
655
+ asgrid=None, nthreads=1, ret_inst=False):
656
+ r"""Paints a catalog of discrete tracers to a grid.
657
+
658
+ Parameters
659
+ ----------
660
+ fields: list
661
+ The fields to be painted to the grid. Each field is given as a 1D array of float.
662
+ dpix: float
663
+ The sidelength of a grid cell.
664
+ normed: bool, optional
665
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
666
+ weighted: bool, optional
667
+ Whether to apply the tracer weights of the catalog. Defaults to ``True``.
668
+ extent: list, optional
669
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
670
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
671
+ fully containing the discrete field tracers.
672
+ method: str, optional
673
+ The chosen mass assignment method applied to each of the fields. Currently supported methods
674
+ are ``NGP``, ``CIC`` and ``TSC`` assignment. Defaults to ``CIC``.
675
+ forcedivide: int, optional
676
+ Forces the number of cells in each dimensions to be divisible by some number.
677
+ Defaults to ``1``.
678
+ ret_inst: bool, optional
679
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
680
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
681
+ asgrid: bool, optional
682
+ Deprecated.
683
+ nthreads: int, optional
684
+ The number of openmp threads used for the reduction procedure. Defaults to ``1``.
685
+ ret_inst: bool, optional
686
+ Deprecated.
687
+
688
+ Returns
689
+ -------
690
+ projectedfields: list
691
+ A list of the 2D arrays containing the reduced fields
692
+ start1: float
693
+ The :math:`x`-position of the first columns' left edge
694
+ start2: float
695
+ The :math:`y`-position of the first rows' lower edge
696
+ dpix: float
697
+ The sidelength of each pixel in the grid. Note that this
698
+ value might slightly differ from the one provided in the parameters.
699
+ normed: bool
700
+ Same as the ``normed`` parameter
701
+ method: str
702
+ Same as the ``method`` parameter
703
+ """
704
+
705
+ if asgrid is not None:
706
+ raise NotImplementedError
707
+
708
+ # Choose index of method for c wrapper
709
+ assert(method in ["NGP", "CIC", "TSC"])
710
+ elmethod = self.assign_methods[method]
711
+ start1, start2, n1, n2 = self._gengridprops(dpix, dpix, forcedivide, extent)
712
+
713
+ # Prepare arguments
714
+ zbinarr = self.zbins.astype(np.int32)
715
+ if not tomo:
716
+ zbinarr = np.zeros_like(zbinarr)
717
+ nbinsz = len(np.unique(zbinarr))
718
+ nfields = len(fields)
719
+ if not weighted:
720
+ weightarr = np.ones(self.ngal, dtype=np.float64)
721
+ else:
722
+ weightarr = self.weight.astype(np.float64)
723
+ fieldarr = np.zeros(nfields*self.ngal, dtype=np.float64)
724
+ for _ in range(nfields):
725
+ fieldarr[_*self.ngal:(1+_)*self.ngal] = fields[_]
726
+
727
+ # Call wrapper and reshape output to (zbins, nfields, size_field)
728
+ proj_shape = (nbinsz, (nfields+1), n2, n1)
729
+ projectedfields = np.zeros((nbinsz*(nfields+1)*n2*n1), dtype=np.float64)
730
+ self.clib.assign_fields(self.pos1.astype(np.float64),
731
+ self.pos2.astype(np.float64),
732
+ zbinarr, weightarr, fieldarr,
733
+ nbinsz, nfields, self.ngal,
734
+ elmethod, start1, start2, dpix,
735
+ n1, n2, nthreads, projectedfields)
736
+ projectedfields = projectedfields.reshape(proj_shape)
737
+ if normed:
738
+ projectedfields[:,1:] = np.nan_to_num(projectedfields[:,1:]/projectedfields[:,0])
739
+
740
+ if not ret_inst:
741
+ return projectedfields, start1, start2, dpix, normed, method
742
+
743
+ return GriddedCatalog(projectedfields,
744
+ start1, start2, dpix, normed, method)
745
+
746
+ def gen_weightgrid2d(self, dpix,
747
+ extent=[None,None,None,None], method="CIC", forcedivide=1,
748
+ nthreads=1):
749
+
750
+ # Choose index of method for c wrapper
751
+ assert(method in ["NGP", "CIC", "TSC"])
752
+ elmethod = self.assign_methods[method]
753
+ start1, start2, n1, n2 = self._gengridprops(dpix, dpix, forcedivide, extent)
754
+
755
+ #void gen_weightgrid2d(
756
+ # double *pos1, double *pos2, int ngal, int method,
757
+ # double min1, double min2, int dpix, int n1, int n2,
758
+ # int nthreads, int *pixinds, double *pixweights){
759
+
760
+ self.ngal
761
+ nsubs = 2*elmethod+1
762
+ pixinds = np.zeros(nsubs*nsubs*self.ngal, dtype=np.int32)
763
+ pixweights = np.zeros(nsubs*nsubs*self.ngal, dtype=np.float64)
764
+ self.clib.gen_weightgrid2d(self.pos1.astype(np.float64),
765
+ self.pos2.astype(np.float64),
766
+ self.ngal, elmethod,
767
+ start1, start2, dpix, n1, n2,
768
+ nthreads, pixinds, pixweights)
769
+ return pixinds, pixweights
770
+
771
+
772
+
773
+ def build_spatialhash(self, dpix=1., extent=[None, None, None, None]):
774
+ r"""Adds a spatial hashing data structure to the catalog.
775
+
776
+ Parameters
777
+ ----------
778
+ dpix: float
779
+ The sidelength of each cell of the hash. Defaults to ``1``.
780
+ extent: list, optional
781
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
782
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
783
+ fully containing the discrete field tracers.
784
+
785
+ Note
786
+ ----
787
+ Calling this method (re-)allocates the ``index_matcher``, ``pixs_galind_bounds``, ``pix_gals``,
788
+ ``pix1_start``, ``pix2_start``, ``pix1_n``, ``pix2_n``, ``pix1_d`` and ``pix2_d``
789
+ attributes of the instance.
790
+ """
791
+
792
+ # Build extent
793
+ if extent[0] is None:
794
+ thismin1 = self.min1
795
+ else:
796
+ thismin1 = extent[0]
797
+ assert(thismin1 <= self.min1)
798
+ if extent[1] is None:
799
+ thismax1 = self.max1
800
+ else:
801
+ thismax1 = extent[1]
802
+ assert(thismax1 >= self.max1)
803
+ if extent[2] is None:
804
+ thismin2 = self.min2
805
+ else:
806
+ thismin2 = extent[2]
807
+ assert(thismin2 <= self.min2)
808
+ if extent[3] is None:
809
+ thismax2 = self.max2
810
+ else:
811
+ thismax2 = extent[3]
812
+ assert(thismax2 >= self.max2)
813
+
814
+ # Collect arguments
815
+ # Note that the C function assumes the mask to start at zero, that's why we shift
816
+ # the galaxy positions
817
+ self.pix1_start = thismin1 - dpix/1.
818
+ self.pix2_start = thismin2 - dpix/1.
819
+ stop1 = thismax1 + dpix/1.
820
+ stop2 = thismax2 + dpix/1.
821
+ self.pix1_n = int(np.ceil((stop1-self.pix1_start)/dpix))
822
+ self.pix2_n = int(np.ceil((stop2-self.pix2_start)/dpix))
823
+ npix = self.pix1_n * self.pix2_n
824
+ self.pix1_d = (stop1-self.pix1_start)/(self.pix1_n)
825
+ self.pix2_d = (stop2-self.pix2_start)/(self.pix2_n)
826
+
827
+ # Compute hashtable
828
+ result = np.zeros(2 * npix + 3 * self.ngal + 1).astype(np.int32)
829
+ self.clib.build_spatialhash(self.pos1, self.pos2, self.ngal,
830
+ self.pix1_d, self.pix2_d,
831
+ self.pix1_start, self.pix2_start,
832
+ self.pix1_n, self.pix2_n,
833
+ result)
834
+
835
+ # Allocate result
836
+ start_isoutside = 0
837
+ start_index_matcher = self.ngal
838
+ start_pixs_galind_bounds = self.ngal + npix
839
+ start_pixs_gals = self.ngal + npix + self.ngal + 1
840
+ start_ngalinpix = self.ngal + npix + self.ngal + 1 + self.ngal
841
+ self.index_matcher = result[start_index_matcher:start_pixs_galind_bounds]
842
+ self.pixs_galind_bounds = result[start_pixs_galind_bounds:start_pixs_gals]
843
+ self.pix_gals = result[start_pixs_gals:start_ngalinpix]
844
+ self.hasspatialhash = True
845
+
846
+
847
+ def _gengridprops(self, dpix, dpix2=None, forcedivide=1, extent=[None,None,None,None]):
848
+ r"""Gives some basic properties of grids created from the discrete tracers.
849
+
850
+ Parameters
851
+ ----------
852
+ dpix: float
853
+ The sidelength of a grid cell.
854
+ dpix2: float, optional
855
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
856
+ If set to ``None`` the pixels are assumed to be squares.
857
+ forcedivide: int, optional
858
+ Forces the number of cells in each dimensions to be divisible by some number.
859
+ Defaults to ``1``.
860
+ extent: list, optional
861
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
862
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
863
+ fully containing the discrete field tracers.
864
+
865
+ Returns
866
+ -------
867
+ start1: float
868
+ The :math:``x``-position of the first column.
869
+ start2: float
870
+ The :math:``y``-position of the first row.
871
+ n1: int
872
+ The number of pixels in the :math:``x``-position.
873
+ n2: int
874
+ The number of pixels in the :math:``y``-position.
875
+ """
876
+
877
+ # Define inner extent of the grid
878
+ fixedsize = False
879
+ if extent[0] is not None:
880
+ fixedsize = True
881
+ if extent[0] is None:
882
+ thismin1 = self.min1
883
+ else:
884
+ thismin1 = extent[0]
885
+ assert(thismin1 <= self.min1)
886
+ if extent[1] is None:
887
+ thismax1 = self.max1
888
+ else:
889
+ thismax1 = extent[1]
890
+ assert(thismax1 >= self.max1)
891
+ if extent[2] is None:
892
+ thismin2 = self.min2
893
+ else:
894
+ thismin2 = extent[2]
895
+ assert(thismin2 <= self.min2)
896
+ if extent[3] is None:
897
+ thismax2 = self.max2
898
+ else:
899
+ thismax2 = extent[3]
900
+ assert(thismax2 >= self.max2)
901
+
902
+ if dpix2 is None:
903
+ dpix2 = dpix
904
+
905
+ # Add buffer to grid and get associated pixelization
906
+ if not fixedsize:
907
+ start1 = thismin1 - 4*dpix
908
+ start2 = thismin2 - 4*dpix2
909
+ n1 = int(np.ceil((thismax1+4*dpix - start1)/dpix))
910
+ n2 = int(np.ceil((thismax2+4*dpix2 - start2)/dpix2))
911
+ n1 += (forcedivide - n1%forcedivide)%forcedivide
912
+ n2 += (forcedivide - n2%forcedivide)%forcedivide
913
+ else:
914
+ start1=extent[0]
915
+ start2=extent[2]
916
+ n1 = int((thismax1-thismin1)/dpix)
917
+ n2 = int((thismax2-thismin2)/dpix2)
918
+ assert(not n1%forcedivide)
919
+ assert(not n2%forcedivide)
920
+
921
+ return start1, start2, n1, n2
922
+
923
+ class ScalarTracerCatalog(Catalog):
924
+ r"""Class constructor.
925
+
926
+ Attributes
927
+ ----------
928
+ pos1: numpy.ndarray
929
+ The :math:`x`-positions of the tracer objects
930
+ pos2: numpy.ndarray
931
+ The :math:`y`-positions of the tracer objects
932
+ tracer: numpy.ndarray
933
+ The values of the scalar tracer field, i.e. galaxy weights or cosmic convergence.
934
+
935
+ Notes
936
+ -----
937
+ Inherits all other parameters and attributes from :class:`Catalog`.
938
+ Additional child-specific parameters can be passed via ``kwargs``.
939
+ """
940
+
941
+ def __init__(self, pos1, pos2, tracer, **kwargs):
942
+ super().__init__(pos1=pos1, pos2=pos2, **kwargs)
943
+ self.tracer = tracer
944
+ self.spin = 0
945
+
946
+ def reduce(self, dpix, dpix2=None, relative_to_hash=None, normed=True, shuffle=0,
947
+ extent=[None,None,None,None], forcedivide=1,
948
+ ret_inst=False):
949
+ r"""Paints the catalog onto a grid with equal-area cells
950
+
951
+ Parameters
952
+ ----------
953
+ dpix: float
954
+ The sidelength of a grid cell.
955
+ dpix2: float, optional
956
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
957
+ If set to ``None`` the pixels are assumed to be squares.
958
+ relative_to_hash: int, optional
959
+ Forces the cell size to be an integer multiple of the cell size of the spatial hash.
960
+ Defaults to ``None``. If set to ``None`` the pixelsize is unrelated to the cell
961
+ size of the spatial hash.
962
+ normed: bool, optional
963
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
964
+ shuffle: int, optional
965
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
966
+ extent: list, optional
967
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
968
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
969
+ fully containing the discrete field tracers.
970
+ forcedivide: int, optional
971
+ Forces the number of cells in each dimensions to be divisible by some number.
972
+ Defaults to ``1``.
973
+ ret_inst: bool, optional
974
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
975
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
976
+ """
977
+ res = super()._reduce(
978
+ dpix=dpix,
979
+ dpix2=None,
980
+ relative_to_hash=None,
981
+ fields=[self.tracer],
982
+ normed=normed,
983
+ shuffle=shuffle,
984
+ extent=extent,
985
+ forcedivide=forcedivide,
986
+ ret_inst=False)
987
+ (w_red, pos1_red, pos2_red, zbins_red, isinner_red, fields_red) = res
988
+ if ret_inst:
989
+ return ScalarTracerCatalog(self.spin, pos1_red, pos2_red,
990
+ fields_red[0],
991
+ weight=w_red, zbins=zbins_red, isinner=isinner_red)
992
+ return res
993
+
994
+ def multihash(self, dpixs, dpix_hash=None, normed=True, shuffle=0,
995
+ extent=[None,None,None,None], forcedivide=1):
996
+ r"""Builds spatialhash for a base catalog and its reductions.
997
+
998
+ Parameters
999
+ ----------
1000
+ dpixs: list
1001
+ The pixel sizes on which the hierarchy of reduced catalogs is constructed.
1002
+ dpix_hash: float, optional
1003
+ The size of the pixels used for the spatial hash of the hierarchy of catalogs. Defaults
1004
+ to ``None``. If set to ``None`` uses the largest value of ``dpixs``.
1005
+ normed: bool, optional
1006
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
1007
+ shuffle: int, optional
1008
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
1009
+ extent: list, optional
1010
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
1011
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
1012
+ fully containing the discrete field tracers.
1013
+ forcedivide: int, optional
1014
+ Forces the number of cells in each dimensions to be divisible by some number.
1015
+ Defaults to ``1``.
1016
+
1017
+ Returns
1018
+ -------
1019
+ res: tuple
1020
+ Contains the output of the ```Catalog._multihash method```
1021
+ """
1022
+ res = super()._multihash(
1023
+ dpixs=dpixs.astype(np.float64),
1024
+ fields=[self.tracer],
1025
+ dpix_hash=dpix_hash,
1026
+ normed=normed,
1027
+ shuffle=shuffle,
1028
+ extent=extent,
1029
+ forcedivide=forcedivide)
1030
+ return res
1031
+
1032
+
1033
+ def frompatchind(self, index, rotsignflip=False):
1034
+
1035
+ prepare = super()._patchind_preparerot(index, rotsignflip=rotsignflip)
1036
+ inds_extpatch, patch_isinner, rotangle, ra_rot, dec_rot, rotangle_polars = prepare
1037
+
1038
+ patchcat = ScalarTracerCatalog(
1039
+ pos1=ra_rot*60.,
1040
+ pos2=dec_rot*60.,
1041
+ tracer=self.tracer[inds_extpatch],
1042
+ weight=self.weight[inds_extpatch],
1043
+ zbins=self.zbins[inds_extpatch],
1044
+ isinner=patch_isinner,
1045
+ units_pos1='arcmin',
1046
+ units_pos2='arcmin',
1047
+ geometry='flat2d',
1048
+ mask=None,
1049
+ zbins_mean=None,
1050
+ zbins_std=None)
1051
+
1052
+ return patchcat
1053
+
1054
+ class SpinTracerCatalog(Catalog):
1055
+ r"""Class constructor.
1056
+
1057
+ Attributes
1058
+ ----------
1059
+ pos1: numpy.ndarray
1060
+ The :math:`x`-positions of the tracer objects
1061
+ pos2: numpy.ndarray
1062
+ The :math:`y`-positions of the tracer objects
1063
+ tracer_1: numpy.ndarray
1064
+ The values of the real part of the tracer field, i.e. galaxy ellipticities.
1065
+ tracer_2: numpy.ndarray
1066
+ The values of the imaginary part of the tracer field, i.e. galaxy ellipticities.
1067
+
1068
+ Notes
1069
+ -----
1070
+ Inherits all other parameters and attributes from :class:`Catalog`.
1071
+ Additional child-specific parameters can be passed via ``kwargs``.
1072
+ """
1073
+
1074
+ def __init__(self, spin, pos1, pos2, tracer_1, tracer_2, **kwargs):
1075
+ super().__init__(pos1=pos1, pos2=pos2, **kwargs)
1076
+ self.tracer_1 = tracer_1.astype(np.float64)
1077
+ self.tracer_2 = tracer_2.astype(np.float64)
1078
+ self.spin = int(spin)
1079
+
1080
+ def reduce(self, dpix, dpix2=None, relative_to_hash=None, normed=True, shuffle=0,
1081
+ extent=[None,None,None,None], forcedivide=1, w2field=True,
1082
+ ret_inst=False):
1083
+ r"""Paints the catalog onto a grid with equal-area cells
1084
+
1085
+ Parameters
1086
+ ----------
1087
+ dpix: float
1088
+ The sidelength of a grid cell.
1089
+ dpix2: float, optional
1090
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
1091
+ If set to ``None`` the pixels are assumed to be squares.
1092
+ relative_to_hash: int, optional
1093
+ Forces the cell size to be an integer multiple of the cell size of the spatial hash.
1094
+ Defaults to ``None``. If set to ``None`` the pixelsize is unrelated to the cell
1095
+ size of the spatial hash.
1096
+ normed: bool, optional
1097
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
1098
+ shuffle: int, optional
1099
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
1100
+ extent: list, optional
1101
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
1102
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
1103
+ fully containing the discrete field tracers.
1104
+ forcedivide: int, optional
1105
+ Forces the number of cells in each dimensions to be divisible by some number.
1106
+ Defaults to ``1``.
1107
+ w2field: bool, optional
1108
+ Adds an additional field equivalent to the squared weight of the tracers to the reduced
1109
+ catalog. Defaaullts to ``True``.
1110
+ ret_inst: bool, optional
1111
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
1112
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
1113
+ """
1114
+
1115
+ if not w2field:
1116
+ fields=(self.tracer_1, self.tracer_2,)
1117
+ else:
1118
+ fields=(self.tracer_1, self.tracer_2, self.weight**2, )
1119
+ res = super()._reduce(
1120
+ dpix=dpix,
1121
+ dpix2=None,
1122
+ relative_to_hash=None,
1123
+ fields=fields,
1124
+ normed=normed,
1125
+ shuffle=shuffle,
1126
+ extent=extent,
1127
+ forcedivide=forcedivide,
1128
+ ret_inst=False)
1129
+ (w_red, pos1_red, pos2_red, zbins_red, isinner_red, fields_red) = res
1130
+ if ret_inst:
1131
+ return SpinTracerCatalog(spin=self.spin, pos1=pos1_red, pos2=pos2_red,
1132
+ tracer_1=fields_red[0], tracer_2=fields_red[1],
1133
+ weight=w_red, zbins=zbins_red, isinner=isinner_red)
1134
+ return res
1135
+
1136
+ def multihash(self, dpixs, dpix_hash=None, normed=True, shuffle=0, w2field=True,
1137
+ extent=[None,None,None,None], forcedivide=1):
1138
+ r"""Builds spatialhash for a base catalog and its reductions.
1139
+
1140
+ Parameters
1141
+ ----------
1142
+ dpixs: list
1143
+ The pixel sizes on which the hierarchy of reduced catalogs is constructed.
1144
+ dpix_hash: float, optional
1145
+ The size of the pixels used for the spatial hash of the hierarchy of catalogs. Defaults
1146
+ to ``None``. If set to ``None`` uses the largest value of ``dpixs``.
1147
+ normed: bool, optional
1148
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
1149
+ shuffle: int, optional
1150
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
1151
+ extent: list, optional
1152
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
1153
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
1154
+ fully containing the discrete field tracers.
1155
+ forcedivide: int, optional
1156
+ Forces the number of cells in each dimensions to be divisible by some number.
1157
+ Defaults to ``1``.
1158
+ w2field: bool, optional
1159
+ Adds an additional field equivalent to the squared weight of the tracers to the reduced
1160
+ catalog. Defaaullts to ``True``.
1161
+
1162
+ Returns
1163
+ -------
1164
+ res: tuple
1165
+ Contains the output of the ```Catalog._multihash method```
1166
+ """
1167
+ if not w2field:
1168
+ fields=(self.tracer_1, self.tracer_2,)
1169
+ else:
1170
+ fields=(self.tracer_1, self.tracer_2, self.weight**2,)
1171
+ res = super()._multihash(
1172
+ dpixs=dpixs,
1173
+ fields=fields,
1174
+ dpix_hash=dpix_hash,
1175
+ normed=normed,
1176
+ shuffle=shuffle,
1177
+ extent=extent,
1178
+ forcedivide=forcedivide)
1179
+ return res
1180
+
1181
+
1182
+ def frompatchind(self, index, rotsignflip=False):
1183
+
1184
+ prepare = super()._patchind_preparerot(index, rotsignflip=rotsignflip)
1185
+ inds_extpatch, patch_isinner, rotangle, ra_rot, dec_rot, rotangle_polars = prepare
1186
+ spintracer_rot = (self.tracer_1[inds_extpatch] + 1j*self.tracer_2[inds_extpatch])*rotangle_polars
1187
+
1188
+ patchcat = SpinTracerCatalog(
1189
+ spin=self.spin,
1190
+ pos1=ra_rot*60.,
1191
+ pos2=dec_rot*60.,
1192
+ tracer_1=spintracer_rot.real,
1193
+ tracer_2=spintracer_rot.imag,
1194
+ weight=self.weight[inds_extpatch],
1195
+ zbins=self.zbins[inds_extpatch],
1196
+ isinner=patch_isinner,
1197
+ units_pos1='arcmin',
1198
+ units_pos2='arcmin',
1199
+ geometry='flat2d',
1200
+ mask=None)
1201
+
1202
+ return patchcat