Coverage for tests/test_packer.py: 23%

86 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-03 03:58 -0700

1# This file is part of obs_lsst. 

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 

22import unittest 

23 

24from lsst.daf.butler import DataCoordinate, Registry, RegistryConfig 

25from lsst.obs.lsst import ( 

26 Latiss, 

27 LsstCam, 

28 LsstCamImSim, 

29 LsstCamPhoSim, 

30 LsstComCam, 

31 LsstTS3, 

32 LsstTS8, 

33 LsstUCDCam, 

34 RubinDimensionPacker, 

35) 

36from lsst.pex.config import Config 

37from lsst.pipe.base import Instrument, ObservationDimensionPacker 

38 

39 

40class _TestConfig(Config): 

41 packer = Instrument.make_dimension_packer_config_field() 

42 

43 

44class RubinDimensionPackerTestCase(unittest.TestCase): 

45 """Test the custom data ID packer implementation for the main Rubin 

46 instruments. 

47 

48 This test mostly checks the data ID packer's methods for self-consistency, 

49 and that the Instrument-class overrides work as expected. Direct tests of 

50 The packing algorithm is tested against hard-coded values in 

51 test_translators.py, since some translators now delegate to it. 

52 """ 

53 

54 def setUp(self) -> None: 

55 registry_config = RegistryConfig() 

56 registry_config["db"] = "sqlite://" 

57 self.registry = Registry.createFromConfig(registry_config) 

58 self.rubin_packer_instruments = [LsstCam, LsstComCam, Latiss] 

59 self.old_packer_instruments = [ 

60 LsstCamImSim, 

61 LsstCamPhoSim, 

62 LsstTS8, 

63 LsstTS3, 

64 LsstUCDCam, 

65 ] 

66 for cls in self.rubin_packer_instruments + self.old_packer_instruments: 

67 cls().register(self.registry) 

68 

69 def check_rubin_dimension_packer( 

70 self, 

71 instrument: Instrument, 

72 is_exposure: bool, 

73 *, 

74 exposure_id: int, 

75 day_obs: int, 

76 seq_num: int, 

77 detector: int, 

78 controller: str = "O", 

79 is_one_to_one_reinterpretation: bool = False, 

80 visit_id: int | None = None, 

81 ) -> None: 

82 """Run tests on an instrument that uses the new Rubin dimension packer. 

83 

84 Parameters 

85 ---------- 

86 instrument : `lsst.pipe.base.Instrument` 

87 Instrument instance to be tested. 

88 is_exposure : `bool` 

89 `True` to pack ``{detector, exposure}`` data IDs, `False` to pack 

90 ``{detector, visit}`` data IDs. 

91 exposure_id : `int` 

92 Integer data ID. 

93 day_obs : `int` 

94 Date of observations as a YYYYMMDD decimal integer, consistent with 

95 ``exposure_id``. 

96 seq_num : `int` 

97 Instrument sequence number, consistent with ``exposure_id``. 

98 detector : `int` 

99 Integer detector data ID value. 

100 controller : `str`, optional 

101 Controller code consistent with ``exposure_id``. 

102 is_one_to_one_reinterpretatation : `bool`, optional 

103 If `True`, this is a visit ID that represents the alternate 

104 interpretation of that exposure (which must be the first snap in a 

105 multi-snap sequence) as a standalone visit. 

106 visit_id : `int` 

107 Integer visit ID. Must be provided only if 

108 ``is_one_to_one_reinterpretatation=True``; otherwise this is the 

109 same as ``exposure_id``. 

110 """ 

111 if visit_id is None: 

112 assert ( 

113 not is_one_to_one_reinterpretation 

114 ), "Test should not infer visit_id in this case." 

115 visit_id = exposure_id 

116 instrument_data_id = self.registry.expandDataId(instrument=instrument.getName()) 

117 config = _TestConfig() 

118 packer = config.packer.apply(instrument_data_id, is_exposure=is_exposure) 

119 self.assertIsInstance(packer, RubinDimensionPacker) 

120 self.assertEqual(packer.maxBits, 41) 

121 full_data_id = DataCoordinate.standardize( 

122 instrument_data_id, exposure=exposure_id, visit=visit_id, detector=detector 

123 ) 

124 packed1 = RubinDimensionPacker.pack_decomposition( 

125 day_obs, 

126 seq_num, 

127 detector, 

128 controller, 

129 is_one_to_one_reinterpretation=is_one_to_one_reinterpretation, 

130 ) 

131 packed2 = RubinDimensionPacker.pack_id_pair( 

132 exposure_id, 

133 detector, 

134 is_one_to_one_reinterpretation=is_one_to_one_reinterpretation, 

135 ) 

136 packed3 = packer.pack(full_data_id) 

137 self.assertEqual(packed1, packed2) 

138 self.assertEqual(packed1, packed3) 

139 ( 

140 u_day_obs, 

141 u_seq_num, 

142 u_detector, 

143 u_controller, 

144 u_is_one_to_one_reinterpretation, 

145 ) = RubinDimensionPacker.unpack_decomposition(packed1) 

146 self.assertEqual(u_day_obs, day_obs) 

147 self.assertEqual(u_seq_num, seq_num) 

