Coverage for python/lsst/cell_coadds/_identifiers.py: 86%

47 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-12 09:09 +0000

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/>. 

21 

22from __future__ import annotations 

23 

24__all__ = ( 

25 "PatchIdentifiers", 

26 "CellIdentifiers", 

27 "ObservationIdentifiers", 

28) 

29 

30 

31from dataclasses import dataclass 

32from typing import Self, cast 

33 

34from lsst.daf.butler import DataCoordinate, DimensionRecord 

35from lsst.skymap import Index2D 

36 

37 

38@dataclass(frozen=True) 

39class PatchIdentifiers: 

40 """Struct of identifiers for a coadd patch.""" 

41 

42 skymap: str 

43 """The name of the skymap this patch belongs to. 

44 """ 

45 

46 tract: int 

47 """The name of the tract this patch belongs to. 

48 """ 

49 

50 patch: Index2D 

51 """Identifiers for the patch itself. 

52 """ 

53 

54 band: str | None 

55 """Name of the band, if any. 

56 """ 

57 

58 @classmethod 

59 def from_data_id(cls, data_id: DataCoordinate) -> PatchIdentifiers: 

60 """Construct from a data ID. 

61 

62 Parameters 

63 ---------- 

64 data_id : `~lsst.daf.butler.DataCoordinate` 

65 Fully-expanded data ID that includes the 'patch' dimension and 

66 optionally the `band` dimension. 

67 

68 Returns 

69 ------- 

70 identifiers : `PatchIdentifiers` 

71 Struct of identifiers for this patch. 

72 """ 

73 patch_record = cast(DimensionRecord, data_id.records["patch"]) 

74 return cls( 

75 skymap=cast(str, data_id["skymap"]), 

76 tract=cast(int, data_id["tract"]), 

77 patch=Index2D(x=patch_record.cell_x, y=patch_record.cell_y), 

78 band=cast(str, data_id.get("band")), 

79 ) 

80 

81 

82@dataclass(frozen=True) 

83class CellIdentifiers(PatchIdentifiers): 

84 """Struct of identifiers for a coadd cell.""" 

85 

86 cell: Index2D 

87 """Identifiers for the cell itself.""" 

88 

89 @classmethod 

90 def from_data_id( # type: ignore [override] 

91 cls, data_id: DataCoordinate, cell: Index2D 

92 ) -> CellIdentifiers: 

93 """Construct from a data ID and a cell index. 

94 

95 Parameters 

96 ---------- 

97 data_id : `~lsst.daf.butler.DataCoordinate` 

98 Fully-expanded data ID that includes the 'patch' dimension and 

99 optionally the `band` dimension. 

100 cell : `~lsst.skymap.Index2D` 

101 Index of the cell within the patch. 

102 

103 Returns 

104 ------- 

105 identifiers : `CellIdentifiers` 

106 Struct of identifiers for this cell within a patch. 

107 """ 

108 patch_record = cast(DimensionRecord, data_id.records["patch"]) 

109 return cls( 

110 skymap=cast(str, data_id["skymap"]), 

111 tract=cast(int, data_id["tract"]), 

112 patch=Index2D(x=patch_record.cell_x, y=patch_record.cell_y), 

113 band=cast(str, data_id.get("band")), 

114 cell=cell, 

115 ) 

116 

117 

118@dataclass(frozen=True) 

119class ObservationIdentifiers: 

120 """Struct of identifiers for an observation that contributed to a coadd 

121 cell. 

122 """ 

123 

124 instrument: str 

125 """Name of the instrument that this observation was taken with. 

126 """ 

127 

128 physical_filter: str 

129 """Name of the physical filter that this observation was taken with. 

130 """ 

131 

132 visit: int 

133 """Unique identifier for the visit. 

134 

135 A visit may be comprised of more than one exposure only if all were 

136 observed back-to-back with no dithers, allowing them to be combined early 

137 the processing with no resampling. All detector-level images in a visit 

138 share the same visit ID. 

139 """ 

140 

141 day_obs: int 

142 """A day and night of observations that rolls over during daylight hours. 

143 The identifier is an decimal integer-concatenated date, i.e. YYYYMMDD, 

144 with the exact rollover time observatory-dependent. 

145 """ 

146 

147 detector: int 

148 """Unique identifier for the detector. 

149 """ 

150 

151 @classmethod 

152 def from_data_id(cls, data_id: DataCoordinate, *, backup_detector: int = -1) -> ObservationIdentifiers: 

153 """Construct from a data ID. 

154 

155 Parameters 

156 ---------- 

157 data_id : `~lsst.daf.butler.DataCoordinate` 

158 Fully-expanded data ID that includes the 'visit', 'detector' and 

159 'day_obs' dimensions. 

160 backup_detector : `int`, optional 

161 Detector ID to use as a backup if not present in ``data_id``. 

162 This is not used if detector information is available in 

163 ``data_id`` and does not override it. 

164 

165 Returns 

166 ------- 

167 identifiers : `ObservationIdentifiers` 

168 Struct of identifiers for this observation. 

169 """ 

170 detector = data_id.get("detector", backup_detector) 

171 day_obs = data_id.get("day_obs", None) 

172 return cls( 

173 instrument=cast(str, data_id["instrument"]), 

174 physical_filter=cast(str, data_id["physical_filter"]), 

175 visit=cast(int, data_id["visit"]), 

176 day_obs=cast(int, day_obs), 

177 detector=cast(int, detector), 

178 ) 

179 

180 def __lt__(self, other: Self, /) -> bool: 

181 return (self.visit, self.detector) < (other.visit, other.detector)