sacc 1.0__py3-none-any.whl → 2.0__py3-none-any.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.
@@ -0,0 +1,332 @@
1
+ from .base import BaseTracer, MULTIPLE_OBJECTS_PER_TABLE
2
+ from astropy.table import Table
3
+
4
+ class BinZTracer(BaseTracer, type_name="bin_z"): # type: ignore
5
+ """A tracer for a single redshift bin. The tracer shall
6
+ be used for binned data where we want a desired quantity
7
+ per interval of redshift, such that we only need the data
8
+ for a given interval instead of at individual redshifts."""
9
+
10
+ storage_type = MULTIPLE_OBJECTS_PER_TABLE
11
+
12
+ def __init__(self, name: str, lower: float, upper: float, **kwargs):
13
+ """
14
+ Create a tracer corresponding to a single redshift bin.
15
+
16
+ :param name: The name of the tracer
17
+ :param lower: The lower bound of the redshift bin
18
+ :param upper: The upper bound of the redshift bin
19
+ """
20
+ super().__init__(name, **kwargs)
21
+ self.lower = lower
22
+ self.upper = upper
23
+
24
+ def __eq__(self, other) -> bool:
25
+ """Test for equality. If :python:`other` is not a
26
+ :python:`BinZTracer`, then it is not equal to :python:`self`.
27
+ Otherwise, they are equal if names, and the z-range of the bins,
28
+ are equal."""
29
+ if not isinstance(other, BinZTracer):
30
+ return False
31
+ return (
32
+ self.name == other.name
33
+ and self.lower == other.lower
34
+ and self.upper == other.upper
35
+ )
36
+
37
+ @classmethod
38
+ def to_table(cls, instance_list):
39
+ """Convert a list of BinZTracers to a single astropy table
40
+
41
+ This is used when saving data to a file.
42
+ One table is generated with the information for all the tracers.
43
+
44
+ :param instance_list: List of tracer instances
45
+ :return: List with a single astropy table
46
+ """
47
+
48
+ names = ["name", "quantity", "lower", "upper"]
49
+
50
+ cols = [
51
+ [obj.name for obj in instance_list],
52
+ [obj.quantity for obj in instance_list],
53
+ [obj.lower for obj in instance_list],
54
+ [obj.upper for obj in instance_list],
55
+ ]
56
+
57
+ table = Table(data=cols, names=names)
58
+ table.meta["SACCTYPE"] = "tracer"
59
+ table.meta["SACCCLSS"] = cls.type_name
60
+ table.meta["EXTNAME"] = f"tracer:{cls.type_name}"
61
+ return table
62
+
63
+ @classmethod
64
+ def from_table(cls, table):
65
+ """Convert an astropy table into a dictionary of tracers
66
+
67
+ This is used when loading data from a file.
68
+ One tracer object is created for each "row" in each table.
69
+
70
+ :param table_list: List of astropy tables
71
+ :return: Dictionary of tracers
72
+ """
73
+ tracers = {}
74
+
75
+ for row in table:
76
+ name = row["name"]
77
+ quantity = row["quantity"]
78
+ lower = row["lower"]
79
+ upper = row["upper"]
80
+ tracers[name] = cls(name, quantity=quantity, lower=lower, upper=upper)
81
+ return tracers
82
+
83
+ class BinLogMTracer(BaseTracer, type_name="bin_logM"): # type: ignore
84
+ """A tracer for a single log-mass bin. The tracer shall
85
+ be used for binned data where we want a desired quantity
86
+ per interval of log(mass), such that we only need the data
87
+ for a given interval instead of at individual masses."""
88
+ storage_type = MULTIPLE_OBJECTS_PER_TABLE
89
+
90
+ def __init__(self, name: str, lower: float, upper: float, **kwargs):
91
+ """
92
+ Create a tracer corresponding to a single log-mass bin.
93
+
94
+ :param name: The name of the tracer
95
+ :param lower: The lower bound of the log-mass bin
96
+ :param upper: The upper bound of the log-mass bin
97
+ """
98
+ super().__init__(name, **kwargs)
99
+ self.lower = lower
100
+ self.upper = upper
101
+
102
+ def __eq__(self, other) -> bool:
103
+ """Test for equality. If :python:`other` is not a
104
+ :python:`BinLogMTracer`, then it is not equal to :python:`self`.
105
+ Otherwise, they are equal if names, and the z-range of the bins,
106
+ are equal."""
107
+ if not isinstance(other, BinLogMTracer):
108
+ return False
109
+ return (
110
+ self.name == other.name
111
+ and self.lower == other.lower
112
+ and self.upper == other.upper
113
+ )
114
+
115
+ @classmethod
116
+ def to_table(cls, instance_list):
117
+ """Convert a list of BinLogMTracers to a single astropy table
118
+
119
+ This is used when saving data to a file.
120
+ One table is generated with the information for all the tracers.
121
+
122
+ :param instance_list: List of tracer instances
123
+ :return: List with a single astropy table
124
+ """
125
+
126
+ names = ["name", "quantity", "lower", "upper"]
127
+
128
+ cols = [
129
+ [obj.name for obj in instance_list],
130
+ [obj.quantity for obj in instance_list],
131
+ [obj.lower for obj in instance_list],
132
+ [obj.upper for obj in instance_list],
133
+ ]
134
+ table = Table(data=cols, names=names)
135
+ table.meta["SACCTYPE"] = "tracer"
136
+ table.meta["SACCCLSS"] = cls.type_name
137
+ table.meta["EXTNAME"] = f"tracer:{cls.type_name}"
138
+ return table
139
+
140
+ @classmethod
141
+ def from_table(cls, table):
142
+ """Convert an astropy table into a dictionary of tracers
143
+
144
+ This is used when loading data from a file.
145
+ One tracer object is created for each "row" in each table.
146
+
147
+ :param table_list: List of astropy tables
148
+ :return: Dictionary of tracers
149
+ """
150
+ tracers = {}
151
+
152
+ for row in table:
153
+ name = row["name"]
154
+ quantity = row["quantity"]
155
+ lower = row["lower"]
156
+ upper = row["upper"]
157
+ tracers[name] = cls(name, quantity=quantity, lower=lower, upper=upper)
158
+ return tracers
159
+
160
+
161
+ class BinRichnessTracer(BaseTracer, type_name="bin_richness"): # type: ignore
162
+ """A tracer for a single richness bin. The tracer shall
163
+ be used for binned data where we want a desired quantity
164
+ per interval of log(richness), such that we only need the data
165
+ for a given interval instead of at individual richness."""
166
+ storage_type = MULTIPLE_OBJECTS_PER_TABLE
167
+
168
+ def __eq__(self, other) -> bool:
169
+ """Test for equality. If :python:`other` is not a
170
+ :python:`BinRichnessTracer`, then it is not equal to :python:`self`.
171
+ Otherwise, they are equal if names and the richness-range of the
172
+ bins, are equal."""
173
+ if not isinstance(other, BinRichnessTracer):
174
+ return False
175
+ return (
176
+ self.name == other.name
177
+ and self.lower == other.lower
178
+ and self.upper == other.upper
179
+ )
180
+
181
+ def __init__(self, name: str, lower: float, upper: float, **kwargs):
182
+ """
183
+ Create a tracer corresponding to a single richness bin.
184
+
185
+ :param name: The name of the tracer
186
+ :param lower: The lower bound of the richness bin in log10.
187
+ :param upper: The upper bound of the richness bin in log10.
188
+ """
189
+ super().__init__(name, **kwargs)
190
+ self.lower = lower
191
+ self.upper = upper
192
+
193
+ @classmethod
194
+ def to_table(cls, instance_list):
195
+ """Convert a list of BinZTracers to a list of astropy tables
196
+
197
+ This is used when saving data to a file.
198
+ One table is generated with the information for all the tracers.
199
+
200
+ :param instance_list: List of tracer instances
201
+ :return: List with a single astropy table
202
+ """
203
+ names = ["name", "quantity", "lower", "upper"]
204
+
205
+ cols = [
206
+ [obj.name for obj in instance_list],
207
+ [obj.quantity for obj in instance_list],
208
+ [obj.lower for obj in instance_list],
209
+ [obj.upper for obj in instance_list],
210
+ ]
211
+
212
+ table = Table(data=cols, names=names)
213
+ table.meta["SACCTYPE"] = "tracer"
214
+ table.meta["SACCCLSS"] = cls.type_name
215
+ table.meta["EXTNAME"] = f"tracer:{cls.type_name}"
216
+ return table
217
+
218
+ @classmethod
219
+ def from_table(cls, table):
220
+ """Convert an astropy table into a dictionary of tracers
221
+
222
+ This is used when loading data from a file.
223
+ One tracer object is created for each "row" in each table.
224
+
225
+ :param table_list: List of astropy tables
226
+ :return: Dictionary of tracers
227
+ """
228
+ tracers = {}
229
+
230
+ for row in table:
231
+ name = row["name"]
232
+ quantity = row["quantity"]
233
+ lower = row["lower"]
234
+ upper = row["upper"]
235
+ tracers[name] = cls(
236
+ name,
237
+ quantity=quantity,
238
+ lower=lower,
239
+ upper=upper,
240
+ )
241
+ return tracers
242
+
243
+
244
+ class BinRadiusTracer(BaseTracer, type_name="bin_radius"): # type: ignore
245
+ """A tracer for a single radial bin, e.g. when dealing with cluster shear profiles.
246
+ It gives the bin edges and the value of the bin "center". The latter would typically
247
+ be returned by CLMM and correspond to the average radius of the galaxies in that
248
+ radial bin. """
249
+
250
+ storage_type = MULTIPLE_OBJECTS_PER_TABLE
251
+
252
+ def __eq__(self, other) -> bool:
253
+ """Test for equality. If :python:`other` is not a
254
+ :python:`BinRadiusTracer`, then it is not equal to :python:`self`.
255
+ Otherwise, they are equal if names and the r-range and centers of the
256
+ bins, are equal."""
257
+ if not isinstance(other, BinRadiusTracer):
258
+ return False
259
+ return (
260
+ self.name == other.name
261
+ and self.lower == other.lower
262
+ and self.center == other.center
263
+ and self.upper == other.upper
264
+ )
265
+
266
+ def __init__(self, name: str, lower: float, upper: float, center: float, **kwargs):
267
+ """
268
+ Create a tracer corresponding to a single radial bin.
269
+
270
+ :param name: The name of the tracer
271
+ :param lower: The lower bound of the radius bin
272
+ :param upper: The upper bound of the radius bin
273
+ :param center: The value to use if a single point-estimate is needed.
274
+
275
+ Note that :python:`center` need not be the midpoint between
276
+ :python:`lower` and :python:`upper`'.
277
+ """
278
+ super().__init__(name, **kwargs)
279
+ self.lower = lower
280
+ self.upper = upper
281
+ self.center = center
282
+
283
+ @classmethod
284
+ def to_table(cls, instance_list):
285
+ """Convert a list of BinRadiusTracers to a single astropy table
286
+
287
+ This is used when saving data to a file.
288
+ One table is generated with the information for all the tracers.
289
+
290
+ :param instance_list: List of tracer instances
291
+ :return: List with a single astropy table
292
+ """
293
+
294
+ names = ["name", "quantity", "lower", "upper", "center"]
295
+
296
+ cols = [
297
+ [obj.name for obj in instance_list],
298
+ [obj.quantity for obj in instance_list],
299
+ [obj.lower for obj in instance_list],
300
+ [obj.upper for obj in instance_list],
301
+ [obj.center for obj in instance_list],
302
+ ]
303
+
304
+ table = Table(data=cols, names=names)
305
+ return table
306
+
307
+ @classmethod
308
+ def from_table(cls, table):
309
+ """Convert an astropy table into a dictionary of tracers
310
+
311
+ This is used when loading data from a file.
312
+ One tracer object is created for each "row" in each table.
313
+
314
+ :param table_list: List of astropy tables
315
+ :return: Dictionary of tracers
316
+ """
317
+ tracers = {}
318
+
319
+ for row in table:
320
+ name = row["name"]
321
+ quantity = row["quantity"]
322
+ lower = row["lower"]
323
+ upper = row["upper"]
324
+ center = row["center"]
325
+ tracers[name] = cls(
326
+ name,
327
+ quantity=quantity,
328
+ lower=lower,
329
+ upper=upper,
330
+ center=center,
331
+ )
332
+ return tracers
sacc/tracers/maps.py ADDED
@@ -0,0 +1,206 @@
1
+ from .base import BaseTracer, ONE_OBJECT_PER_TABLE, ONE_OBJECT_MULTIPLE_TABLES
2
+ from ..utils import remove_dict_null_values
3
+ from astropy.table import Table
4
+ import numpy as np
5
+
6
+ class MapTracer(BaseTracer, type_name='Map'):
7
+ """
8
+ A Tracer type for a sky map.
9
+
10
+ Takes at least two arguments, defining the map beam.
11
+
12
+ Parameters
13
+ ----------
14
+ name: str
15
+ The name for this specific tracer object.
16
+ ell: array
17
+ Array of multipole values at which the beam is defined.
18
+ beam: array
19
+ Beam multipoles at each value of ell.
20
+ beam_extra: array
21
+ Other beam-related arrays
22
+ (e.g. uncertainties, principal components,
23
+ alternative measurements, whatever).
24
+ map_unit: str
25
+ Map units (e.g. 'uK_CMB'). 'none' by default.
26
+ """
27
+
28
+ storage_type = ONE_OBJECT_PER_TABLE
29
+
30
+ def __init__(self, name, spin, ell, beam,
31
+ beam_extra=None, map_unit='none', **kwargs):
32
+ super().__init__(name, **kwargs)
33
+ self.spin = spin
34
+ self.map_unit = map_unit
35
+ self.ell = np.array(ell)
36
+ self.beam = np.array(beam)
37
+ self.beam_extra = {} if beam_extra is None else beam_extra
38
+
39
+ def to_table(self):
40
+ # Beams
41
+ names = ['ell', 'beam']
42
+ cols = [self.ell, self.beam]
43
+ for beam_id, col in self.beam_extra.items():
44
+ names.append(str(beam_id))
45
+ cols.append(col)
46
+ table = Table(data=cols, names=names)
47
+ table.meta['SACCQTTY'] = self.quantity
48
+ table.meta['SACCNAME'] = self.name
49
+ extname = f'tracer:{self.type_name}:{self.name}:beam'
50
+ table.meta['EXTNAME'] = extname
51
+ table.meta['MAP_UNIT'] = self.map_unit
52
+ table.meta['SPIN'] = self.spin
53
+ for key, value in self.metadata.items():
54
+ table.meta['META_'+key] = value
55
+ remove_dict_null_values(table.meta)
56
+
57
+ return table
58
+
59
+
60
+ @classmethod
61
+ def from_table(cls, table):
62
+ """Convert a single astropy table into a MapTracer instance.
63
+
64
+ This is used when loading data from a file.
65
+
66
+ Parameters
67
+ ----------
68
+ table: astropy.table.Table
69
+
70
+ Returns
71
+ -------
72
+ tracer: MapTracer
73
+ An instance of MapTracer created from the table.
74
+ """
75
+ name = table.meta['SACCNAME']
76
+ quantity = table.meta.get('SACCQTTY', 'generic')
77
+ map_unit = table.meta['MAP_UNIT']
78
+ spin = table.meta['SPIN']
79
+ metadata = {key[5:]: value for key, value in table.meta.items() if key.startswith("META_")}
80
+
81
+ ell = table['ell']
82
+ beam = table['beam']
83
+ beam_extra = {col.name: col.data for col in table.columns.values() if col.name not in ['ell', 'beam']}
84
+
85
+ return cls(name, spin, ell, beam, beam_extra=beam_extra, map_unit=map_unit, quantity=quantity, metadata=metadata)
86
+
87
+
88
+ class NuMapTracer(BaseTracer, type_name='NuMap'):
89
+ """
90
+ A Tracer type for a sky map at a given frequency.
91
+
92
+ Takes at least four arguments, defining the bandpass and beam.
93
+
94
+ Parameters
95
+ ----------
96
+ name: str
97
+ The name for this specific tracer, e.g. a frequency band
98
+ identifier.
99
+ spin: int
100
+ Spin for this observable. Either 0 (e.g. intensity)
101
+ or 2 (e.g. polarization).
102
+ nu: array
103
+ Array of frequencies.
104
+ bandpass: array
105
+ Bandpass transmission.
106
+ bandpass_extra: array
107
+ Other bandpass-related arrays
108
+ (e.g. uncertainties, principal components,
109
+ alternative measurements, whatever).
110
+ ell: array
111
+ Array of multipole values at which the beam is defined.
112
+ beam: array
113
+ Beam.
114
+ beam_extra: array
115
+ Other beam-related arrays
116
+ (e.g. uncertainties, principal components,
117
+ alternative measurements, whatever).
118
+ nu_unit: str
119
+ Frequency units ('GHz' by default).
120
+ map_unit: str
121
+ Map units (e.g. 'uK_CMB'). 'none' by default.
122
+ """
123
+
124
+ storage_type = ONE_OBJECT_MULTIPLE_TABLES
125
+
126
+ def __init__(self, name, spin, nu, bandpass,
127
+ ell, beam, bandpass_extra=None,
128
+ beam_extra=None, nu_unit='GHz',
129
+ map_unit='none', **kwargs):
130
+ super().__init__(name, **kwargs)
131
+ self.spin = spin
132
+ self.nu = np.array(nu)
133
+ self.nu_unit = nu_unit
134
+ self.map_unit = map_unit
135
+ self.bandpass = np.array(bandpass)
136
+ self.bandpass_extra = {} if bandpass_extra is None else bandpass_extra
137
+ self.ell = np.array(ell)
138
+ self.beam = np.array(beam)
139
+ self.beam_extra = {} if beam_extra is None else beam_extra
140
+
141
+ def to_tables(self):
142
+ # Bandpass
143
+ names = ['nu', 'bandpass']
144
+ cols = [self.nu, self.bandpass]
145
+ for bandpass_id, col in self.bandpass_extra.items():
146
+ names.append(str(bandpass_id))
147
+ cols.append(col)
148
+ bandpass_table = Table(data=cols, names=names)
149
+ bandpass_table.meta['SACCQTTY'] = self.quantity
150
+ bandpass_table.meta['NU_UNIT'] = self.nu_unit
151
+ bandpass_table.meta['SACCNAME'] = self.name
152
+ bandpass_table.meta['SPIN'] = self.spin
153
+ for key, value in self.metadata.items():
154
+ bandpass_table.meta['META_'+key] = value
155
+ remove_dict_null_values(bandpass_table.meta)
156
+
157
+ # Beam
158
+ names = ['ell', 'beam']
159
+ cols = [self.ell, self.beam]
160
+ for beam_id, col in self.beam_extra.items():
161
+ names.append(str(beam_id))
162
+ cols.append(col)
163
+ beam_table = Table(data=cols, names=names)
164
+ beam_table.meta['SACCQTTY'] = self.quantity
165
+ beam_table.meta['MAP_UNIT'] = self.map_unit
166
+ beam_table.meta['SPIN'] = self.spin
167
+ for key, value in self.metadata.items():
168
+ beam_table.meta['META_'+key] = value
169
+ remove_dict_null_values(beam_table.meta)
170
+
171
+ return {'bandpass': bandpass_table, 'beam': beam_table}
172
+
173
+ @classmethod
174
+ def from_tables(cls, table_dict):
175
+ """Convert a dictionary of astropy tables into a NuMapTracer instance."""
176
+ bandpass_table = table_dict['bandpass']
177
+ beam_table = table_dict['beam']
178
+
179
+ # Get the various bits of metadata out of the bandpass table
180
+ name = bandpass_table.meta['SACCNAME']
181
+ spin = bandpass_table.meta['SPIN']
182
+ quantity = bandpass_table.meta.get('SACCQTTY', 'generic')
183
+ nu_unit = bandpass_table.meta['NU_UNIT']
184
+
185
+ # Additional miscellaneous metadata
186
+ metadata = {key[5:]: value for key, value in bandpass_table.meta.items() if key.startswith("META_")}
187
+
188
+ # And the actual bandpass data columns themselves
189
+ nu = bandpass_table['nu']
190
+ bandpass = bandpass_table['bandpass']
191
+ bandpass_extra = {col.name: col.data for col in bandpass_table.columns.values() if col.name not in ['nu', 'bandpass']}
192
+
193
+ # Now the same for the beam table
194
+ ell = beam_table['ell']
195
+ beam = beam_table['beam']
196
+ beam_extra = {col.name: col.data for col in beam_table.columns.values() if col.name not in ['ell', 'beam']}
197
+ map_unit = beam_table.meta['MAP_UNIT']
198
+
199
+ return cls(name, spin, nu, bandpass,
200
+ ell, beam,
201
+ bandpass_extra=bandpass_extra,
202
+ beam_extra=beam_extra,
203
+ nu_unit=nu_unit,
204
+ map_unit=map_unit,
205
+ quantity=quantity,
206
+ metadata=metadata)
sacc/tracers/misc.py ADDED
@@ -0,0 +1,87 @@
1
+ from .base import BaseTracer, MULTIPLE_OBJECTS_PER_TABLE
2
+ from astropy.table import Table
3
+ from ..utils import hide_null_values, remove_dict_null_values
4
+
5
+ class MiscTracer(BaseTracer, type_name='Misc'):
6
+ """A Tracer type for miscellaneous other data points.
7
+
8
+ MiscTracers do not have any attributes except for their
9
+ name, so can be used for tagging external data, for example.
10
+
11
+ Parameters
12
+ ----------
13
+ name: str
14
+ The name of the tracer
15
+ """
16
+ storage_type = MULTIPLE_OBJECTS_PER_TABLE
17
+
18
+ def __init__(self, name, **kwargs):
19
+ super().__init__(name, **kwargs)
20
+
21
+ @classmethod
22
+ def to_table(cls, instance_list):
23
+ """Convert a list of MiscTracer instances to a astropy tables.
24
+
25
+ This is used when saving data to file.
26
+
27
+ All the instances are converted to a single table, which is
28
+ returned in a list with one element so that it can be used
29
+ in combination with the parent.
30
+
31
+ You can use the parent class to_tables class method to convert
32
+ a mixed list of different tracer types.
33
+
34
+ You shouldn't generally need to call this method directly.
35
+
36
+ Parameters
37
+ ----------
38
+ instance_list: list
39
+ list of MiscTracer objects
40
+
41
+ Returns
42
+ -------
43
+ tables: list
44
+ List containing one astropy table
45
+ """
46
+ metadata_cols = set()
47
+ for obj in instance_list:
48
+ metadata_cols.update(obj.metadata.keys())
49
+ metadata_cols = list(metadata_cols)
50
+
51
+ cols = [[obj.name for obj in instance_list],
52
+ [obj.quantity for obj in instance_list]]
53
+ for name in metadata_cols:
54
+ cols.append([obj.metadata.get(name) for obj in instance_list])
55
+
56
+ table = Table(data=cols,
57
+ names=['name', 'quantity'] + metadata_cols)
58
+ hide_null_values(table)
59
+ return table
60
+
61
+ @classmethod
62
+ def from_table(cls, table):
63
+ """Convert a list of astropy table into a dictionary of MiscTracer instances.
64
+
65
+ In general table_list should have a single element in, since all the
66
+ MiscTracers are stored in a single table during to_tables
67
+
68
+ Parameters
69
+ ----------
70
+ table_list: List[astropy.table.Table]
71
+
72
+ Returns
73
+ -------
74
+ tracers: Dict[str: MiscTracer]
75
+ """
76
+ tracers = {}
77
+
78
+ metadata_cols = [col for col in table.colnames
79
+ if col not in ['name', 'quantity']]
80
+
81
+ for row in table:
82
+ name = row['name']
83
+ quantity = row['quantity']
84
+ metadata = {key: row[key] for key in metadata_cols}
85
+ remove_dict_null_values(metadata)
86
+ tracers[name] = cls(name, quantity=quantity, metadata=metadata)
87
+ return tracers