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/catalog.py ADDED
@@ -0,0 +1,1216 @@
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']['patchextend_deg'] = jointcat.patchinds['info']['patchextend_deg']
286
+ self.patchinds['info']['nside_hash'] = jointcat.patchinds['info']['nside_hash']
287
+ self.patchinds['info']['method'] = jointcat.patchinds['info']['method']
288
+ self.patchinds['info']['kmeanshp_maxiter'] = jointcat.patchinds['info']['kmeanshp_maxiter']
289
+ self.patchinds['info']['kmeanshp_tol'] = jointcat.patchinds['info']['kmeanshp_tol']
290
+ self.patchinds['info']['kmeanshp_randomstate'] = jointcat.patchinds['info']['kmeanshp_randomstate']
291
+ self.patchinds['info']['healpix_nside'] = jointcat.patchinds['info']['healpix_nside']
292
+ self.patchinds['info']['patchcenters'] = jointcat.patchinds['info']['patchcenters']
293
+ self.patchinds['info']['patchareas'] = jointcat.patchinds['info']['patchareas']
294
+ self.patchinds['info']['patch_ngalsinner'] = np.zeros(jointcat.npatches)
295
+ self.patchinds['info']['patch_ngalsouter'] = np.zeros(jointcat.npatches)
296
+ self.patchinds['patches'] = {}
297
+ for elp in range(jointcat.npatches):
298
+ _inds = jointcat.patchinds['patches'][elp]
299
+ seli = (_inds['inner']>=cumngals[0])*(_inds['inner']<cumngals[1])
300
+ selo = (_inds['outer']>=cumngals[0])*(_inds['outer']<cumngals[1])
301
+ self.patchinds['info']['patch_ngalsinner'][elp] = np.sum(seli)
302
+ self.patchinds['info']['patch_ngalsouter'][elp] = np.sum(selo)
303
+ self.patchinds['patches'][elp] = {}
304
+ self.patchinds['patches'][elp]['inner'] = _inds['inner'][seli]
305
+ self.patchinds['patches'][elp]['outer'] = _inds['outer'][selo]
306
+ for elcat, cat in enumerate(other_cats):
307
+ cat.patchinds = {}
308
+ cat.patchinds['info'] = {}
309
+ cat.patchinds['info']['patchextend_deg'] = jointcat.patchinds['info']['patchextend_deg']
310
+ cat.patchinds['info']['nside_hash'] = jointcat.patchinds['info']['nside_hash']
311
+ cat.patchinds['info']['method'] = jointcat.patchinds['info']['method']
312
+ cat.patchinds['info']['kmeanshp_maxiter'] = jointcat.patchinds['info']['kmeanshp_maxiter']
313
+ cat.patchinds['info']['kmeanshp_tol'] = jointcat.patchinds['info']['kmeanshp_tol']
314
+ cat.patchinds['info']['kmeanshp_randomstate'] = jointcat.patchinds['info']['kmeanshp_randomstate']
315
+ cat.patchinds['info']['healpix_nside'] = jointcat.patchinds['info']['healpix_nside']
316
+ cat.patchinds['info']['patchcenters'] = jointcat.patchinds['info']['patchcenters']
317
+ cat.patchinds['info']['patchareas'] = jointcat.patchinds['info']['patchareas']
318
+ cat.patchinds['info']['patch_ngalsinner'] = np.zeros(jointcat.npatches)
319
+ cat.patchinds['info']['patch_ngalsouter'] = np.zeros(jointcat.npatches)
320
+ cat.patchinds['patches'] = {}
321
+ for elp in range(jointcat.npatches):
322
+ _inds = jointcat.patchinds['patches'][elp]
323
+ seli = (_inds['inner']>=cumngals[elcat+1])*(_inds['inner']<cumngals[elcat+2])
324
+ selo = (_inds['outer']>=cumngals[elcat+1])*(_inds['outer']<cumngals[elcat+2])
325
+ cat.patchinds['info']['patch_ngalsinner'][elp] = np.sum(seli)
326
+ cat.patchinds['info']['patch_ngalsouter'][elp] = np.sum(selo)
327
+ cat.patchinds['patches'][elp] = {}
328
+ cat.patchinds['patches'][elp]['inner'] = _inds['inner'][seli]-cumngals[elcat+1]
329
+ cat.patchinds['patches'][elp]['outer'] = _inds['outer'][selo]-cumngals[elcat+1]
330
+
331
+ # Finalize setting attributes for all instances
332
+ self.npatches = npatches
333
+ for cat in other_cats:
334
+ cat.npatches = npatches
335
+
336
+ def _patchind_preparerot(self, index, rotsignflip=False):
337
+
338
+ assert(self.patchinds is not None)
339
+ assert(self.geometry in ['spherical'])
340
+
341
+ return frompatchindices_preparerot(index, self.patchinds, self.pos1, self.pos2, rotsignflip)
342
+
343
+ # Reduces catalog to smaller catalog where positions & quantities are
344
+ # averaged over regular grid
345
+ def _reduce(self, fields, dpix, dpix2=None, relative_to_hash=None, normed=True, shuffle=0,
346
+ extent=[None,None,None,None], forcedivide=1,
347
+ ret_inst=False):
348
+ r"""Paints a catalog onto a grid with equal-area cells
349
+
350
+ Parameters
351
+ ----------
352
+ fields: list
353
+ The fields to be painted to the grid. Each field is given as a 1D array of float.
354
+ dpix: float
355
+ The sidelength of a grid cell.
356
+ dpix2: float, optional
357
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
358
+ If set to ``None`` the pixels are assumed to be squares.
359
+ relative_to_hash: int, optional
360
+ Forces the cell size to be an integer multiple of the cell size of the spatial hash.
361
+ Defaults to ``None``. If set to ``None`` the pixelsize is unrelated to the cell
362
+ size of the spatial hash.
363
+ normed: bool, optional
364
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
365
+ shuffle: int, optional
366
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
367
+ extent: list, optional
368
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
369
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
370
+ fully containing the discrete field tracers.
371
+ forcedivide: int, optional
372
+ Forces the number of cells in each dimensions to be divisible by some number.
373
+ Defaults to ``1``.
374
+ ret_inst: bool, optional
375
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
376
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
377
+ """
378
+
379
+ # Initialize grid
380
+ if relative_to_hash is None:
381
+ if dpix2 is None:
382
+ dpix2 = dpix
383
+ start1, start2, n1, n2 = self._gengridprops(dpix, dpix2, forcedivide, extent)
384
+ else:
385
+ assert(self.hasspatialhash)
386
+ assert(isinstance(relative_to_hash,np.int32))
387
+ start1 = self.pix1_start
388
+ start2 = self.pix2_start
389
+ dpix = self.pix1_d/np.float64(relative_to_hash)
390
+ dpix2 = self.pix2_d/np.float64(relative_to_hash)
391
+ n1 = self.pix1_n*relative_to_hash
392
+ n2 = self.pix2_n*relative_to_hash
393
+
394
+ # Prepare arguments
395
+ zbinarr = self.zbins.astype(np.int32)
396
+ nbinsz = len(np.unique(zbinarr))
397
+ ncompfields = []
398
+ scalarquants = []
399
+ nfields = 0
400
+ for field in fields:
401
+ if type(field[0].item()) is float:
402
+ scalarquants.append(field)
403
+ nfields += 1
404
+ ncompfields.append(1)
405
+ if type(field[0].item()) is complex:
406
+ scalarquants.append(field.real)
407
+ scalarquants.append(field.imag)
408
+ nfields += 2
409
+ ncompfields.append(2)
410
+ scalarquants = np.asarray(scalarquants)
411
+
412
+ # Compute reduction (individually for each zbin)
413
+ assert(shuffle in [True, False, 0, 1, 2, 3, 4])
414
+ isinner_red = np.zeros(self.ngal, dtype=np.float64)
415
+ w_red = np.zeros(self.ngal, dtype=np.float64)
416
+ pos1_red = np.zeros(self.ngal, dtype=np.float64)
417
+ pos2_red = np.zeros(self.ngal, dtype=np.float64)
418
+ zbins_red = np.zeros(self.ngal, dtype=np.int32)
419
+ scalarquants_red = np.zeros((nfields, self.ngal), dtype=np.float64)
420
+ ind_start = 0
421
+ for elz in range(nbinsz):
422
+ sel_z = zbinarr==elz
423
+ ngal_z = np.sum(sel_z)
424
+ ngal_red_z = 0
425
+ red_shape = (len(fields), ngal_z)
426
+ isinner_red_z = np.zeros(ngal_z, dtype=np.float64)
427
+ w_red_z = np.zeros(ngal_z, dtype=np.float64)
428
+ pos1_red_z = np.zeros(ngal_z, dtype=np.float64)
429
+ pos2_red_z = np.zeros(ngal_z, dtype=np.float64)
430
+ scalarquants_red_z = np.zeros(nfields*ngal_z, dtype=np.float64)
431
+ self.clib.reducecat(self.isinner[sel_z].astype(np.float64),
432
+ self.weight[sel_z].astype(np.float64),
433
+ self.pos1[sel_z].astype(np.float64),
434
+ self.pos2[sel_z].astype(np.float64),
435
+ scalarquants[:,sel_z].flatten().astype(np.float64),
436
+ ngal_z, nfields, np.int32(normed),
437
+ dpix, dpix2, start1, start2, n1, n2, np.int32(shuffle),
438
+ isinner_red_z, w_red_z, pos1_red_z, pos2_red_z, scalarquants_red_z, ngal_red_z)
439
+ isinner_red[ind_start:ind_start+ngal_z] = isinner_red_z
440
+ w_red[ind_start:ind_start+ngal_z] = w_red_z
441
+ pos1_red[ind_start:ind_start+ngal_z] = pos1_red_z
442
+ pos2_red[ind_start:ind_start+ngal_z] = pos2_red_z
443
+ zbins_red[ind_start:ind_start+ngal_z] = elz*np.ones(ngal_z, dtype=np.int32)
444
+ scalarquants_red[:,ind_start:ind_start+ngal_z] = scalarquants_red_z.reshape((nfields, ngal_z))
445
+ ind_start += ngal_z
446
+
447
+ # Accumulate reduced atalog
448
+ sel_nonzero = w_red>0
449
+ isinner_red = isinner_red[sel_nonzero]
450
+ w_red = w_red[sel_nonzero]
451
+ pos1_red = pos1_red[sel_nonzero]
452
+ pos2_red = pos2_red[sel_nonzero]
453
+ zbins_red = zbins_red[sel_nonzero]
454
+ scalarquants_red = scalarquants_red[:,sel_nonzero]
455
+ fields_red = []
456
+ tmpcomp = 0
457
+ for elf in range(len(fields)):
458
+ if ncompfields[elf]==1:
459
+ fields_red.append(scalarquants_red[tmpcomp])
460
+ if ncompfields[elf]==2:
461
+ fields_red.append(scalarquants_red[tmpcomp]+1J*scalarquants_red[tmpcomp+1])
462
+ tmpcomp += ncompfields[elf]
463
+ #isinner_red[isinner_red<0.5] = 0
464
+ #isinner_red[isinner_red>=0.5] = 1
465
+ if ret_inst:
466
+ return Catalog(pos1=pos1_red, pos2=pos2_red, weight=w_red, zbins=zbins_red,
467
+ isinner=isinner_red.astype(np.float64)), fields_red
468
+
469
+ return w_red, pos1_red, pos2_red, zbins_red, isinner_red, fields_red
470
+
471
+ def _multihash(self, dpixs, fields, dpix_hash=None, normed=True, shuffle=0,
472
+ extent=[None,None,None,None], forcedivide=1):
473
+ r"""Builds spatialhash for a base catalog and its reductions.
474
+
475
+ Parameters
476
+ ----------
477
+ dpixs: list
478
+ The pixel sizes on which the hierarchy of reduced catalogs is constructed.
479
+ fields: list
480
+ The fields for which the multihash is constructed. Each field is given as a 1D array of float.
481
+ dpix_hash: float, optional
482
+ The size of the pixels used for the spatial hash of the hierarchy of catalogs. Defaults
483
+ to ``None``. If set to ``None`` uses the largest value of ``dpixs``.
484
+ normed: bool, optional
485
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
486
+ shuffle: int, optional
487
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
488
+ extent: list, optional
489
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
490
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
491
+ fully containing the discrete field tracers.
492
+ forcedivide: int, optional
493
+ Forces the number of cells in each dimensions to be divisible by some number.
494
+ Defaults to ``1``.
495
+
496
+ Returns
497
+ -------
498
+ ngals: list
499
+ Contains the number of galaxies for each of the catalogs in the hierarchy.
500
+ pos1s: list
501
+ Contains the :math:`x`-positions for each of the catalogs in the hierarchy.
502
+ pos2s: list
503
+ Contains the :math:`y`-positions for each of the catalogs in the hierarchy.
504
+ weights: list
505
+ Contains the tracer weights for each of the catalogs in the hierarchy.
506
+ zbins: list
507
+ Contains the tomographic redshift bins for each of the catalogs in the hierarchy.
508
+ isinners: list
509
+ Contains the flag on wheter a tracer is within the interior part of the footprint
510
+ for each of the catalogs in the hierarchy.
511
+ allfields: list
512
+ Contains the tracer fields for each of the catalogs in the hierarchy.
513
+ index_matchers: list
514
+ Contains the ``index_matchers`` arrays for each of the catalogs in the hierarchy.
515
+ See the ```index_matcher`` attribute for more information.
516
+ pixs_galind_bounds: list
517
+ Contains the ``pixs_galind_bounds`` arrays for each of the catalogs in the hierarchy.
518
+ See the ```pixs_galind_bounds`` attribute for more information.
519
+ pix_gals: list
520
+ Contains the ``pix_gals`` arrays for each of the catalogs in the hierarchy.
521
+ See the ```pix_gals`` attribute for more information.
522
+ dpixs1_true: list
523
+ Contains final values of the pixel sidelength along the :math:`x`-direction for each
524
+ of the catalogs in the hierarchy.
525
+ dpixs2_true: list
526
+ Contains final values of the pixel sidelength along the :math:`y`-direction for each
527
+ of the catalogs in the hierarchy.
528
+ """
529
+
530
+ dpixs = sorted(dpixs)
531
+ if dpix_hash is None:
532
+ dpix_hash = dpixs[-1]
533
+ if extent[0] is None:
534
+ extent = [self.min1-dpix_hash, self.max1+dpix_hash, self.min2-dpix_hash, self.max2+dpix_hash]
535
+
536
+
537
+ # Initialize spatial hash for discrete catalog
538
+ self.build_spatialhash(dpix=dpix_hash, extent=extent)
539
+ ngals = [self.ngal]
540
+ isinners = [self.isinner]
541
+ pos1s = [self.pos1]
542
+ pos2s = [self.pos2]
543
+ weights = [self.weight]
544
+ zbins = [self.zbins]
545
+ allfields = [fields]
546
+ if not normed:
547
+ allfields[0] *= self.weight
548
+ index_matchers = [self.index_matcher]
549
+ pixs_galind_bounds = [self.pixs_galind_bounds]
550
+ pix_gals = [self.pix_gals]
551
+
552
+ # Build spatial hashes for reduced catalogs
553
+ fac_pix1 = self.pix1_d/dpix_hash
554
+ fac_pix2 = self.pix2_d/dpix_hash
555
+ dpixs1_true = np.zeros_like(np.asarray(dpixs))
556
+ dpixs2_true = np.zeros_like(np.asarray(dpixs))
557
+ #print(len(fields),fields)
558
+ for elreso in range(len(dpixs)):
559
+ #print("Doing reso %i"%elreso)
560
+ dpixs1_true[elreso]=fac_pix1*dpixs[elreso]
561
+ dpixs2_true[elreso]=fac_pix2*dpixs[elreso]
562
+ #print(dpixs[elreso], dpixs1_true[elreso], dpixs2_true[elreso], len(self.pos1))
563
+ nextcat, fields_red = self._reduce(fields=fields,
564
+ dpix=dpixs1_true[elreso],
565
+ dpix2=dpixs2_true[elreso],
566
+ relative_to_hash=np.int32(2**(len(dpixs)-elreso-1)),
567
+ #relative_to_hash=None,
568
+ normed=normed,
569
+ shuffle=shuffle,
570
+ extent=extent,
571
+ forcedivide=forcedivide,
572
+ ret_inst=True)
573
+ nextcat.build_spatialhash(dpix=dpix_hash, extent=extent)
574
+ ngals.append(nextcat.ngal)
575
+ isinners.append(nextcat.isinner)
576
+ pos1s.append(nextcat.pos1)
577
+ pos2s.append(nextcat.pos2)
578
+ weights.append(nextcat.weight)
579
+ zbins.append(nextcat.zbins)
580
+ allfields.append(fields_red)
581
+ index_matchers.append(nextcat.index_matcher)
582
+ pixs_galind_bounds.append(nextcat.pixs_galind_bounds)
583
+ pix_gals.append(nextcat.pix_gals)
584
+
585
+ return ngals, pos1s, pos2s, weights, zbins, isinners, allfields, index_matchers, pixs_galind_bounds, pix_gals, dpixs1_true, dpixs2_true
586
+
587
+ def _jointextent(self, others, extend=0):
588
+ r"""Draws largest possible rectangle over set of catalogs.
589
+
590
+ Parameters
591
+ ----------
592
+ others: list
593
+ Contains ``Catalog`` instances over which the joint extent will
594
+ be drawn
595
+ extend: float, optional
596
+ Include an additional boundary layer around the joint extent
597
+ of the catalogs. Defaults to ``0`` (no extension).
598
+
599
+ Returns
600
+ -------
601
+ xlo: float
602
+ The lower ``x``-boundary of the joint extent.
603
+ xhi: float
604
+ The upper ``x``-boundary of the joint extent.
605
+ ylo: float
606
+ The lower ``y``-boundary of the joint extent.
607
+ yhi: float
608
+ The upper ``y``-boundary of the joint extent.
609
+
610
+ """
611
+ for other in others:
612
+ assert(isinstance(other, Catalog))
613
+
614
+ xlo = self.min1
615
+ xhi = self.max1
616
+ ylo = self.min2
617
+ yhi = self.max2
618
+ for other in others:
619
+ xlo = min(xlo, other.min1)
620
+ xhi = max(xhi, other.max1)
621
+ ylo = min(ylo, other.min2)
622
+ yhi = max(yhi, other.max2)
623
+
624
+ return (xlo-extend, xhi+extend, ylo-extend, yhi+extend)
625
+
626
+
627
+ def create_mask(self, method="Basic", pixsize=1., apply=False, extend=0.):
628
+
629
+ assert(method in ["Basic", "Density", "Random"])
630
+
631
+ if method=="Basic":
632
+ npix_1 = int(np.ceil((self.max1-self.min1)/pixsize))
633
+ npix_2 = int(np.ceil((self.max2-self.min2)/pixsize))
634
+ self.mask = FlatDataGrid_2D(np.zeros((npix_2,npix_1), dtype=np.float64),
635
+ self.min1, self.min2, pixsize, pixsize)
636
+ if method=="Density":
637
+ start1, start2, n1, n2 = self._gengridprops(pixsize, pixsize)
638
+ reduced = self.togrid(dpix=pixsize,method="NGP",fields=[], tomo=False)
639
+ mask = (reduced[0].reshape((n2,n1))==0).astype(np.float64)
640
+ self.mask = FlatDataGrid_2D(mask, start1, start2, pixsize, pixsize)
641
+
642
+ # Add a masked buffer region around enclosing rectangle
643
+ if extend>0.:
644
+ npix_ext = int(np.ceil(extend/pixsize))
645
+ extstart1 = self.mask.start_1 - npix_ext*pixsize
646
+ extstart2 = self.mask.start_2 - npix_ext*pixsize
647
+ extmask = np.ones((self.mask.npix_2+2*npix_ext, self.mask.npix_1+2*npix_ext))
648
+ extmask[npix_ext:-npix_ext,npix_ext:-npix_ext] = self.mask.data
649
+ self.mask = FlatDataGrid_2D(extmask, extstart1, extstart2, pixsize, pixsize)
650
+
651
+ self. __checkmask()
652
+
653
+ self. __applymask(apply)
654
+
655
+ def __checkmask(self):
656
+ assert(self.mask.start_1 <= self.min1)
657
+ assert(self.mask.start_2 <= self.min2)
658
+ assert(self.mask.pix1_lbounds[-1] >= self.max1-self.mask.dpix_1)
659
+ assert(self.mask.pix2_lbounds[-1] >= self.max2-self.mask.dpix_2)
660
+
661
+ def __applymask(self, method):
662
+ assert(method in [False, True, "WeightsOnly"])
663
+
664
+
665
+
666
+ # Maps catalog to grid
667
+ def togrid(self, fields, dpix, normed=False, weighted=True, tomo=True,
668
+ extent=[None,None,None,None], method="CIC", forcedivide=1,
669
+ asgrid=None, nthreads=1, ret_inst=False):
670
+ r"""Paints a catalog of discrete tracers to a grid.
671
+
672
+ Parameters
673
+ ----------
674
+ fields: list
675
+ The fields to be painted to the grid. Each field is given as a 1D array of float.
676
+ dpix: float
677
+ The sidelength of a grid cell.
678
+ normed: bool, optional
679
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
680
+ weighted: bool, optional
681
+ Whether to apply the tracer weights of the catalog. Defaults to ``True``.
682
+ extent: list, optional
683
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
684
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
685
+ fully containing the discrete field tracers.
686
+ method: str, optional
687
+ The chosen mass assignment method applied to each of the fields. Currently supported methods
688
+ are ``NGP``, ``CIC`` and ``TSC`` assignment. Defaults to ``CIC``.
689
+ forcedivide: int, optional
690
+ Forces the number of cells in each dimensions to be divisible by some number.
691
+ Defaults to ``1``.
692
+ ret_inst: bool, optional
693
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
694
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
695
+ asgrid: bool, optional
696
+ Deprecated.
697
+ nthreads: int, optional
698
+ The number of openmp threads used for the reduction procedure. Defaults to ``1``.
699
+ ret_inst: bool, optional
700
+ Deprecated.
701
+
702
+ Returns
703
+ -------
704
+ projectedfields: list
705
+ A list of the 2D arrays containing the reduced fields
706
+ start1: float
707
+ The :math:`x`-position of the first columns' left edge
708
+ start2: float
709
+ The :math:`y`-position of the first rows' lower edge
710
+ dpix: float
711
+ The sidelength of each pixel in the grid. Note that this
712
+ value might slightly differ from the one provided in the parameters.
713
+ normed: bool
714
+ Same as the ``normed`` parameter
715
+ method: str
716
+ Same as the ``method`` parameter
717
+ """
718
+
719
+ if asgrid is not None:
720
+ raise NotImplementedError
721
+
722
+ # Choose index of method for c wrapper
723
+ assert(method in ["NGP", "CIC", "TSC"])
724
+ elmethod = self.assign_methods[method]
725
+ start1, start2, n1, n2 = self._gengridprops(dpix, dpix, forcedivide, extent)
726
+
727
+ # Prepare arguments
728
+ zbinarr = self.zbins.astype(np.int32)
729
+ if not tomo:
730
+ zbinarr = np.zeros_like(zbinarr)
731
+ nbinsz = len(np.unique(zbinarr))
732
+ nfields = len(fields)
733
+ if not weighted:
734
+ weightarr = np.ones(self.ngal, dtype=np.float64)
735
+ else:
736
+ weightarr = self.weight.astype(np.float64)
737
+ fieldarr = np.zeros(nfields*self.ngal, dtype=np.float64)
738
+ for _ in range(nfields):
739
+ fieldarr[_*self.ngal:(1+_)*self.ngal] = fields[_]
740
+
741
+ # Call wrapper and reshape output to (zbins, nfields, size_field)
742
+ proj_shape = (nbinsz, (nfields+1), n2, n1)
743
+ projectedfields = np.zeros((nbinsz*(nfields+1)*n2*n1), dtype=np.float64)
744
+ self.clib.assign_fields(self.pos1.astype(np.float64),
745
+ self.pos2.astype(np.float64),
746
+ zbinarr, weightarr, fieldarr,
747
+ nbinsz, nfields, self.ngal,
748
+ elmethod, start1, start2, dpix,
749
+ n1, n2, nthreads, projectedfields)
750
+ projectedfields = projectedfields.reshape(proj_shape)
751
+ if normed:
752
+ projectedfields[:,1:] = np.nan_to_num(projectedfields[:,1:]/projectedfields[:,0])
753
+
754
+ if not ret_inst:
755
+ return projectedfields, start1, start2, dpix, normed, method
756
+
757
+ return GriddedCatalog(projectedfields,
758
+ start1, start2, dpix, normed, method)
759
+
760
+ def gen_weightgrid2d(self, dpix,
761
+ extent=[None,None,None,None], method="CIC", forcedivide=1,
762
+ nthreads=1):
763
+
764
+ # Choose index of method for c wrapper
765
+ assert(method in ["NGP", "CIC", "TSC"])
766
+ elmethod = self.assign_methods[method]
767
+ start1, start2, n1, n2 = self._gengridprops(dpix, dpix, forcedivide, extent)
768
+
769
+ #void gen_weightgrid2d(
770
+ # double *pos1, double *pos2, int ngal, int method,
771
+ # double min1, double min2, int dpix, int n1, int n2,
772
+ # int nthreads, int *pixinds, double *pixweights){
773
+
774
+ self.ngal
775
+ nsubs = 2*elmethod+1
776
+ pixinds = np.zeros(nsubs*nsubs*self.ngal, dtype=np.int32)
777
+ pixweights = np.zeros(nsubs*nsubs*self.ngal, dtype=np.float64)
778
+ self.clib.gen_weightgrid2d(self.pos1.astype(np.float64),
779
+ self.pos2.astype(np.float64),
780
+ self.ngal, elmethod,
781
+ start1, start2, dpix, n1, n2,
782
+ nthreads, pixinds, pixweights)
783
+ return pixinds, pixweights
784
+
785
+
786
+
787
+ def build_spatialhash(self, dpix=1., extent=[None, None, None, None]):
788
+ r"""Adds a spatial hashing data structure to the catalog.
789
+
790
+ Parameters
791
+ ----------
792
+ dpix: float
793
+ The sidelength of each cell of the hash. Defaults to ``1``.
794
+ extent: list, optional
795
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
796
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
797
+ fully containing the discrete field tracers.
798
+
799
+ Note
800
+ ----
801
+ Calling this method (re-)allocates the ``index_matcher``, ``pixs_galind_bounds``, ``pix_gals``,
802
+ ``pix1_start``, ``pix2_start``, ``pix1_n``, ``pix2_n``, ``pix1_d`` and ``pix2_d``
803
+ attributes of the instance.
804
+ """
805
+
806
+ # Build extent
807
+ if extent[0] is None:
808
+ thismin1 = self.min1
809
+ else:
810
+ thismin1 = extent[0]
811
+ assert(thismin1 <= self.min1)
812
+ if extent[1] is None:
813
+ thismax1 = self.max1
814
+ else:
815
+ thismax1 = extent[1]
816
+ assert(thismax1 >= self.max1)
817
+ if extent[2] is None:
818
+ thismin2 = self.min2
819
+ else:
820
+ thismin2 = extent[2]
821
+ assert(thismin2 <= self.min2)
822
+ if extent[3] is None:
823
+ thismax2 = self.max2
824
+ else:
825
+ thismax2 = extent[3]
826
+ assert(thismax2 >= self.max2)
827
+
828
+ # Collect arguments
829
+ # Note that the C function assumes the mask to start at zero, that's why we shift
830
+ # the galaxy positions
831
+ self.pix1_start = thismin1 - dpix/1.
832
+ self.pix2_start = thismin2 - dpix/1.
833
+ stop1 = thismax1 + dpix/1.
834
+ stop2 = thismax2 + dpix/1.
835
+ self.pix1_n = int(np.ceil((stop1-self.pix1_start)/dpix))
836
+ self.pix2_n = int(np.ceil((stop2-self.pix2_start)/dpix))
837
+ npix = self.pix1_n * self.pix2_n
838
+ self.pix1_d = (stop1-self.pix1_start)/(self.pix1_n)
839
+ self.pix2_d = (stop2-self.pix2_start)/(self.pix2_n)
840
+
841
+ # Compute hashtable
842
+ result = np.zeros(2 * npix + 3 * self.ngal + 1).astype(np.int32)
843
+ self.clib.build_spatialhash(self.pos1, self.pos2, self.ngal,
844
+ self.pix1_d, self.pix2_d,
845
+ self.pix1_start, self.pix2_start,
846
+ self.pix1_n, self.pix2_n,
847
+ result)
848
+
849
+ # Allocate result
850
+ start_isoutside = 0
851
+ start_index_matcher = self.ngal
852
+ start_pixs_galind_bounds = self.ngal + npix
853
+ start_pixs_gals = self.ngal + npix + self.ngal + 1
854
+ start_ngalinpix = self.ngal + npix + self.ngal + 1 + self.ngal
855
+ self.index_matcher = result[start_index_matcher:start_pixs_galind_bounds]
856
+ self.pixs_galind_bounds = result[start_pixs_galind_bounds:start_pixs_gals]
857
+ self.pix_gals = result[start_pixs_gals:start_ngalinpix]
858
+ self.hasspatialhash = True
859
+
860
+
861
+ def _gengridprops(self, dpix, dpix2=None, forcedivide=1, extent=[None,None,None,None]):
862
+ r"""Gives some basic properties of grids created from the discrete tracers.
863
+
864
+ Parameters
865
+ ----------
866
+ dpix: float
867
+ The sidelength of a grid cell.
868
+ dpix2: float, optional
869
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
870
+ If set to ``None`` the pixels are assumed to be squares.
871
+ forcedivide: int, optional
872
+ Forces the number of cells in each dimensions to be divisible by some number.
873
+ Defaults to ``1``.
874
+ extent: list, optional
875
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
876
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
877
+ fully containing the discrete field tracers.
878
+
879
+ Returns
880
+ -------
881
+ start1: float
882
+ The :math:``x``-position of the first column.
883
+ start2: float
884
+ The :math:``y``-position of the first row.
885
+ n1: int
886
+ The number of pixels in the :math:``x``-position.
887
+ n2: int
888
+ The number of pixels in the :math:``y``-position.
889
+ """
890
+
891
+ # Define inner extent of the grid
892
+ fixedsize = False
893
+ if extent[0] is not None:
894
+ fixedsize = True
895
+ if extent[0] is None:
896
+ thismin1 = self.min1
897
+ else:
898
+ thismin1 = extent[0]
899
+ assert(thismin1 <= self.min1)
900
+ if extent[1] is None:
901
+ thismax1 = self.max1
902
+ else:
903
+ thismax1 = extent[1]
904
+ assert(thismax1 >= self.max1)
905
+ if extent[2] is None:
906
+ thismin2 = self.min2
907
+ else:
908
+ thismin2 = extent[2]
909
+ assert(thismin2 <= self.min2)
910
+ if extent[3] is None:
911
+ thismax2 = self.max2
912
+ else:
913
+ thismax2 = extent[3]
914
+ assert(thismax2 >= self.max2)
915
+
916
+ if dpix2 is None:
917
+ dpix2 = dpix
918
+
919
+ # Add buffer to grid and get associated pixelization
920
+ if not fixedsize:
921
+ start1 = thismin1 - 4*dpix
922
+ start2 = thismin2 - 4*dpix2
923
+ n1 = int(np.ceil((thismax1+4*dpix - start1)/dpix))
924
+ n2 = int(np.ceil((thismax2+4*dpix2 - start2)/dpix2))
925
+ n1 += (forcedivide - n1%forcedivide)%forcedivide
926
+ n2 += (forcedivide - n2%forcedivide)%forcedivide
927
+ else:
928
+ start1=extent[0]
929
+ start2=extent[2]
930
+ n1 = int((thismax1-thismin1)/dpix)
931
+ n2 = int((thismax2-thismin2)/dpix2)
932
+ assert(not n1%forcedivide)
933
+ assert(not n2%forcedivide)
934
+
935
+ return start1, start2, n1, n2
936
+
937
+ class ScalarTracerCatalog(Catalog):
938
+ r"""Class constructor.
939
+
940
+ Attributes
941
+ ----------
942
+ pos1: numpy.ndarray
943
+ The :math:`x`-positions of the tracer objects
944
+ pos2: numpy.ndarray
945
+ The :math:`y`-positions of the tracer objects
946
+ tracer: numpy.ndarray
947
+ The values of the scalar tracer field, i.e. galaxy weights or cosmic convergence.
948
+
949
+ Notes
950
+ -----
951
+ Inherits all other parameters and attributes from :class:`Catalog`.
952
+ Additional child-specific parameters can be passed via ``kwargs``.
953
+ """
954
+
955
+ def __init__(self, pos1, pos2, tracer, **kwargs):
956
+ super().__init__(pos1=pos1, pos2=pos2, **kwargs)
957
+ self.tracer = tracer
958
+ self.spin = 0
959
+
960
+ def reduce(self, dpix, dpix2=None, relative_to_hash=None, normed=True, shuffle=0,
961
+ extent=[None,None,None,None], forcedivide=1,
962
+ ret_inst=False):
963
+ r"""Paints the catalog onto a grid with equal-area cells
964
+
965
+ Parameters
966
+ ----------
967
+ dpix: float
968
+ The sidelength of a grid cell.
969
+ dpix2: float, optional
970
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
971
+ If set to ``None`` the pixels are assumed to be squares.
972
+ relative_to_hash: int, optional
973
+ Forces the cell size to be an integer multiple of the cell size of the spatial hash.
974
+ Defaults to ``None``. If set to ``None`` the pixelsize is unrelated to the cell
975
+ size of the spatial hash.
976
+ normed: bool, optional
977
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
978
+ shuffle: int, optional
979
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
980
+ extent: list, optional
981
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
982
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
983
+ fully containing the discrete field tracers.
984
+ forcedivide: int, optional
985
+ Forces the number of cells in each dimensions to be divisible by some number.
986
+ Defaults to ``1``.
987
+ ret_inst: bool, optional
988
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
989
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
990
+ """
991
+ res = super()._reduce(
992
+ dpix=dpix,
993
+ dpix2=None,
994
+ relative_to_hash=None,
995
+ fields=[self.tracer],
996
+ normed=normed,
997
+ shuffle=shuffle,
998
+ extent=extent,
999
+ forcedivide=forcedivide,
1000
+ ret_inst=False)
1001
+ (w_red, pos1_red, pos2_red, zbins_red, isinner_red, fields_red) = res
1002
+ if ret_inst:
1003
+ return ScalarTracerCatalog(self.spin, pos1_red, pos2_red,
1004
+ fields_red[0],
1005
+ weight=w_red, zbins=zbins_red, isinner=isinner_red)
1006
+ return res
1007
+
1008
+ def multihash(self, dpixs, dpix_hash=None, normed=True, shuffle=0,
1009
+ extent=[None,None,None,None], forcedivide=1):
1010
+ r"""Builds spatialhash for a base catalog and its reductions.
1011
+
1012
+ Parameters
1013
+ ----------
1014
+ dpixs: list
1015
+ The pixel sizes on which the hierarchy of reduced catalogs is constructed.
1016
+ dpix_hash: float, optional
1017
+ The size of the pixels used for the spatial hash of the hierarchy of catalogs. Defaults
1018
+ to ``None``. If set to ``None`` uses the largest value of ``dpixs``.
1019
+ normed: bool, optional
1020
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
1021
+ shuffle: int, optional
1022
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
1023
+ extent: list, optional
1024
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
1025
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
1026
+ fully containing the discrete field tracers.
1027
+ forcedivide: int, optional
1028
+ Forces the number of cells in each dimensions to be divisible by some number.
1029
+ Defaults to ``1``.
1030
+
1031
+ Returns
1032
+ -------
1033
+ res: tuple
1034
+ Contains the output of the ```Catalog._multihash method```
1035
+ """
1036
+ res = super()._multihash(
1037
+ dpixs=dpixs.astype(np.float64),
1038
+ fields=[self.tracer],
1039
+ dpix_hash=dpix_hash,
1040
+ normed=normed,
1041
+ shuffle=shuffle,
1042
+ extent=extent,
1043
+ forcedivide=forcedivide)
1044
+ return res
1045
+
1046
+
1047
+ def frompatchind(self, index):
1048
+
1049
+ prepare = super()._patchind_preparerot(index, rotsignflip=False)
1050
+ inds_extpatch, patch_isinner, rotangle, ra_rot, dec_rot, rotangle_polars = prepare
1051
+
1052
+ patchcat = ScalarTracerCatalog(
1053
+ pos1=ra_rot*60.,
1054
+ pos2=dec_rot*60.,
1055
+ tracer=self.tracer[inds_extpatch],
1056
+ weight=self.weight[inds_extpatch],
1057
+ zbins=self.zbins[inds_extpatch],
1058
+ isinner=patch_isinner,
1059
+ units_pos1='arcmin',
1060
+ units_pos2='arcmin',
1061
+ geometry='flat2d',
1062
+ mask=None,
1063
+ zbins_mean=None,
1064
+ zbins_std=None)
1065
+
1066
+ return patchcat
1067
+
1068
+ class SpinTracerCatalog(Catalog):
1069
+ r"""Class constructor.
1070
+
1071
+ Attributes
1072
+ ----------
1073
+ pos1: numpy.ndarray
1074
+ The :math:`x`-positions of the tracer objects
1075
+ pos2: numpy.ndarray
1076
+ The :math:`y`-positions of the tracer objects
1077
+ tracer_1: numpy.ndarray
1078
+ The values of the real part of the tracer field, i.e. galaxy ellipticities.
1079
+ tracer_2: numpy.ndarray
1080
+ The values of the imaginary part of the tracer field, i.e. galaxy ellipticities.
1081
+
1082
+ Notes
1083
+ -----
1084
+ Inherits all other parameters and attributes from :class:`Catalog`.
1085
+ Additional child-specific parameters can be passed via ``kwargs``.
1086
+ """
1087
+
1088
+ def __init__(self, spin, pos1, pos2, tracer_1, tracer_2, **kwargs):
1089
+ super().__init__(pos1=pos1, pos2=pos2, **kwargs)
1090
+ self.tracer_1 = tracer_1.astype(np.float64)
1091
+ self.tracer_2 = tracer_2.astype(np.float64)
1092
+ self.spin = int(spin)
1093
+
1094
+ def reduce(self, dpix, dpix2=None, relative_to_hash=None, normed=True, shuffle=0,
1095
+ extent=[None,None,None,None], forcedivide=1, w2field=True,
1096
+ ret_inst=False):
1097
+ r"""Paints the catalog onto a grid with equal-area cells
1098
+
1099
+ Parameters
1100
+ ----------
1101
+ dpix: float
1102
+ The sidelength of a grid cell.
1103
+ dpix2: float, optional
1104
+ The sidelength of a grid cell in :math:`y`-direction. Defaults to ``None``.
1105
+ If set to ``None`` the pixels are assumed to be squares.
1106
+ relative_to_hash: int, optional
1107
+ Forces the cell size to be an integer multiple of the cell size of the spatial hash.
1108
+ Defaults to ``None``. If set to ``None`` the pixelsize is unrelated to the cell
1109
+ size of the spatial hash.
1110
+ normed: bool, optional
1111
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
1112
+ shuffle: int, optional
1113
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
1114
+ extent: list, optional
1115
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
1116
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
1117
+ fully containing the discrete field tracers.
1118
+ forcedivide: int, optional
1119
+ Forces the number of cells in each dimensions to be divisible by some number.
1120
+ Defaults to ``1``.
1121
+ w2field: bool, optional
1122
+ Adds an additional field equivalent to the squared weight of the tracers to the reduced
1123
+ catalog. Defaaullts to ``True``.
1124
+ ret_inst: bool, optional
1125
+ Decides on wheter to return the output as a list of arrays containing the reduced catalog or
1126
+ on returning a new ``Catalog`` instance. Defaults to ``False``.
1127
+ """
1128
+
1129
+ if not w2field:
1130
+ fields=(self.tracer_1, self.tracer_2,)
1131
+ else:
1132
+ fields=(self.tracer_1, self.tracer_2, self.weight**2, )
1133
+ res = super()._reduce(
1134
+ dpix=dpix,
1135
+ dpix2=None,
1136
+ relative_to_hash=None,
1137
+ fields=fields,
1138
+ normed=normed,
1139
+ shuffle=shuffle,
1140
+ extent=extent,
1141
+ forcedivide=forcedivide,
1142
+ ret_inst=False)
1143
+ (w_red, pos1_red, pos2_red, zbins_red, isinner_red, fields_red) = res
1144
+ if ret_inst:
1145
+ return SpinTracerCatalog(spin=self.spin, pos1=pos1_red, pos2=pos2_red,
1146
+ tracer_1=fields_red[0], tracer_2=fields_red[1],
1147
+ weight=w_red, zbins=zbins_red, isinner=isinner_red)
1148
+ return res
1149
+
1150
+ def multihash(self, dpixs, dpix_hash=None, normed=True, shuffle=0, w2field=True,
1151
+ extent=[None,None,None,None], forcedivide=1):
1152
+ r"""Builds spatialhash for a base catalog and its reductions.
1153
+
1154
+ Parameters
1155
+ ----------
1156
+ dpixs: list
1157
+ The pixel sizes on which the hierarchy of reduced catalogs is constructed.
1158
+ dpix_hash: float, optional
1159
+ The size of the pixels used for the spatial hash of the hierarchy of catalogs. Defaults
1160
+ to ``None``. If set to ``None`` uses the largest value of ``dpixs``.
1161
+ normed: bool, optional
1162
+ Decide on whether to average or to sum the field over pixels. Defaults to ``True``.
1163
+ shuffle: int, optional
1164
+ Choose a definition on how to set the central point of each pixel. Defaults to zero.
1165
+ extent: list, optional
1166
+ Sets custom boundaries ``[xmin, xmax, ymin, ymax]`` for the grid. Each element defaults
1167
+ to ``None``. Each element equal to ``None`` sets the grid boundary as the smallest value
1168
+ fully containing the discrete field tracers.
1169
+ forcedivide: int, optional
1170
+ Forces the number of cells in each dimensions to be divisible by some number.
1171
+ Defaults to ``1``.
1172
+ w2field: bool, optional
1173
+ Adds an additional field equivalent to the squared weight of the tracers to the reduced
1174
+ catalog. Defaaullts to ``True``.
1175
+
1176
+ Returns
1177
+ -------
1178
+ res: tuple
1179
+ Contains the output of the ```Catalog._multihash method```
1180
+ """
1181
+ if not w2field:
1182
+ fields=(self.tracer_1, self.tracer_2,)
1183
+ else:
1184
+ fields=(self.tracer_1, self.tracer_2, self.weight**2,)
1185
+ res = super()._multihash(
1186
+ dpixs=dpixs,
1187
+ fields=fields,
1188
+ dpix_hash=dpix_hash,
1189
+ normed=normed,
1190
+ shuffle=shuffle,
1191
+ extent=extent,
1192
+ forcedivide=forcedivide)
1193
+ return res
1194
+
1195
+
1196
+ def frompatchind(self, index, rotsignflip=False):
1197
+
1198
+ prepare = super()._patchind_preparerot(index, rotsignflip=rotsignflip)
1199
+ inds_extpatch, patch_isinner, rotangle, ra_rot, dec_rot, rotangle_polars = prepare
1200
+ spintracer_rot = (self.tracer_1[inds_extpatch] + 1j*self.tracer_2[inds_extpatch])*rotangle_polars
1201
+
1202
+ patchcat = SpinTracerCatalog(
1203
+ spin=self.spin,
1204
+ pos1=ra_rot*60.,
1205
+ pos2=dec_rot*60.,
1206
+ tracer_1=spintracer_rot.real,
1207
+ tracer_2=spintracer_rot.imag,
1208
+ weight=self.weight[inds_extpatch],
1209
+ zbins=self.zbins[inds_extpatch],
1210
+ isinner=patch_isinner,
1211
+ units_pos1='arcmin',
1212
+ units_pos2='arcmin',
1213
+ geometry='flat2d',
1214
+ mask=None)
1215
+
1216
+ return patchcat