lsst.skymap
g6512b7a353+6ecdf13bca
Toggle main menu visibility
Loading...
Searching...
No Matches
python
lsst
skymap
packers.py
Go to the documentation of this file.
1
# This file is part of skymap.
2
#
3
# Developed for the LSST Data Management System.
4
# This product includes software developed by the LSST Project
5
# (http://www.lsst.org).
6
# See the COPYRIGHT file at the top-level directory of this distribution
7
# for details of code ownership.
8
#
9
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
11
# the Free Software Foundation, either version 3 of the License, or
12
# (at your option) any later version.
13
#
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU General Public License for more details.
18
#
19
# You should have received a copy of the GNU General Public License
20
# along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22
from
__future__
import
annotations
23
24
__all__ = (
"SkyMapDimensionPacker"
,)
25
26
from
collections.abc
import
Mapping
27
28
from
lsst.pex.config
import
Config, Field, DictField, ConfigurableField
29
from
lsst.daf.butler
import
DimensionPacker, DimensionGroup, DataCoordinate
30
31
32
class
SkyMapDimensionPackerConfig
(Config):
33
bands = DictField(
34
"Mapping from band name to integer to use in the packed ID. "
35
"The default (None) is to use a hard-coded list of common bands; "
36
"pipelines that can enumerate the set of bands they are likely to see "
37
"should override this."
,
38
keytype=str,
39
itemtype=int,
40
default=
None
,
41
optional=
True
,
42
)
43
n_bands = Field(
44
"Number of bands to reserve space for. "
45
"If zero, bands are not included in the packed integer at all. "
46
"If `None`, the size of 'bands' is used."
,
47
dtype=int,
48
optional=
True
,
49
default=0,
50
)
51
n_tracts = Field(
52
"Number of tracts, or, more precisely, one greater than the maximum tract ID."
53
"Default (None) obtains this value from the skymap dimension record."
,
54
dtype=int,
55
optional=
True
,
56
default=
None
,
57
)
58
n_patches = Field(
59
"Number of patches per tract, or, more precisely, one greater than the maximum patch ID."
60
"Default (None) obtains this value from the skymap dimension record."
,
61
dtype=int,
62
optional=
True
,
63
default=
None
,
64
)
65
66
67
class
SkyMapDimensionPacker
(DimensionPacker):
68
"""A `DimensionPacker` for tract, patch and optionally band,
69
given a SkyMap.
70
71
Parameters
72
----------
73
fixed : `lsst.daf.butler.DataCoordinate`
74
Data ID that identifies just the ``skymap`` dimension. Must have
75
dimension records attached unless ``n_tracts`` and ``n_patches`` are
76
not `None`.
77
dimensions : `lsst.daf.butler.DimensionGroup`, optional
78
The dimensions of data IDs packed by this instance. Must include
79
``{skymap, tract, patch}``, and may include ``band``. If not provided,
80
this will be set to include ``band`` if ``n_bands != 0``.
81
bands : `~collections.abc.Mapping` [ `str`, `int` ] or `None`, optional
82
Mapping from band name to integer to use in the packed ID. `None` uses
83
a fixed set of bands defined in this class. When calling code can
84
enumerate the bands it is likely to see, providing an explicit mapping
85
is preferable.
86
n_bands : `int` or `None`, optional
87
The number of bands to leave room for in the packed ID. If `None`,
88
this will be set to ``len(bands)``. If ``0``, the band will not be
89
included in the dimensions at all. If ``1``, the band will be included
90
in the dimensions but will not occupy any extra bits in the packed ID.
91
This may be larger or smaller than ``len(bands)``, to reserve extra
92
space for the future or align to byte boundaries, or support a subset
93
of a larger mapping, respectively.
94
n_tracts : `int` or `None`, optional
95
The number of tracts to leave room for in the packed ID. If `None`,
96
this will be set via the ``skymap`` dimension record in ``fixed``.
97
n_patches : `int` or `None`, optional
98
The number of patches (per tract) to leave room for in the packed ID.
99
If `None`, this will be set via the ``skymap`` dimension record in
100
``fixed``.
101
102
Notes
103
-----
104
The standard pattern for constructing instances of this class is to use
105
`make_config_field`::
106
107
class SomeConfig(lsst.pex.config.Config):
108
packer = ObservationDimensionPacker.make_config_field()
109
110
class SomeTask(lsst.pipe.base.Task):
111
ConfigClass = SomeConfig
112
113
def run(self, ..., data_id: DataCoordinate):
114
packer = self.config.packer.apply(data_id)
115
packed_id = packer.pack(data_id)
116
...
117
118
"""
119
120
SUPPORTED_FILTERS = (
121
[
None
]
122
+ list(
"ugrizyUBGVRIZYJHK"
)
# split string into single chars
123
+ [f
"N{d}"
for
d
in
(387, 515, 656, 816, 921, 1010)]
# HSC narrow-bands
124
+ [f
"N{d}"
for
d
in
(419, 540, 708, 964)]
# DECam narrow-bands
125
)
126
"""Sequence of supported bands used to construct a mapping from band name
127
to integer when the 'bands' config option is `None` or no config is
128
provided.
129
130
This variable should no longer be modified to add new filters; pass
131
``bands`` at construction or use `from_config` instead.
132
"""
133
134
ConfigClass = SkyMapDimensionPackerConfig
135
136
def
__init__
(
137
self,
138
fixed: DataCoordinate,
139
dimensions: DimensionGroup |
None
=
None
,
140
bands: Mapping[str, int] |
None
=
None
,
141
n_bands: int |
None
=
None
,
142
n_tracts: int |
None
=
None
,
143
n_patches: int |
None
=
None
,
144
):
145
if
bands
is
None
:
146
bands = {b: i
for
i, b
in
enumerate(self.
SUPPORTED_FILTERS
)}
147
if
dimensions
is
None
:
148
if
n_bands
is
None
:
149
n_bands = len(bands)
150
dimension_names = [
"tract"
,
"patch"
]
151
if
n_bands != 0:
152
dimension_names.append(
"band"
)
153
dimensions = fixed.universe.conform(dimension_names)
154
else
:
155
if
"band"
not
in
dimensions.names:
156
n_bands = 0
157
if
dimensions.names != {
"tract"
,
"patch"
,
"skymap"
}:
158
raise
ValueError(
159
f
"Invalid dimensions for skymap dimension packer with n_bands=0: {dimensions}."
160
)
161
else
:
162
if
dimensions.names != {
"tract"
,
"patch"
,
"skymap"
,
"band"
}:
163
raise
ValueError(
164
f
"Invalid dimensions for skymap dimension packer with n_bands>0: {dimensions}."
165
)
166
if
n_bands
is
None
:
167
n_bands = len(bands)
168
if
n_tracts
is
None
:
169
n_tracts = fixed.records[
"skymap"
].tract_max
170
if
n_patches
is
None
:
171
n_patches = (
172
fixed.records[
"skymap"
].patch_nx_max
173
* fixed.records[
"skymap"
].patch_ny_max
174
)
175
super().
__init__
(fixed, dimensions)
176
self.
_bands
= bands
177
self.
_n_bands
= n_bands
178
self.
_n_tracts
= n_tracts
179
self.
_n_patches
= n_patches
180
self.
_bands_list
=
None
181
182
@classmethod
183
def
make_config_field
(
184
cls,
185
doc: str =
"How to pack tract, patch, and possibly band into an integer."
186
) -> ConfigurableField:
187
"""Make a config field to control how skymap data IDs are packed.
188
189
Parameters
190
----------
191
doc : `str`, optional
192
Documentation for the config field.
193
194
Returns
195
-------
196
field : `lsst.pex.config.ConfigurableField`
197
A config field whose instance values are [wrapper proxies to]
198
`SkyMapDimensionPackerConfig` instances.
199
"""
200
return
ConfigurableField(doc, target=cls.
from_config
, ConfigClass=cls.
ConfigClass
)
201
202
@classmethod
203
def
from_config
(
204
cls, data_id: DataCoordinate, config: SkyMapDimensionPackerConfig
205
) -> SkyMapDimensionPacker:
206
"""Construct a dimension packer from a config object and a data ID.
207
208
Parameters
209
----------
210
data_id : `lsst.daf.butler.DataCoordinate`
211
Data ID that identifies at least the ``skymap`` dimension. Must
212
have dimension records attached unless ``config.n_tracts`` and
213
``config.n_patches`` are both not `None`.
214
config : `SkyMapDimensionPackerConfig`
215
Configuration object.
216
217
Returns
218
-------
219
packer : `SkyMapDimensionPackerConfig`
220
New dimension packer.
221
222
Notes
223
-----
224
This interface is provided for consistency with the `lsst.pex.config`
225
"Configurable" concept, and is invoked when ``apply(data_id)`` is
226
called on a config instance attribute that corresponds to a field
227
created by `make_config_field`. The constructor signature cannot play
228
this role easily for backwards compatibility reasons.
229
"""
230
return
cls(
231
data_id.subset(data_id.universe.conform([
"skymap"
])),
232
n_bands=config.n_bands,
233
bands=config.bands,
234
n_tracts=config.n_tracts,
235
n_patches=config.n_patches,
236
)
237
238
@property
239
def
maxBits
(self) -> int:
240
# Docstring inherited from DataIdPacker.maxBits
241
packedMax = self.
_n_tracts
* self.
_n_patches
242
if
self.
_n_bands
:
243
packedMax *= self.
_n_bands
244
return
(packedMax - 1).bit_length()
245
246
def
_pack
(self, dataId: DataCoordinate) -> int:
247
# Docstring inherited from DataIdPacker.pack
248
if
dataId[
"patch"
] >= self.
_n_patches
:
249
raise
ValueError(f
"Patch ID {dataId['patch']} is out of bounds; expected <{self._n_patches}."
)
250
if
dataId[
"tract"
] >= self.
_n_tracts
:
251
raise
ValueError(f
"Tract ID {dataId['tract']} is out of bounds; expected <{self._n_tracts}."
)
252
packed = dataId[
"patch"
] + self.
_n_patches
* dataId[
"tract"
]
253
if
self.
_n_bands
:
254
if
(band_index := self.
_bands
.get(dataId[
"band"
]))
is
None
:
255
raise
ValueError(
256
f
"Band {dataId['band']!r} is not supported by SkyMapDimensionPacker "
257
f
"configuration; expected one of {list(self._bands)}."
258
)
259
if
band_index >= self.
_n_bands
:
260
raise
ValueError(
261
f
"Band index {band_index} for {dataId['band']!r} is out of bounds; "
262
f
"expected <{self._n_bands}."
263
)
264
packed += self.
_bands
[dataId[
"band"
]] * self.
_n_patches
* self.
_n_tracts
265
return
packed
266
267
def
unpack
(self, packedId: int) -> DataCoordinate:
268
# Docstring inherited from DataIdPacker.unpack
269
d = {
"skymap"
: self.fixed[
"skymap"
]}
270
if
self.
_n_bands
:
271
index, packedId = divmod(packedId, (self.
_n_tracts
* self.
_n_patches
))
272
if
self.
_bands_list
is
None
:
273
self.
_bands_list
= list(self.
_bands
)
274
d[
"band"
] = self.
_bands_list
[index]
275
d[
"tract"
], d[
"patch"
] = divmod(packedId, self.
_n_patches
)
276
return
DataCoordinate.standardize(d, dimensions=self.dimensions)
lsst.skymap.packers.SkyMapDimensionPackerConfig
Definition
packers.py:32
lsst.skymap.packers.SkyMapDimensionPacker
Definition
packers.py:67
lsst.skymap.packers.SkyMapDimensionPacker.__init__
__init__(self, DataCoordinate fixed, DimensionGroup|None dimensions=None, Mapping[str, int]|None bands=None, int|None n_bands=None, int|None n_tracts=None, int|None n_patches=None)
Definition
packers.py:144
lsst.skymap.packers.SkyMapDimensionPacker.SUPPORTED_FILTERS
tuple SUPPORTED_FILTERS
Definition
packers.py:120
lsst.skymap.packers.SkyMapDimensionPacker._n_tracts
_n_tracts
Definition
packers.py:178
lsst.skymap.packers.SkyMapDimensionPacker.maxBits
int maxBits(self)
Definition
packers.py:239
lsst.skymap.packers.SkyMapDimensionPacker.ConfigClass
ConfigClass
Definition
packers.py:134
lsst.skymap.packers.SkyMapDimensionPacker._n_bands
_n_bands
Definition
packers.py:177
lsst.skymap.packers.SkyMapDimensionPacker._bands_list
_bands_list
Definition
packers.py:180
lsst.skymap.packers.SkyMapDimensionPacker.make_config_field
ConfigurableField make_config_field(cls, str doc="How to pack tract, patch, and possibly band into an integer.")
Definition
packers.py:186
lsst.skymap.packers.SkyMapDimensionPacker.from_config
SkyMapDimensionPacker from_config(cls, DataCoordinate data_id, SkyMapDimensionPackerConfig config)
Definition
packers.py:205
lsst.skymap.packers.SkyMapDimensionPacker._pack
int _pack(self, DataCoordinate dataId)
Definition
packers.py:246
lsst.skymap.packers.SkyMapDimensionPacker._n_patches
_n_patches
Definition
packers.py:179
lsst.skymap.packers.SkyMapDimensionPacker._bands
_bands
Definition
packers.py:176
lsst.skymap.packers.SkyMapDimensionPacker.unpack
DataCoordinate unpack(self, int packedId)
Definition
packers.py:267
lsst::pex::config
Generated on
for lsst.skymap by
1.17.0