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