148 self.assertEqual(u_detector, detector) 

149 self.assertEqual(u_controller, controller) 

150 self.assertEqual(u_is_one_to_one_reinterpretation, is_one_to_one_reinterpretation) 

151 ( 

152 u_exposure_id, 

153 u_detector, 

154 u_is_one_to_one_reinterpretation, 

155 ) = RubinDimensionPacker.unpack_id_pair(packed1) 

156 self.assertEqual(u_exposure_id, exposure_id) 

157 self.assertEqual(u_detector, detector) 

158 self.assertEqual(u_is_one_to_one_reinterpretation, is_one_to_one_reinterpretation) 

159 u_data_id = packer.unpack(packed1) 

160 self.assertEqual(u_data_id, full_data_id.subset(packer.dimensions)) 

161 

162 def check_old_dimension_packer( 

163 self, 

164 instrument: Instrument, 

165 is_exposure: bool, 

166 ) -> None: 

167 """Test that an Instrument's default dimension packer is still 

168 `lsst.pipe.base.ObservationDimensionPacker`. 

169 """ 

170 instrument_data_id = self.registry.expandDataId(instrument=instrument.getName()) 

171 config = _TestConfig() 

172 packer = config.packer.apply(instrument_data_id, is_exposure=is_exposure) 

173 # This instrument still uses the pipe_base default dimension packer, 

174 # which is tested there. Nothing more to do here. 

175 self.assertIsInstance(packer, ObservationDimensionPacker) 

176 

177 def test_latiss(self): 

178 instrument = Latiss() 

179 instrument.register(self.registry) 

180 # Input values obtained from: 

181 # $ butler query-dimension-records /repo/main exposure --where \ 

182 # "instrument='LATISS'" --limit 1 

183 self.check_rubin_dimension_packer( 

184 instrument, 

185 is_exposure=True, 

186 exposure_id=2022062800004, 

187 day_obs=20220628, 

188 seq_num=4, 

189 detector=0, 

190 ) 

191 # Input values obtained from: 

192 # $ butler query-dimension-records /repo/main visit --where \ 

193 # "instrument='LATISS'" --limit 1 

194 self.check_rubin_dimension_packer( 

195 instrument, 

196 is_exposure=False, 

197 exposure_id=2021090800749, 

198 day_obs=20210908, 

199 seq_num=749, 

200 detector=0, 

201 ) 

202 # Input data obtained from: 

203 # $ butler query-dimension-records /repo/embargo visit --where \ 

204 # "instrument='LATISS' AND visit_system=0 AND exposure != visit" \ 

205 # --limit 1 

206 self.check_rubin_dimension_packer( 

207 instrument, 

208 is_exposure=False, 

209 exposure_id=2022101101105, 

210 day_obs=20221011, 

211 seq_num=1105, 

212 detector=0, 

213 visit_id=92022101101105, 

214 is_one_to_one_reinterpretation=True, 

215 ) 

216 

217 def test_lsstCam(self): 

218 instrument = LsstCam() 

219 instrument.register(self.registry) 

220 # Input values obtained from: 

221 # $ butler query-dimension-records /repo/main exposure --where \ 

222 # "instrument='LSSTCam'" --limit 1 

223 self.check_rubin_dimension_packer( 

224 instrument, 

225 is_exposure=True, 

226 exposure_id=3021121400075, 

227 day_obs=20211214, 

228 seq_num=75, 

229 detector=150, 

230 controller="C", 

231 ) 

232 

233 def test_comCam(self): 

234 instrument = LsstComCam() 

235 instrument.register(self.registry) 

236 # Input values obtained from: 

237 # $ butler query-dimension-records /repo/main exposure --where \ 

238 # "instrument='LSSTComCam'" --limit 1 

239 self.check_rubin_dimension_packer( 

240 instrument, 

241 is_exposure=True, 

242 exposure_id=2020091100004, 

243 day_obs=20200911, 

244 seq_num=4, 

245 detector=5, 

246 ) 

247 

248 def test_imsim(self): 

249 instrument = LsstCamImSim() 

250 instrument.register(self.registry) 

251 self.check_old_dimension_packer(instrument, is_exposure=True) 

252 self.check_old_dimension_packer(instrument, is_exposure=False) 

253 

254 def test_phosim(self): 

255 instrument = LsstCamPhoSim() 

256 instrument.register(self.registry) 

257 self.check_old_dimension_packer(instrument, is_exposure=True) 

258 self.check_old_dimension_packer(instrument, is_exposure=False) 

259 

260 def test_ts3(self): 

261 instrument = LsstTS3() 

262 instrument.register(self.registry) 

263 self.check_old_dimension_packer(instrument, is_exposure=True) 

264 

265 def test_ts8(self): 

266 instrument = LsstTS8() 

267 instrument.register(self.registry) 

268 self.check_old_dimension_packer(instrument, is_exposure=True) 

269 

270 def test_ucdcam(self): 

271 instrument = LsstUCDCam() 

272 instrument.register(self.registry) 

273 self.check_old_dimension_packer(instrument, is_exposure=True) 

274 

275 

276if __name__ == "__main__": 276 ↛ 277line 276 didn't jump to line 277, because the condition on line 276 was never true

277 unittest.main()