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.utils import getPackageDir 

29from lsst.obs.base.instrument import Instrument 

30from .filters import LSSTCAM_FILTER_DEFINITIONS, LATISS_FILTER_DEFINITIONS 

31 

32from .translators import LatissTranslator, LsstCamTranslator, \ 

33 LsstUCDCamTranslator, LsstTS3Translator, LsstComCamTranslator, \ 

34 LsstPhoSimTranslator, LsstTS8Translator, LsstImSimTranslator 

35 

36PACKAGE_DIR = getPackageDir("obs_lsst") 

37 

38 

39class LsstCam(Instrument): 

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

41 

42 Parameters 

43 ---------- 

44 camera : `lsst.cameraGeom.Camera` 

45 Camera object from which to extract detector information. 

46 filters : `list` of `FilterDefinition` 

47 An ordered list of filters to define the set of PhysicalFilters 

48 associated with this instrument in the registry. 

49 

50 While both the camera geometry and the set of filters associated with a 

51 camera are expected to change with time in general, their Butler Registry 

52 representations defined by an Instrument do not. Instead: 

53 

54 - We only extract names, IDs, and purposes from the detectors in the 

55 camera, which should be static information that actually reflects 

56 detector "slots" rather than the physical sensors themselves. Because 

57 the distinction between physical sensors and slots is unimportant in 

58 the vast majority of Butler use cases, we just use "detector" even 

59 though the concept really maps better to "detector slot". Ideally in 

60 the future this distinction between static and time-dependent 

61 information would be encoded in cameraGeom itself (e.g. by making the 

62 time-dependent Detector class inherit from a related class that only 

63 carries static content). 

64 

65 - The Butler Registry is expected to contain physical_filter entries for 

66 all filters an instrument has ever had, because we really only care 

67 about which filters were used for particular observations, not which 

68 filters were *available* at some point in the past. And changes in 

69 individual filters over time will be captured as changes in their 

70 TransmissionCurve datasets, not changes in the registry content (which 

71 is really just a label). While at present Instrument and Registry 

72 do not provide a way to add new physical_filters, they will in the 

73 future. 

74 """ 

75 filterDefinitions = LSSTCAM_FILTER_DEFINITIONS 

76 instrument = "LSSTCam" 

77 policyName = "lsstCam" 

78 _camera = None 

79 _cameraCachedClass = None 

80 translatorClass = LsstCamTranslator 

81 obsDataPackage = "obs_lsst_data" 

82 

83 @property 

84 def configPaths(self): 

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

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

87 

88 @classmethod 

89 def getName(cls): 

90 # Docstring inherited from Instrument.getName 

91 return cls.instrument 

92 

93 @classmethod 

94 def getCamera(cls): 

95 # Constructing a YAML camera takes a long time so cache the result 

96 # We have to be careful to ensure we cache at the subclass level 

97 # since LsstCam base class will look like a cache to the subclasses 

98 if cls._camera is None or cls._cameraCachedClass != cls: 

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

100 cls._camera = yamlCamera.makeCamera(cameraYamlFile) 

101 cls._cameraCachedClass = cls 

102 return cls._camera 

103 

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 

109 

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 

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

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

123 

124 self._registerFilters(registry) 

125 

126 def extractDetectorRecord(self, camGeomDetector): 

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

128 """ 

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

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

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

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

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

134 # They are separate in ObservationInfo 

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

136 

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

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

139 # prefixed by the enum type name). 

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

141 

142 return dict( 

143 instrument=self.getName(), 

144 id=camGeomDetector.getId(), 

145 full_name=camGeomDetector.getName(), 

146 name_in_raft=name, 

147 purpose=purpose, 

148 raft=group, 

149 ) 

150 

151 

152class LsstComCam(LsstCam): 

153 """Gen3 Butler specialization for ComCam data. 

154 """ 

155 

156 instrument = "LSSTComCam" 

157 policyName = "comCam" 

158 translatorClass = LsstComCamTranslator 

159 

160 def getRawFormatter(self, dataId): 

161 # local import to prevent circular dependency 

162 from .rawFormatter import LsstComCamRawFormatter 

163 return LsstComCamRawFormatter 

164 

165 

166class LsstImSim(LsstCam): 

167 """Gen3 Butler specialization for ImSim simulations. 

168 """ 

169 

170 instrument = "LSST-ImSim" 

171 policyName = "imsim" 

172 translatorClass = LsstImSimTranslator 

173 

174 def getRawFormatter(self, dataId): 

175 # local import to prevent circular dependency 

176 from .rawFormatter import LsstImSimRawFormatter 

177 return LsstImSimRawFormatter 

178 

179 

180class LsstPhoSim(LsstCam): 

181 """Gen3 Butler specialization for Phosim simulations. 

182 """ 

183 

184 instrument = "LSST-PhoSim" 

185 policyName = "phosim" 

186 translatorClass = LsstPhoSimTranslator 

187 

188 def getRawFormatter(self, dataId): 

189 # local import to prevent circular dependency 

190 from .rawFormatter import LsstPhoSimRawFormatter 

191 return LsstPhoSimRawFormatter 

192 

193 

194class LsstTS8(LsstCam): 

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

196 """ 

197 

198 instrument = "LSST-TS8" 

199 policyName = "ts8" 

200 translatorClass = LsstTS8Translator 

201 

202 def getRawFormatter(self, dataId): 

203 # local import to prevent circular dependency 

204 from .rawFormatter import LsstTS8RawFormatter 

205 return LsstTS8RawFormatter 

206 

207 

208class LsstUCDCam(LsstCam): 

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

210 """ 

211 

212 instrument = "LSST-UCDCam" 

213 policyName = "ucd" 

214 translatorClass = LsstUCDCamTranslator 

215 

216 def getRawFormatter(self, dataId): 

217 # local import to prevent circular dependency 

218 from .rawFormatter import LsstUCDCamRawFormatter 

219 return LsstUCDCamRawFormatter 

220 

221 

222class LsstTS3(LsstCam): 

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

224 """ 

225 

226 instrument = "LSST-TS3" 

227 policyName = "ts3" 

228 translatorClass = LsstTS3Translator 

229 

230 def getRawFormatter(self, dataId): 

231 # local import to prevent circular dependency 

232 from .rawFormatter import LsstTS3RawFormatter 

233 return LsstTS3RawFormatter 

234 

235 

236class Latiss(LsstCam): 

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

238 """ 

239 filterDefinitions = LATISS_FILTER_DEFINITIONS 

240 instrument = "LATISS" 

241 policyName = "latiss" 

242 translatorClass = LatissTranslator 

243 

244 def extractDetectorRecord(self, camGeomDetector): 

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

246 # detector. 

247 record = super().extractDetectorRecord(camGeomDetector) 

248 record["raft"] = None 

249 record["name_in_raft"] = record["full_name"] 

250 return record 

251 

252 def getRawFormatter(self, dataId): 

253 # local import to prevent circular dependency 

254 from .rawFormatter import LatissRawFormatter 

255 return LatissRawFormatter