Coverage for python/lsst/cell_coadds/_identifiers.py: 86%
51 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 04:38 -0700
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 04:38 -0700
1# This file is part of cell_coadds.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://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 <https://www.gnu.org/licenses/>.
22from __future__ import annotations
24__all__ = (
25 "PatchIdentifiers",
26 "CellIdentifiers",
27 "ObservationIdentifiers",
28)
31from dataclasses import dataclass
32from typing import Self, cast
34from lsst.daf.butler import DataCoordinate, DimensionRecord
35from lsst.pipe.base import Instrument
36from lsst.skymap import Index2D
39@dataclass(frozen=True)
40class PatchIdentifiers:
41 """Struct of identifiers for a coadd patch."""
43 skymap: str
44 """The name of the skymap this patch belongs to.
45 """
47 tract: int
48 """The name of the tract this patch belongs to.
49 """
51 patch: Index2D
52 """Identifiers for the patch itself.
53 """
55 band: str | None
56 """Name of the band, if any.
57 """
59 @classmethod
60 def from_data_id(cls, data_id: DataCoordinate) -> PatchIdentifiers:
61 """Construct from a data ID.
63 Parameters
64 ----------
65 data_id : `~lsst.daf.butler.DataCoordinate`
66 Fully-expanded data ID that includes the 'patch' dimension and
67 optionally the `band` dimension.
69 Returns
70 -------
71 identifiers : `PatchIdentifiers`
72 Struct of identifiers for this patch.
73 """
74 patch_record = cast(DimensionRecord, data_id.records["patch"])
75 return cls(
76 skymap=cast(str, data_id["skymap"]),
77 tract=cast(int, data_id["tract"]),
78 patch=Index2D(x=patch_record.cell_x, y=patch_record.cell_y),
79 band=cast(str, data_id.get("band")),
80 )
83@dataclass(frozen=True)
84class CellIdentifiers(PatchIdentifiers):
85 """Struct of identifiers for a coadd cell."""
87 cell: Index2D
88 """Identifiers for the cell itself."""
90 @classmethod
91 def from_data_id( # type: ignore [override]
92 cls, data_id: DataCoordinate, cell: Index2D
93 ) -> CellIdentifiers:
94 """Construct from a data ID and a cell index.
96 Parameters
97 ----------
98 data_id : `~lsst.daf.butler.DataCoordinate`
99 Fully-expanded data ID that includes the 'patch' dimension and
100 optionally the `band` dimension.
101 cell : `~lsst.skymap.Index2D`
102 Index of the cell within the patch.
104 Returns
105 -------
106 identifiers : `CellIdentifiers`
107 Struct of identifiers for this cell within a patch.
108 """
109 patch_record = cast(DimensionRecord, data_id.records["patch"])
110 return cls(
111 skymap=cast(str, data_id["skymap"]),
112 tract=cast(int, data_id["tract"]),
113 patch=Index2D(x=patch_record.cell_x, y=patch_record.cell_y),
114 band=cast(str, data_id.get("band")),
115 cell=cell,
116 )
119@dataclass(frozen=True)
120class ObservationIdentifiers:
121 """Struct of identifiers for an observation that contributed to a coadd
122 cell.
123 """
125 instrument: str
126 """Name of the instrument that this observation was taken with.
127 """
129 physical_filter: str
130 """Name of the physical filter that this observation was taken with.
131 """
133 packed: int
134 """ID that uniquely identifies both the visit and detector by packing
135 together their IDs.
136 """
138 visit: int
139 """Unique identifier for the visit.
141 A visit may be comprised of more than one exposure only if all were
142 observed back-to-back with no dithers, allowing them to be combined early
143 the processing with no resampling. All detector-level images in a visit
144 share the same visit ID.
145 """
147 day_obs: int
148 """A day and night of observations that rolls over during daylight hours.
149 The identifier is an decimal integer-concatenated date, i.e. YYYYMMDD,
150 with the exact rollover time observatory-dependent.
151 """
153 detector: int
154 """Unique identifier for the detector.
155 """
157 @classmethod
158 def from_data_id(cls, data_id: DataCoordinate, *, backup_detector: int = -1) -> ObservationIdentifiers:
159 """Construct from a data ID.
161 Parameters
162 ----------
163 data_id : `~lsst.daf.butler.DataCoordinate`
164 Fully-expanded data ID that includes the 'visit', 'detector' and
165 'day_obs' dimensions.
166 backup_detector : `int`, optional
167 Detector ID to use as a backup if not present in ``data_id``.
168 This is not used if detector information is available in
169 ``data_id`` and does not override it.
171 Returns
172 -------
173 identifiers : `ObservationIdentifiers`
174 Struct of identifiers for this observation.
175 """
176 packer = Instrument.make_default_dimension_packer(data_id, is_exposure=False)
177 detector = data_id.get("detector", backup_detector)
178 day_obs = data_id.get("day_obs", None)
179 return cls(
180 instrument=cast(str, data_id["instrument"]),
181 physical_filter=cast(str, data_id["physical_filter"]),
182 # Passing detector twice does not crash the packer. So send it in
183 # without checking if available in data_id.
184 packed=cast(int, packer.pack(data_id, detector=detector, returnMaxBits=False)),
185 visit=cast(int, data_id["visit"]),
186 day_obs=cast(int, day_obs),
187 detector=cast(int, detector),
188 )
190 def __lt__(self, other: Self, /) -> bool:
191 return self.packed < other.packed