Coverage for python/lsst/obs/lsst/_instrument.py : 59%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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", "LsstImSim", "LsstPhoSim", "LsstTS8",
23 "Latiss", "LsstTS3", "LsstUCDCam", "LsstComCam")
25import os.path
27import lsst.obs.base.yamlCamera as yamlCamera
28from lsst.daf.butler.core.utils import getFullTypeName
29from lsst.utils import getPackageDir
30from lsst.obs.base import Instrument
31from lsst.obs.base.gen2to3 import TranslatorFactory
32from .filters import LSSTCAM_FILTER_DEFINITIONS, LATISS_FILTER_DEFINITIONS
34from .translators import LatissTranslator, LsstCamTranslator, \
35 LsstUCDCamTranslator, LsstTS3Translator, LsstComCamTranslator, \
36 LsstPhoSimTranslator, LsstTS8Translator, LsstImSimTranslator
38PACKAGE_DIR = getPackageDir("obs_lsst")
41class LsstCam(Instrument):
42 """Gen3 Butler specialization for the LSST Main Camera.
44 Parameters
45 ----------
46 camera : `lsst.cameraGeom.Camera`
47 Camera object from which to extract detector information.
48 filters : `list` of `FilterDefinition`
49 An ordered list of filters to define the set of PhysicalFilters
50 associated with this instrument in the registry.
52 While both the camera geometry and the set of filters associated with a
53 camera are expected to change with time in general, their Butler Registry
54 representations defined by an Instrument do not. Instead:
56 - We only extract names, IDs, and purposes from the detectors in the
57 camera, which should be static information that actually reflects
58 detector "slots" rather than the physical sensors themselves. Because
59 the distinction between physical sensors and slots is unimportant in
60 the vast majority of Butler use cases, we just use "detector" even
61 though the concept really maps better to "detector slot". Ideally in
62 the future this distinction between static and time-dependent
63 information would be encoded in cameraGeom itself (e.g. by making the
64 time-dependent Detector class inherit from a related class that only
65 carries static content).
67 - The Butler Registry is expected to contain physical_filter entries for
68 all filters an instrument has ever had, because we really only care
69 about which filters were used for particular observations, not which
70 filters were *available* at some point in the past. And changes in
71 individual filters over time will be captured as changes in their
72 TransmissionCurve datasets, not changes in the registry content (which
73 is really just a label). While at present Instrument and Registry
74 do not provide a way to add new physical_filters, they will in the
75 future.
76 """
77 filterDefinitions = LSSTCAM_FILTER_DEFINITIONS
78 instrument = "LSSTCam"
79 policyName = "lsstCam"
80 translatorClass = LsstCamTranslator
81 obsDataPackage = "obs_lsst_data"
83 @property
84 def configPaths(self):
85 return [os.path.join(PACKAGE_DIR, "config"),
86 os.path.join(PACKAGE_DIR, "config", self.policyName)]
88 @classmethod
89 def getName(cls):
90 # Docstring inherited from Instrument.getName
91 return cls.instrument
93 @classmethod
94 def getCamera(cls):
95 # Constructing a YAML camera takes a long time but we rely on
96 # yamlCamera to cache for us.
97 cameraYamlFile = os.path.join(PACKAGE_DIR, "policy", f"{cls.policyName}.yaml")
98 camera = yamlCamera.makeCamera(cameraYamlFile)
99 if camera.getName() != cls.getName():
100 raise RuntimeError(f"Expected to read camera geometry for {cls.instrument}"
101 f" but instead got geometry for {cls._camera.getName()}")
102 return camera
104 def getRawFormatter(self, dataId):
105 # Docstring inherited from Instrument.getRawFormatter
106 # local import to prevent circular dependency
107 from .rawFormatter import LsstCamRawFormatter
108 return LsstCamRawFormatter
110 def register(self, registry):
111 # Docstring inherited from Instrument.register
112 # The maximum values below make Gen3's ObservationDataIdPacker produce
113 # outputs that match Gen2's ccdExposureId.
114 obsMax = self.translatorClass.max_detector_exposure_id()
115 registry.insertDimensionData("instrument",
116 {"name": self.getName(),
117 "detector_max": self.translatorClass.DETECTOR_MAX,
118 "visit_max": obsMax,
119 "exposure_max": obsMax,
120 "class_name": getFullTypeName(self),
121 })
123 records = [self.extractDetectorRecord(detector) for detector in self.getCamera()]
124 registry.insertDimensionData("detector", *records)
126 self._registerFilters(registry)
128 def extractDetectorRecord(self, camGeomDetector):
129 """Create a Gen3 Detector entry dict from a cameraGeom.Detector.
130 """
131 # All of the LSST instruments have detector names like R??_S??; we'll
132 # split them up here, and instruments with only one raft can override
133 # to change the group to something else if desired.
134 # Long-term, we should get these fields into cameraGeom separately
135 # so there's no need to specialize at this stage.
136 # They are separate in ObservationInfo
137 group, name = camGeomDetector.getName().split("_")
139 # getType() returns a pybind11-wrapped enum, which unfortunately
140 # has no way to extract the name of just the value (it's always
141 # prefixed by the enum type name).
142 purpose = str(camGeomDetector.getType()).split(".")[-1]
144 return dict(
145 instrument=self.getName(),
146 id=camGeomDetector.getId(),
147 full_name=camGeomDetector.getName(),
148 name_in_raft=name,
149 purpose=purpose,
150 raft=group,
151 )
153 def makeDataIdTranslatorFactory(self) -> TranslatorFactory:
154 # Docstring inherited from lsst.obs.base.Instrument.
155 factory = TranslatorFactory()
156 factory.addGenericInstrumentRules(self.getName(), detectorKey="detector", exposureKey="expId")
157 return factory
160class LsstComCam(LsstCam):
161 """Gen3 Butler specialization for ComCam data.
162 """
164 instrument = "LSSTComCam"
165 policyName = "comCam"
166 translatorClass = LsstComCamTranslator
168 def getRawFormatter(self, dataId):
169 # local import to prevent circular dependency
170 from .rawFormatter import LsstComCamRawFormatter
171 return LsstComCamRawFormatter
174class LsstImSim(LsstCam):
175 """Gen3 Butler specialization for ImSim simulations.
176 """
178 instrument = "LSST-ImSim"
179 policyName = "imsim"
180 translatorClass = LsstImSimTranslator
182 def getRawFormatter(self, dataId):
183 # local import to prevent circular dependency
184 from .rawFormatter import LsstImSimRawFormatter
185 return LsstImSimRawFormatter
188class LsstPhoSim(LsstCam):
189 """Gen3 Butler specialization for Phosim simulations.
190 """
192 instrument = "LSST-PhoSim"
193 policyName = "phosim"
194 translatorClass = LsstPhoSimTranslator
196 def getRawFormatter(self, dataId):
197 # local import to prevent circular dependency
198 from .rawFormatter import LsstPhoSimRawFormatter
199 return LsstPhoSimRawFormatter
202class LsstTS8(LsstCam):
203 """Gen3 Butler specialization for raft test stand data.
204 """
206 instrument = "LSST-TS8"
207 policyName = "ts8"
208 translatorClass = LsstTS8Translator
210 def getRawFormatter(self, dataId):
211 # local import to prevent circular dependency
212 from .rawFormatter import LsstTS8RawFormatter
213 return LsstTS8RawFormatter
216class LsstUCDCam(LsstCam):
217 """Gen3 Butler specialization for UCDCam test stand data.
218 """
220 instrument = "LSST-UCDCam"
221 policyName = "ucd"
222 translatorClass = LsstUCDCamTranslator
224 def getRawFormatter(self, dataId):
225 # local import to prevent circular dependency
226 from .rawFormatter import LsstUCDCamRawFormatter
227 return LsstUCDCamRawFormatter
230class LsstTS3(LsstCam):
231 """Gen3 Butler specialization for TS3 test stand data.
232 """
234 instrument = "LSST-TS3"
235 policyName = "ts3"
236 translatorClass = LsstTS3Translator
238 def getRawFormatter(self, dataId):
239 # local import to prevent circular dependency
240 from .rawFormatter import LsstTS3RawFormatter
241 return LsstTS3RawFormatter
244class Latiss(LsstCam):
245 """Gen3 Butler specialization for AuxTel LATISS data.
246 """
247 filterDefinitions = LATISS_FILTER_DEFINITIONS
248 instrument = "LATISS"
249 policyName = "latiss"
250 translatorClass = LatissTranslator
252 def extractDetectorRecord(self, camGeomDetector):
253 # Override to remove group (raft) name, because LATISS only has one
254 # detector.
255 record = super().extractDetectorRecord(camGeomDetector)
256 record["raft"] = None
257 record["name_in_raft"] = record["full_name"]
258 return record
260 def getRawFormatter(self, dataId):
261 # local import to prevent circular dependency
262 from .rawFormatter import LatissRawFormatter
263 return LatissRawFormatter