Hide keyboard shortcuts

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/>. 

21 

22__all__ = ("LsstCam", "LsstImSim", "LsstPhoSim", "LsstTS8", 

23 "Latiss", "LsstTS3", "LsstUCDCam", "LsstComCam") 

24 

25import os.path 

26 

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, 

33 LSST_IMSIM_FILTER_DEFINITIONS, TS3_FILTER_DEFINITIONS, 

34 TS8_FILTER_DEFINITIONS, COMCAM_FILTER_DEFINITIONS, 

35 ) 

36 

37from .translators import LatissTranslator, LsstCamTranslator, \ 

38 LsstUCDCamTranslator, LsstTS3Translator, LsstComCamTranslator, \ 

39 LsstPhoSimTranslator, LsstTS8Translator, LsstImSimTranslator 

40 

41PACKAGE_DIR = getPackageDir("obs_lsst") 

42 

43 

44class LsstCam(Instrument): 

45 """Gen3 Butler specialization for the LSST Main Camera. 

46 

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. 

54 

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: 

58 

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). 

69 

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 

86 @property 

87 def configPaths(self): 

88 return [os.path.join(PACKAGE_DIR, "config"), 

89 os.path.join(PACKAGE_DIR, "config", self.policyName)] 

90 

91 @classmethod 

92 def getName(cls): 

93 # Docstring inherited from Instrument.getName 

94 return cls.instrument 

95 

96 @classmethod 

97 def getCamera(cls): 

98 # Constructing a YAML camera takes a long time but we rely on 

99 # yamlCamera to cache for us. 

100 cameraYamlFile = os.path.join(PACKAGE_DIR, "policy", f"{cls.policyName}.yaml") 

101 camera = yamlCamera.makeCamera(cameraYamlFile) 

102 if camera.getName() != cls.getName(): 

103 raise RuntimeError(f"Expected to read camera geometry for {cls.instrument}" 

104 f" but instead got geometry for {cls._camera.getName()}") 

105 return camera 

106 

107 def getRawFormatter(self, dataId): 

108 # Docstring inherited from Instrument.getRawFormatter 

109 # local import to prevent circular dependency 

110 from .rawFormatter import LsstCamRawFormatter 

111 return LsstCamRawFormatter 

112 

113 def register(self, registry): 

114 # Docstring inherited from Instrument.register 

115 # The maximum values below make Gen3's ObservationDataIdPacker produce 

116 # outputs that match Gen2's ccdExposureId. 

117 obsMax = self.translatorClass.max_detector_exposure_id() 

118 registry.insertDimensionData("instrument", 

119 {"name": self.getName(), 

120 "detector_max": self.translatorClass.DETECTOR_MAX, 

121 "visit_max": obsMax, 

122 "exposure_max": obsMax, 

123 "class_name": getFullTypeName(self), 

124 }) 

125 

126 records = [self.extractDetectorRecord(detector) for detector in self.getCamera()] 

127 registry.insertDimensionData("detector", *records) 

128 

129 self._registerFilters(registry) 

130 

131 def extractDetectorRecord(self, camGeomDetector): 

132 """Create a Gen3 Detector entry dict from a cameraGeom.Detector. 

133 """ 

134 # All of the LSST instruments have detector names like R??_S??; we'll 

135 # split them up here, and instruments with only one raft can override 

136 # to change the group to something else if desired. 

137 # Long-term, we should get these fields into cameraGeom separately 

138 # so there's no need to specialize at this stage. 

139 # They are separate in ObservationInfo 

140 group, name = camGeomDetector.getName().split("_") 

141 

142 # getType() returns a pybind11-wrapped enum, which unfortunately 

143 # has no way to extract the name of just the value (it's always 

144 # prefixed by the enum type name). 

145 purpose = str(camGeomDetector.getType()).split(".")[-1] 

146 

147 return dict( 

148 instrument=self.getName(), 

149 id=camGeomDetector.getId(), 

150 full_name=camGeomDetector.getName(), 

151 name_in_raft=name, 

152 purpose=purpose, 

153 raft=group, 

154 ) 

155 

156 def makeDataIdTranslatorFactory(self) -> TranslatorFactory: 

157 # Docstring inherited from lsst.obs.base.Instrument. 

158 factory = TranslatorFactory() 

159 factory.addGenericInstrumentRules(self.getName(), detectorKey="detector", exposureKey="expId") 

160 return factory 

161 

162 

163class LsstComCam(LsstCam): 

164 """Gen3 Butler specialization for ComCam data. 

