22from __future__
import annotations
24__all__ = (
"SkyMapDimensionPacker",)
26from collections.abc
import Mapping
29from lsst.daf.butler
import DimensionPacker, DimensionGraph, DimensionGroup, DataCoordinate
30from deprecated.sphinx
import deprecated
35 "Mapping from band name to integer to use in the packed ID. "
36 "The default (None) is to use a hard-coded list of common bands; "
37 "pipelines that can enumerate the set of bands they are likely to see "
38 "should override this.",
45 "Number of bands to reserve space for. "
46 "If zero, bands are not included in the packed integer at all. "
47 "If `None`, the size of 'bands' is used.",
53 "Number of tracts, or, more precisely, one greater than the maximum tract ID."
54 "Default (None) obtains this value from the skymap dimension record.",
60 "Number of patches per tract, or, more precisely, one greater than the maximum patch ID."
61 "Default (None) obtains this value from the skymap dimension record.",
69 """A `DimensionPacker` for tract, patch and optionally band,
74 fixed : `lsst.daf.butler.DataCoordinate`
75 Data ID that identifies just the ``skymap`` dimension. Must have
76 dimension records attached unless ``n_tracts`` and ``n_patches`` are
78 dimensions : `lsst.daf.butler.DimensionGraph`, or \
79 `lsst.daf.butler.DimensionGroup`, optional
80 The dimensions of data IDs packed by this instance. Must include
81 ``{skymap, tract, patch}``, and may include ``band``. If not provided,
82 this will be set to include ``band`` if ``n_bands != 0``.
83 bands : `~collections.abc.Mapping` [ `str`, `int` ] or `None`, optional
84 Mapping from band name to integer to use in the packed ID. `None` uses
85 a fixed set of bands defined in this class. When calling code can
86 enumerate the bands it is likely to see, providing an explicit mapping
88 n_bands : `int` or `None`, optional
89 The number of bands to leave room for in the packed ID. If `None`,
90 this will be set to ``len(bands)``. If ``0``, the band will not be
91 included in the dimensions at all. If ``1``, the band will be included
92 in the dimensions but will not occupy any extra bits in the packed ID.
93 This may be larger or smaller than ``len(bands)``, to reserve extra
94 space for the future or align to byte boundaries, or support a subset
95 of a larger mapping, respectively.
96 n_tracts : `int` or `None`, optional
97 The number of tracts to leave room for in the packed ID. If `None`,
98 this will be set via the ``skymap`` dimension record in ``fixed``.
99 n_patches : `int` or `None`, optional
100 The number of patches (per tract) to leave room for in the packed ID.
101 If `None`, this will be set via the ``skymap`` dimension record in
106 The standard pattern for constructing instances of this class is to use
107 `make_config_field`::
109 class SomeConfig(lsst.pex.config.Config):
110 packer = ObservationDimensionPacker.make_config_field()
112 class SomeTask(lsst.pipe.base.Task):
113 ConfigClass = SomeConfig
115 def run(self, ..., data_id: DataCoordinate):
116 packer = self.config.packer.apply(data_id)
117 packed_id = packer.pack(data_id)
122 SUPPORTED_FILTERS = (
124 + list(
"ugrizyUBGVRIZYJHK")
125 + [f
"N{d}" for d
in (387, 515, 656, 816, 921, 1010)]
126 + [f
"N{d}" for d
in (419, 540, 708, 964)]
128 """Sequence of supported bands used to construct a mapping from band name
129 to integer when the 'bands' config option is `None` or no config is
132 This variable should no longer be modified to add new filters; pass
133 ``bands`` at construction or use `from_config` instead.
136 ConfigClass = SkyMapDimensionPackerConfig
141 reason=
"This classmethod cannot reflect all __init__ args and will be removed after v26.",
143 category=FutureWarning,
146 """Return an integer that represents the band with the given
152 raise NotImplementedError(f
"band '{name}' not supported by this ID packer.")
157 reason=
"This classmethod cannot reflect all __init__ args and will be removed after v26.",
159 category=FutureWarning,
162 """Return an band name from its integer representation."""
168 reason=
"This classmethod cannot reflect all __init__ args and will be removed after v26.",
170 category=FutureWarning,
177 fixed: DataCoordinate,
178 dimensions: DimensionGroup | DimensionGraph |
None =
None,
179 bands: Mapping[str, int] |
None =
None,
180 n_bands: int |
None =
None,
181 n_tracts: int |
None =
None,
182 n_patches: int |
None =
None,
186 if dimensions
is None:
189 dimension_names = [
"tract",
"patch"]
191 dimension_names.append(
"band")
192 dimensions = fixed.universe.conform(dimension_names)
194 if "band" not in dimensions.names:
196 if dimensions.names != {
"tract",
"patch",
"skymap"}:
198 f
"Invalid dimensions for skymap dimension packer with n_bands=0: {dimensions}."
201 if dimensions.names != {
"tract",
"patch",
"skymap",
"band"}:
203 f
"Invalid dimensions for skymap dimension packer with n_bands>0: {dimensions}."
208 n_tracts = fixed.records[
"skymap"].tract_max
209 if n_patches
is None:
211 fixed.records[
"skymap"].patch_nx_max
212 * fixed.records[
"skymap"].patch_ny_max
224 doc: str =
"How to pack tract, patch, and possibly band into an integer."
225 ) -> ConfigurableField:
226 """Make a config field to control how skymap data IDs are packed.
230 doc : `str`, optional
231 Documentation for the config field.
235 field : `lsst.pex.config.ConfigurableField`
236 A config field whose instance values are [wrapper proxies to]
237 `SkyMapDimensionPackerConfig` instances.
243 cls, data_id: DataCoordinate, config: SkyMapDimensionPackerConfig
244 ) -> SkyMapDimensionPacker:
245 """Construct a dimension packer from a config object and a data ID.
249 data_id : `lsst.daf.butler.DataCoordinate`
250 Data ID that identifies at least the ``skymap`` dimension. Must
251 have dimension records attached unless ``config.n_tracts`` and
252 ``config.n_patches`` are both not `None`.
253 config : `SkyMapDimensionPackerConfig`
254 Configuration object.
258 packer : `SkyMapDimensionPackerConfig`
259 New dimension packer.
263 This interface is provided for consistency with the `lsst.pex.config`
264 "Configurable" concept, and is invoked when ``apply(data_id)`` is
265 called on a config instance attribute that corresponds to a field
266 created by `make_config_field`. The constructor signature cannot play
267 this role easily for backwards compatibility reasons.
270 data_id.subset(data_id.universe.conform([
"skymap"])),
271 n_bands=config.n_bands,
273 n_tracts=config.n_tracts,
274 n_patches=config.n_patches,
283 return (packedMax - 1).bit_length()
285 def _pack(self, dataId: DataCoordinate) -> int:
288 raise ValueError(f
"Patch ID {dataId['patch']} is out of bounds; expected <{self._n_patches}.")
290 raise ValueError(f
"Tract ID {dataId['tract']} is out of bounds; expected <{self._n_tracts}.")
291 packed = dataId[
"patch"] + self.
_n_patches * dataId[
"tract"]
293 if (band_index := self.
_bands.get(dataId[
"band"]))
is None:
295 f
"Band {dataId['band']!r} is not supported by SkyMapDimensionPacker "
296 f
"configuration; expected one of {list(self._bands)}."
300 f
"Band index {band_index} for {dataId['band']!r} is out of bounds; "
301 f
"expected <{self._n_bands}."
306 def unpack(self, packedId: int) -> DataCoordinate:
308 d = {
"skymap": self.fixed[
"skymap"]}
314 d[
"tract"], d[
"patch"] = divmod(packedId, self.
_n_patches)
315 return DataCoordinate.standardize(d, dimensions=self.dimensions)
getFilterNameFromInt(cls, num)
ConfigurableField make_config_field(cls, str doc="How to pack tract, patch, and possibly band into an integer.")
__init__(self, DataCoordinate fixed, DimensionGroup|DimensionGraph|None dimensions=None, Mapping[str, int]|None bands=None, int|None n_bands=None, int|None n_tracts=None, int|None n_patches=None)
SkyMapDimensionPacker from_config(cls, DataCoordinate data_id, SkyMapDimensionPackerConfig config)
int _pack(self, DataCoordinate dataId)
getIntFromFilter(cls, name)
DataCoordinate unpack(self, int packedId)