Coverage for tests/test_packer.py: 24%
91 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-11 11:03 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-11 11:03 +0000
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/>.
22import unittest
24from lsst.daf.butler import DataCoordinate, RegistryConfig
25from lsst.daf.butler.registry.sql_registry import SqlRegistry
26from lsst.obs.lsst import (
27 Latiss,
28 LsstCam,
29 LsstCamImSim,
30 LsstCamPhoSim,
31 LsstComCam,
32 LsstComCamSim,
33 LsstTS3,
34 LsstTS8,
35 LsstUCDCam,
36 RubinDimensionPacker,
37)
38from lsst.pex.config import Config
39from lsst.pipe.base import Instrument, ObservationDimensionPacker
42class _TestConfig(Config):
43 packer = Instrument.make_dimension_packer_config_field()
46class RubinDimensionPackerTestCase(unittest.TestCase):
47 """Test the custom data ID packer implementation for the main Rubin
48 instruments.
50 This test mostly checks the data ID packer's methods for self-consistency,
51 and that the Instrument-class overrides work as expected. Direct tests of
52 The packing algorithm is tested against hard-coded values in
53 test_translators.py, since some translators now delegate to it.
54 """
56 def setUp(self) -> None:
57 registry_config = RegistryConfig()
58 registry_config["db"] = "sqlite://"
59 self.registry = SqlRegistry.createFromConfig(registry_config)
60 self.rubin_packer_instruments = [LsstCam, LsstComCam, LsstComCamSim,
61 Latiss]
62 self.old_packer_instruments = [
63 LsstCamImSim,
64 LsstCamPhoSim,
65 LsstTS8,
66 LsstTS3,
67 LsstUCDCam,
68 ]
69 for cls in self.rubin_packer_instruments + self.old_packer_instruments:
70 cls().register(self.registry)
72 def check_rubin_dimension_packer(
73 self,
74 instrument: Instrument,
75 is_exposure: bool,
76 *,
77 exposure_id: int,
78 day_obs: int,
79 seq_num: int,
80 detector: int,
81 controller: str = "O",
82 is_one_to_one_reinterpretation: bool = False,
83 visit_id: int | None = None,
84 ) -> None:
85 """Run tests on an instrument that uses the new Rubin dimension packer.
87 Parameters
88 ----------
89 instrument : `lsst.pipe.base.Instrument`
90 Instrument instance to be tested.
91 is_exposure : `bool`
92 `True` to pack ``{detector, exposure}`` data IDs, `False` to pack
93 ``{detector, visit}`` data IDs.
94 exposure_id : `int`
95 Integer data ID.
96 day_obs : `int`
97 Date of observations as a YYYYMMDD decimal integer, consistent with
98 ``exposure_id``.
99 seq_num : `int`
100 Instrument sequence number, consistent with ``exposure_id``.
101 detector : `int`
102 Integer detector data ID value.
103 controller : `str`, optional
104 Controller code consistent with ``exposure_id``.
105 is_one_to_one_reinterpretation : `bool`, optional
106 If `True`, this is a visit ID that represents the alternate
107 interpretation of that exposure (which must be the first snap in a
108 multi-snap sequence) as a standalone visit.
109 visit_id : `int`
110 Integer visit ID. Must be provided only if
111 ``is_one_to_one_reinterpretatation=True``; otherwise this is the
112 same as ``exposure_id``.
113 """
114 if visit_id is None:
115 assert (
116 not is_one_to_one_reinterpretation
117 ), "Test should not infer visit_id in this case."
118 visit_id = exposure_id
119 instrument_data_id = self.registry.expandDataId(instrument=instrument.getName())
120 config = _TestConfig()
121 packer = config.packer.apply(instrument_data_id, is_exposure=is_exposure)
122 self.assertIsInstance(packer, RubinDimensionPacker)
123 self.assertEqual(packer.maxBits, 41)
124 full_data_id = DataCoordinate.standardize(
125 instrument_data_id, exposure=exposure_id, visit=visit_id, detector=detector
126 )
127 packed1 = RubinDimensionPacker.pack_decomposition(
128 day_obs,
129 seq_num,
130 detector,
131 controller,
132 is_one_to_one_reinterpretation=is_one_to_one_reinterpretation,
133 )
134 packed2 = RubinDimensionPacker.pack_id_pair(
135 exposure_id,
136 detector,
137 is_one_to_one_reinterpretation=is_one_to_one_reinterpretation,
138 )
139 packed3 = packer.pack(full_data_id)
140 self.assertEqual(packed1, packed2)
141 self.assertEqual(packed1, packed3)
142 (
143 u_day_obs,
144 u_seq_num,
145 u_detector,
146 u_controller,
147 u_is_one_to_one_reinterpretation,
148 ) = RubinDimensionPacker.unpack_decomposition(packed1)
149 self.assertEqual(u_day_obs, day_obs)
150 self.assertEqual(u_seq_num, seq_num)
151 self.assertEqual(u_detector, detector)
152 self.assertEqual(u_controller, controller)
153 self.assertEqual(u_is_one_to_one_reinterpretation, is_one_to_one_reinterpretation)
154 (
155 u_exposure_id,
156 u_detector,
157 u_is_one_to_one_reinterpretation,
158 ) = RubinDimensionPacker.unpack_id_pair(packed1)
159 self.assertEqual(u_exposure_id, exposure_id)
160 self.assertEqual(u_detector, detector)
161 self.assertEqual(u_is_one_to_one_reinterpretation, is_one_to_one_reinterpretation)
162 u_data_id = packer.unpack(packed1)
163 self.assertEqual(u_data_id, full_data_id.subset(packer.dimensions))
165 def check_old_dimension_packer(
166 self,
167 instrument: Instrument,
168 is_exposure: bool,
169 ) -> None:
170 """Test that an Instrument's default dimension packer is still
171 `lsst.pipe.base.ObservationDimensionPacker`.
172 """
173 instrument_data_id = self.registry.expandDataId(instrument=instrument.getName())
174 config = _TestConfig()
175 packer = config.packer.apply(instrument_data_id, is_exposure=is_exposure)
176 # This instrument still uses the pipe_base default dimension packer,
177 # which is tested there. Nothing more to do here.
178 self.assertIsInstance(packer, ObservationDimensionPacker)
180 def test_latiss(self):
181 instrument = Latiss()
182 instrument.register(self.registry)
183 # Input values obtained from:
184 # $ butler query-dimension-records /repo/main exposure --where \
185 # "instrument='LATISS'" --limit 1
186 self.check_rubin_dimension_packer(
187 instrument,
188 is_exposure=True,
189 exposure_id=2022062800004,
190 day_obs=20220628,
191 seq_num=4,
192 detector=0,
193 )
194 # Input values obtained from:
195 # $ butler query-dimension-records /repo/main visit --where \
196 # "instrument='LATISS'" --limit 1
197 self.check_rubin_dimension_packer(
198 instrument,
199 is_exposure=False,
200 exposure_id=2021090800749,
201 day_obs=20210908,
202 seq_num=749,
203 detector=0,
204 )
205 # Input data obtained from:
206 # $ butler query-dimension-records /repo/embargo visit --where \
207 # "instrument='LATISS' AND visit_system=0 AND exposure != visit" \
208 # --limit 1
209 self.check_rubin_dimension_packer(
210 instrument,
211 is_exposure=False,
212 exposure_id=2022101101105,
213 day_obs=20221011,
214 seq_num=1105,
215 detector=0,
216 visit_id=92022101101105,
217 is_one_to_one_reinterpretation=True,
218 )
220 def test_lsstCam(self):
221 instrument = LsstCam()
222 instrument.register(self.registry)
223 # Input values obtained from:
224 # $ butler query-dimension-records /repo/main exposure --where \
225 # "instrument='LSSTCam'" --limit 1
226 self.check_rubin_dimension_packer(
227 instrument,
228 is_exposure=True,
229 exposure_id=3021121400075,
230 day_obs=20211214,
231 seq_num=75,
232 detector=150,
233 controller="C",
234 )
236 def test_comCam(self):
237 instrument = LsstComCam()
238 instrument.register(self.registry)
239 # Input values obtained from:
240 # $ butler query-dimension-records /repo/main exposure --where \
241 # "instrument='LSSTComCam'" --limit 1
242 self.check_rubin_dimension_packer(
243 instrument,
244 is_exposure=True,
245 exposure_id=2020091100004,
246 day_obs=20200911,
247 seq_num=4,
248 detector=5,
249 )
251 def test_comCamSim(self):
252 instrument = LsstComCamSim()
253 instrument.register(self.registry)
254 # Input values obtained from:
255 # $ butler query-dimension-records data/input/comCamSim exposure \
256 # --where "instrument='LSSTComCamSim'" --limit 1
257 self.check_rubin_dimension_packer(
258 instrument,
259 is_exposure=False,
260 exposure_id=7024032100720,
261 day_obs=20240321,
262 seq_num=720,
263 detector=4,
264 controller="S"
265 )
267 def test_imsim(self):
268 instrument = LsstCamImSim()
269 instrument.register(self.registry)
270 self.check_old_dimension_packer(instrument, is_exposure=True)
271 self.check_old_dimension_packer(instrument, is_exposure=False)
273 def test_phosim(self):
274 instrument = LsstCamPhoSim()
275 instrument.register(self.registry)
276 self.check_old_dimension_packer(instrument, is_exposure=True)
277 self.check_old_dimension_packer(instrument, is_exposure=False)
279 def test_ts3(self):
280 instrument = LsstTS3()
281 instrument.register(self.registry)
282 self.check_old_dimension_packer(instrument, is_exposure=True)
284 def test_ts8(self):
285 instrument = LsstTS8()
286 instrument.register(self.registry)
287 self.check_old_dimension_packer(instrument, is_exposure=True)
289 def test_ucdcam(self):
290 instrument = LsstUCDCam()
291 instrument.register(self.registry)
292 self.check_old_dimension_packer(instrument, is_exposure=True)
295if __name__ == "__main__": 295 ↛ 296line 295 didn't jump to line 296, because the condition on line 295 was never true
296 unittest.main()