165 """ 

166 

167 filterDefinitions = COMCAM_FILTER_DEFINITIONS 

168 instrument = "LSSTComCam" 

169 policyName = "comCam" 

170 translatorClass = LsstComCamTranslator 

171 

172 def getRawFormatter(self, dataId): 

173 # local import to prevent circular dependency 

174 from .rawFormatter import LsstComCamRawFormatter 

175 return LsstComCamRawFormatter 

176 

177 

178class LsstImSim(LsstCam): 

179 """Gen3 Butler specialization for ImSim simulations. 

180 """ 

181 

182 instrument = "LSST-ImSim" 

183 policyName = "imsim" 

184 translatorClass = LsstImSimTranslator 

185 filterDefinitions = LSST_IMSIM_FILTER_DEFINITIONS 

186 

187 def getRawFormatter(self, dataId): 

188 # local import to prevent circular dependency 

189 from .rawFormatter import LsstImSimRawFormatter 

190 return LsstImSimRawFormatter 

191 

192 

193class LsstPhoSim(LsstCam): 

194 """Gen3 Butler specialization for Phosim simulations. 

195 """ 

196 

197 instrument = "LSST-PhoSim" 

198 policyName = "phosim" 

199 translatorClass = LsstPhoSimTranslator 

200 

201 def getRawFormatter(self, dataId): 

202 # local import to prevent circular dependency 

203 from .rawFormatter import LsstPhoSimRawFormatter 

204 return LsstPhoSimRawFormatter 

205 

206 

207class LsstTS8(LsstCam): 

208 """Gen3 Butler specialization for raft test stand data. 

209 """ 

210 

211 filterDefinitions = TS8_FILTER_DEFINITIONS 

212 instrument = "LSST-TS8" 

213 policyName = "ts8" 

214 translatorClass = LsstTS8Translator 

215 

216 def getRawFormatter(self, dataId): 

217 # local import to prevent circular dependency 

218 from .rawFormatter import LsstTS8RawFormatter 

219 return LsstTS8RawFormatter 

220 

221 

222class LsstUCDCam(LsstCam): 

223 """Gen3 Butler specialization for UCDCam test stand data. 

224 """ 

225 

226 instrument = "LSST-UCDCam" 

227 policyName = "ucd" 

228 translatorClass = LsstUCDCamTranslator 

229 

230 def getRawFormatter(self, dataId): 

231 # local import to prevent circular dependency 

232 from .rawFormatter import LsstUCDCamRawFormatter 

233 return LsstUCDCamRawFormatter 

234 

235 

236class LsstTS3(LsstCam): 

237 """Gen3 Butler specialization for TS3 test stand data. 

238 """ 

239 

240 filterDefinitions = TS3_FILTER_DEFINITIONS 

241 instrument = "LSST-TS3" 

242 policyName = "ts3" 

243 translatorClass = LsstTS3Translator 

244 

245 def getRawFormatter(self, dataId): 

246 # local import to prevent circular dependency 

247 from .rawFormatter import LsstTS3RawFormatter 

248 return LsstTS3RawFormatter 

249 

250 

251class Latiss(LsstCam): 

252 """Gen3 Butler specialization for AuxTel LATISS data. 

253 """ 

254 filterDefinitions = LATISS_FILTER_DEFINITIONS 

255 instrument = "LATISS" 

256 policyName = "latiss" 

257 translatorClass = LatissTranslator 

258 

259 def extractDetectorRecord(self, camGeomDetector): 

260 # Override to remove group (raft) name, because LATISS only has one 

261 # detector. 

262 record = super().extractDetectorRecord(camGeomDetector) 

263 record["raft"] = None 

264 record["name_in_raft"] = record["full_name"] 

265 return record 

266 

267 def getRawFormatter(self, dataId): 

268 # local import to prevent circular dependency 

269 from .rawFormatter import LatissRawFormatter 

270 return LatissRawFormatter