Coverage for python/lsst/obs/lsst/instrument.py : 61%

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.instrument import Instrument
31from .filters import LSSTCAM_FILTER_DEFINITIONS, LATISS_FILTER_DEFINITIONS
33from .translators import LatissTranslator, LsstCamTranslator, \
34 LsstUCDCamTranslator, LsstTS3Translator, LsstComCamTranslator, \
35 LsstPhoSimTranslator, LsstTS8Translator, LsstImSimTranslator
37PACKAGE_DIR = getPackageDir("obs_lsst")
40class LsstCam(Instrument):
41 """Gen3 Butler specialization for the LSST Main Camera.
43 Parameters
44 ----------
45 camera : `lsst.cameraGeom.Camera`
46 Camera object from which to extract detector information.
47 filters : `list` of `FilterDefinition`
48 An ordered list of filters to define the set of PhysicalFilters
49 associated with this instrument in the registry.
51 While both the camera geometry and the set of filters associated with a
52 camera are expected to change with time in general, their Butler Registry
53 representations defined by an Instrument do not. Instead:
55 - We only extract names, IDs, and purposes from the detectors in the
56 camera, which should be static information that actually reflects
57 detector "slots" rather than the physical sensors themselves. Because
58 the distinction between physical sensors and slots is unimportant in
59 the vast majority of Butler use cases, we just use "detector" even
60 though the concept really maps better to "detector slot". Ideally in
61 the future this distinction between static and time-dependent
62 information would be encoded in cameraGeom itself (e.g. by making the
63 time-dependent Detector class inherit from a related class that only
64 carries static content).
66 - The Butler Registry is expected to contain physical_filter entries for
67 all filters an instrument has ever had, because we really only care
68 about which filters were used for particular observations, not which
69 filters were *available* at some point in the past. And changes in
70 individual filters over time will be captured as changes in their
71 TransmissionCurve datasets, not changes in the registry content (which
72 is really just a label). While at present Instrument and Registry
73 do not provide a way to add new physical_filters, they will in the
74 future.
75 """
76 filterDefinitions = LSSTCAM_FILTER_DEFINITIONS
77 instrument = "LSSTCam"
78 policyName = "lsstCam"
79 _camera = None
80 _cameraCachedClass = None
81 translatorClass = LsstCamTranslator
82 obsDataPackage = "obs_lsst_data"
84 @property
85 def configPaths(self):
86 return [os.path.join(PACKAGE_DIR, "config"),
87 os.path.join(PACKAGE_DIR, "config", self.policyName)]
89 @classmethod
90 def getName(cls):
91 # Docstring inherited from Instrument.getName
92 return cls.instrument
94 @classmethod
95 def getCamera(cls):
96 # Constructing a YAML camera takes a long time so cache the result
97 # We have to be careful to ensure we cache at the subclass level
98 # since LsstCam base class will look like a cache to the subclasses
99 if cls._camera is None or cls._cameraCachedClass != cls:
100 cameraYamlFile = os.path.join(PACKAGE_DIR, "policy", f"{cls.policyName}.yaml")
101 cls._camera = yamlCamera.makeCamera(cameraYamlFile)
102 cls._cameraCachedClass = cls
103 return cls._camera
105 def getRawFormatter(self, dataId):
106 # Docstring inherited from Instrument.getRawFormatter
107 # local import to prevent circular dependency
108 from .rawFormatter import LsstCamRawFormatter
109 return LsstCamRawFormatter
111 def register(self, registry):
112 # Docstring inherited from Instrument.register
113 # The maximum values below make Gen3's ObservationDataIdPacker produce
114 # outputs that match Gen2's ccdExposureId.
115 obsMax = self.translatorClass.max_detector_exposure_id()
116 registry.insertDimensionData("instrument",
117 {"name": self.getName(),
118 "detector_max": self.translatorClass.DETECTOR_MAX,
119 "visit_max": obsMax,
120 "exposure_max": obsMax,
121 "class_name": getFullTypeName(self),
122 })
124 records = [self.extractDetectorRecord(detector) for detector in self.getCamera()]
125 registry.insertDimensionData("detector", *records)
127 self._registerFilters(registry)
129 def extractDetectorRecord(self, camGeomDetector):
130 """Create a Gen3 Detector entry dict from a cameraGeom.Detector.
131 """
132 # All of the LSST instruments have detector names like R??_S??; we'll
133 # split them up here, and instruments with only one raft can override
134 # to change the group to something else if desired.
135 # Long-term, we should get these fields into cameraGeom separately
136 # so there's no need to specialize at this stage.
137 # They are separate in ObservationInfo
138 group, name = camGeomDetector.getName().split("_")
140 # getType() returns a pybind11-wrapped enum, which unfortunately
141 # has no way to extract the name of just the value (it's always
142 # prefixed by the enum type name).
143 purpose = str(camGeomDetector.getType()).split(".")[-1]
145 return dict(
146 instrument=self.getName(),
147 id=camGeomDetector.getId(),
148 full_name=camGeomDetector.getName(),
149 name_in_raft=name,
150 purpose=purpose,
151 raft=group,
152 )
155class LsstComCam(LsstCam):
156 """Gen3 Butler specialization for ComCam data.
157 """
159 instrument = "LSSTComCam"
160 policyName = "comCam"
161 translatorClass = LsstComCamTranslator
163 def getRawFormatter(self, dataId):
164 # local import to prevent circular dependency
165 from .rawFormatter import LsstComCamRawFormatter
166 return LsstComCamRawFormatter
169class LsstImSim(LsstCam):
170 """Gen3 Butler specialization for ImSim simulations.
171 """
173 instrument = "LSST-ImSim"
174 policyName = "imsim"
175 translatorClass = LsstImSimTranslator
177 def getRawFormatter(self, dataId):
178 # local import to prevent circular dependency
179 from .rawFormatter import LsstImSimRawFormatter
180 return LsstImSimRawFormatter
183class LsstPhoSim(LsstCam):
184 """Gen3 Butler specialization for Phosim simulations.
185 """
187 instrument = "LSST-PhoSim"
188 policyName = "phosim"
189 translatorClass = LsstPhoSimTranslator
191 def getRawFormatter(self, dataId):
192 # local import to prevent circular dependency
193 from .rawFormatter import LsstPhoSimRawFormatter
194 return LsstPhoSimRawFormatter
197class LsstTS8(LsstCam):
198 """Gen3 Butler specialization for raft test stand data.
199 """
201 instrument = "LSST-TS8"
202 policyName = "ts8"
203 translatorClass = LsstTS8Translator
205 def getRawFormatter(self, dataId):
206 # local import to prevent circular dependency
207 from .rawFormatter import LsstTS8RawFormatter
208 return LsstTS8RawFormatter
211class LsstUCDCam(LsstCam):
212 """Gen3 Butler specialization for UCDCam test stand data.
213 """
215 instrument = "LSST-UCDCam"
216 policyName = "ucd"
217 translatorClass = LsstUCDCamTranslator
219 def getRawFormatter(self, dataId):
220 # local import to prevent circular dependency
221 from .rawFormatter import LsstUCDCamRawFormatter
222 return LsstUCDCamRawFormatter
225class LsstTS3(LsstCam):
226 """Gen3 Butler specialization for TS3 test stand data.
227 """
229 instrument = "LSST-TS3"
230 policyName = "ts3"
231 translatorClass = LsstTS3Translator
233 def getRawFormatter(self, dataId):
234 # local import to prevent circular dependency
235 from .rawFormatter import LsstTS3RawFormatter
236 return LsstTS3RawFormatter
239class Latiss(LsstCam):
240 """Gen3 Butler specialization for AuxTel LATISS data.
241 """
242 filterDefinitions = LATISS_FILTER_DEFINITIONS
243 instrument = "LATISS"
244 policyName = "latiss"
245 translatorClass = LatissTranslator
247 def extractDetectorRecord(self, camGeomDetector):
248 # Override to remove group (raft) name, because LATISS only has one
249 # detector.
250 record = super().extractDetectorRecord(camGeomDetector)
251 record["raft"] = None
252 record["name_in_raft"] = record["full_name"]
253 return record
255 def getRawFormatter(self, dataId):
256 # local import to prevent circular dependency
257 from .rawFormatter import LatissRawFormatter
258 return LatissRawFormatter