Coverage for tests/test_packer.py: 23%
86 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-12 12:07 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-12 12:07 +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, 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
40class _TestConfig(Config):
41 packer = Instrument.make_dimension_packer_config_field()
44class RubinDimensionPackerTestCase(unittest.TestCase):
45 """Test the custom data ID packer implementation for the main Rubin
46 instruments.
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 """
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)
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.
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))
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)
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 )
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 )
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 )
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)
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)
260 def test_ts3(self):
261 instrument = LsstTS3()
262 instrument.register(self.registry)
263 self.check_old_dimension_packer(instrument, is_exposure=True)
265 def test_ts8(self):
266 instrument = LsstTS8()
267 instrument.register(self.registry)
268 self.check_old_dimension_packer(instrument, is_exposure=True)
270 def test_ucdcam(self):
271 instrument = LsstUCDCam()
272 instrument.register(self.registry)
273 self.check_old_dimension_packer(instrument, is_exposure=True)
276if __name__ == "__main__": 276 ↛ 277line 276 didn't jump to line 277, because the condition on line 276 was never true
277 unittest.main()