Coverage for python/lsst/obs/lsst/_instrument.py: 66%
112 statements
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-09 03:52 -0700
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-09 03:52 -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/>.
22__all__ = ("LsstCam", "LsstCamImSim", "LsstCamPhoSim", "LsstTS8",
23 "Latiss", "LsstTS3", "LsstUCDCam", "LsstComCam")
25import os.path
27import lsst.obs.base.yamlCamera as yamlCamera
28from lsst.utils.introspection import get_full_type_name
29from lsst.utils import getPackageDir
30from lsst.obs.base import Instrument, VisitSystem
31from lsst.obs.base.gen2to3 import TranslatorFactory
32from .filters import (LSSTCAM_FILTER_DEFINITIONS, LATISS_FILTER_DEFINITIONS,
33 LSSTCAM_IMSIM_FILTER_DEFINITIONS, TS3_FILTER_DEFINITIONS,
34 TS8_FILTER_DEFINITIONS, COMCAM_FILTER_DEFINITIONS,
35 )
37from .translators import LatissTranslator, LsstCamTranslator, \
38 LsstUCDCamTranslator, LsstTS3Translator, LsstComCamTranslator, \
39 LsstCamPhoSimTranslator, LsstTS8Translator, LsstCamImSimTranslator
41PACKAGE_DIR = getPackageDir("obs_lsst")
44class LsstCam(Instrument):
45 """Gen3 Butler specialization for the LSST Main Camera.
47 Parameters
48 ----------
49 camera : `lsst.cameraGeom.Camera`
50 Camera object from which to extract detector information.
51 filters : `list` of `FilterDefinition`
52 An ordered list of filters to define the set of PhysicalFilters
53 associated with this instrument in the registry.
55 While both the camera geometry and the set of filters associated with a
56 camera are expected to change with time in general, their Butler Registry
57 representations defined by an Instrument do not. Instead:
59 - We only extract names, IDs, and purposes from the detectors in the
60 camera, which should be static information that actually reflects
61 detector "slots" rather than the physical sensors themselves. Because
62 the distinction between physical sensors and slots is unimportant in
63 the vast majority of Butler use cases, we just use "detector" even
64 though the concept really maps better to "detector slot". Ideally in
65 the future this distinction between static and time-dependent
66 information would be encoded in cameraGeom itself (e.g. by making the
67 time-dependent Detector class inherit from a related class that only
68 carries static content).
70 - The Butler Registry is expected to contain physical_filter entries for
71 all filters an instrument has ever had, because we really only care
72 about which filters were used for particular observations, not which
73 filters were *available* at some point in the past. And changes in
74 individual filters over time will be captured as changes in their
75 TransmissionCurve datasets, not changes in the registry content (which
76 is really just a label). While at present Instrument and Registry
77 do not provide a way to add new physical_filters, they will in the
78 future.
79 """
80 filterDefinitions = LSSTCAM_FILTER_DEFINITIONS
81 instrument = "LSSTCam"
82 policyName = "lsstCam"
83 translatorClass = LsstCamTranslator
84 obsDataPackage = "obs_lsst_data"
85 visitSystem = VisitSystem.BY_SEQ_START_END
87 @property
88 def configPaths(self):
89 return [os.path.join(PACKAGE_DIR, "config"),
90 os.path.join(PACKAGE_DIR, "config", self.policyName)]
92 @classmethod
93 def getName(cls):
94 # Docstring inherited from Instrument.getName
95 return cls.instrument
97 @classmethod
98 def getCamera(cls):
99 # Constructing a YAML camera takes a long time but we rely on
100 # yamlCamera to cache for us.
101 cameraYamlFile = os.path.join(PACKAGE_DIR, "policy", f"{cls.policyName}.yaml")
102 camera = yamlCamera.makeCamera(cameraYamlFile)
103 if camera.getName() != cls.getName():
104 raise RuntimeError(f"Expected to read camera geometry for {cls.instrument}"
105 f" but instead got geometry for {camera.getName()}")
106 return camera
108 def getRawFormatter(self, dataId):
109 # Docstring inherited from Instrument.getRawFormatter
110 # local import to prevent circular dependency
111 from .rawFormatter import LsstCamRawFormatter
112 return LsstCamRawFormatter
114 def register(self, registry, update=False):
115 # Docstring inherited from Instrument.register
116 # The maximum values below make Gen3's ObservationDataIdPacker produce
117 # outputs that match Gen2's ccdExposureId.
118 obsMax = self.translatorClass.max_exposure_id()
119 with registry.transaction():
120 registry.syncDimensionData(
121 "instrument",
122 {
123 "name": self.getName(),
124 "detector_max": self.translatorClass.DETECTOR_MAX,
125 "visit_max": obsMax,
126 "exposure_max": obsMax,
127 "class_name": get_full_type_name(self),
128 "visit_system": self.visitSystem.value,
129 },
130 update=update
131 )
132 for detector in self.getCamera():
133 registry.syncDimensionData("detector", self.extractDetectorRecord(detector), update=update)
135 self._registerFilters(registry, update=update)
137 def extractDetectorRecord(self, camGeomDetector):
138 """Create a Gen3 Detector entry dict from a cameraGeom.Detector.
139 """
140 # All of the LSST instruments have detector names like R??_S??; we'll
141 # split them up here, and instruments with only one raft can override
142 # to change the group to something else if desired.
143 # Long-term, we should get these fields into cameraGeom separately
144 # so there's no need to specialize at this stage.
145 # They are separate in ObservationInfo
146 group, name = camGeomDetector.getName().split("_")
148 # getType() returns a pybind11-wrapped enum, which unfortunately
149 # has no way to extract the name of just the value (it's always
150 # prefixed by the enum type name).
151 purpose = str(camGeomDetector.getType()).split(".")[-1]
153 return dict(
154 instrument=self.getName(),
155 id=camGeomDetector.getId(),
156 full_name=camGeomDetector.getName(),
157 name_in_raft=name,
158 purpose=purpose,
159 raft=group,
160 )
162 def makeDataIdTranslatorFactory(self) -> TranslatorFactory:
163 # Docstring inherited from lsst.obs.base.Instrument.
164 factory = TranslatorFactory()
165 factory.addGenericInstrumentRules(self.getName(), detectorKey="detector", exposureKey="expId")
166 return factory
169class LsstComCam(LsstCam):
170 """Gen3 Butler specialization for ComCam data.
171 """
173 filterDefinitions = COMCAM_FILTER_DEFINITIONS
174 instrument = "LSSTComCam"
175 policyName = "comCam"
176 translatorClass = LsstComCamTranslator
178 def getRawFormatter(self, dataId):
179 # local import to prevent circular dependency
180 from .rawFormatter import LsstComCamRawFormatter
181 return LsstComCamRawFormatter
184class LsstCamImSim(LsstCam):
185 """Gen3 Butler specialization for ImSim simulations.
186 """
188 instrument = "LSSTCam-imSim"
189 policyName = "imsim"
190 translatorClass = LsstCamImSimTranslator
191 filterDefinitions = LSSTCAM_IMSIM_FILTER_DEFINITIONS
192 visitSystem = VisitSystem.ONE_TO_ONE
194 def getRawFormatter(self, dataId):
195 # local import to prevent circular dependency
196 from .rawFormatter import LsstCamImSimRawFormatter
197 return LsstCamImSimRawFormatter
200class LsstCamPhoSim(LsstCam):
201 """Gen3 Butler specialization for Phosim simulations.
202 """
204 instrument = "LSSTCam-PhoSim"
205 policyName = "phosim"
206 translatorClass = LsstCamPhoSimTranslator
207 visitSystem = VisitSystem.ONE_TO_ONE
209 def getRawFormatter(self, dataId):
210 # local import to prevent circular dependency
211 from .rawFormatter import LsstCamPhoSimRawFormatter
212 return LsstCamPhoSimRawFormatter
215class LsstTS8(LsstCam):
216 """Gen3 Butler specialization for raft test stand data.
217 """
219 filterDefinitions = TS8_FILTER_DEFINITIONS
220 instrument = "LSST-TS8"
221 policyName = "ts8"
222 translatorClass = LsstTS8Translator
223 visitSystem = VisitSystem.ONE_TO_ONE
225 def getRawFormatter(self, dataId):
226 # local import to prevent circular dependency
227 from .rawFormatter import LsstTS8RawFormatter
228 return LsstTS8RawFormatter
231class LsstUCDCam(LsstCam):
232 """Gen3 Butler specialization for UCDCam test stand data.
233 """
235 instrument = "LSST-UCDCam"
236 policyName = "ucd"
237 translatorClass = LsstUCDCamTranslator
238 visitSystem = VisitSystem.ONE_TO_ONE
240 def getRawFormatter(self, dataId):
241 # local import to prevent circular dependency
242 from .rawFormatter import LsstUCDCamRawFormatter
243 return LsstUCDCamRawFormatter
246class LsstTS3(LsstCam):
247 """Gen3 Butler specialization for TS3 test stand data.
248 """
250 filterDefinitions = TS3_FILTER_DEFINITIONS
251 instrument = "LSST-TS3"
252 policyName = "ts3"
253 translatorClass = LsstTS3Translator
254 visitSystem = VisitSystem.ONE_TO_ONE
256 def getRawFormatter(self, dataId):
257 # local import to prevent circular dependency
258 from .rawFormatter import LsstTS3RawFormatter
259 return LsstTS3RawFormatter
262class Latiss(LsstCam):
263 """Gen3 Butler specialization for AuxTel LATISS data.
264 """
265 filterDefinitions = LATISS_FILTER_DEFINITIONS
266 instrument = "LATISS"
267 policyName = "latiss"
268 translatorClass = LatissTranslator
270 def extractDetectorRecord(self, camGeomDetector):
271 # Override to remove group (raft) name, because LATISS only has one
272 # detector.
273 record = super().extractDetectorRecord(camGeomDetector)
274 record["raft"] = None
275 record["name_in_raft"] = record["full_name"]
276 return record
278 def getRawFormatter(self, dataId):
279 # local import to prevent circular dependency
280 from .rawFormatter import LatissRawFormatter
281 return LatissRawFormatter