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.instrument import Instrument 

31from .filters import LSSTCAM_FILTER_DEFINITIONS, LATISS_FILTER_DEFINITIONS 

32 

33from .translators import LatissTranslator, LsstCamTranslator, \ 

34 LsstUCDCamTranslator, LsstTS3Translator, LsstComCamTranslator, \ 

35 LsstPhoSimTranslator, LsstTS8Translator, LsstImSimTranslator 

36 

37PACKAGE_DIR = getPackageDir("obs_lsst") 

38 

39 

40class LsstCam(Instrument): 

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

42 

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. 

50 

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: 

54 

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

65 

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" 

83 

84 @property 

85 def configPaths(self): 

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

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

88 

89 @classmethod 

90 def getName(cls): 

91 # Docstring inherited from Instrument.getName 

92 return cls.instrument 

93 

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 

104 

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 

110 

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

123 

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

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

126 

127 self._registerFilters(registry) 

128 

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("_") 

139 

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] 

144 

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 ) 

153 

154 

155class LsstComCam(LsstCam): 

156 """Gen3 Butler specialization for ComCam data. 

157 """ 

158 

159 instrument = "LSSTComCam" 

160 policyName = "comCam" 

161 translatorClass = LsstComCamTranslator 

162 

163 def getRawFormatter(self, dataId): 

164 # local import to prevent circular dependency 

165 from .rawFormatter import LsstComCamRawFormatter 

166 return LsstComCamRawFormatter 

167 

168 

169class LsstImSim(LsstCam): 

170 """Gen3 Butler specialization for ImSim simulations. 

171 """ 

172 

173 instrument = "LSST-ImSim" 

174 policyName = "imsim" 

175 translatorClass = LsstImSimTranslator 

176 

177 def getRawFormatter(self, dataId): 

178 # local import to prevent circular dependency 

179 from .rawFormatter import LsstImSimRawFormatter 

180 return LsstImSimRawFormatter 

181 

182 

183class LsstPhoSim(LsstCam): 

184 """Gen3 Butler specialization for Phosim simulations. 

185 """ 

186 

187 instrument = "LSST-PhoSim" 

188 policyName = "phosim" 

189 translatorClass = LsstPhoSimTranslator 

190 

191 def getRawFormatter(self, dataId): 

192 # local import to prevent circular dependency 

193 from .rawFormatter import LsstPhoSimRawFormatter 

194 return LsstPhoSimRawFormatter 

195 

196 

197class LsstTS8(LsstCam): 

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

199 """ 

200 

201 instrument = "LSST-TS8" 

202 policyName = "ts8" 

203 translatorClass = LsstTS8Translator 

204 

205 def getRawFormatter(self, dataId): 

206 # local import to prevent circular dependency 

207 from .rawFormatter import LsstTS8RawFormatter 

208 return LsstTS8RawFormatter 

209 

210 

211class LsstUCDCam(LsstCam): 

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

213 """ 

214 

215 instrument = "LSST-UCDCam" 

216 policyName = "ucd" 

217 translatorClass = LsstUCDCamTranslator 

218 

219 def getRawFormatter(self, dataId): 

220 # local import to prevent circular dependency 

221 from .rawFormatter import LsstUCDCamRawFormatter 

222 return LsstUCDCamRawFormatter 

223 

224 

225class LsstTS3(LsstCam): 

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

227 """ 

228 

229 instrument = "LSST-TS3" 

230 policyName = "ts3" 

231 translatorClass = LsstTS3Translator 

232 

233 def getRawFormatter(self, dataId): 

234 # local import to prevent circular dependency 

235 from .rawFormatter import LsstTS3RawFormatter 

236 return LsstTS3RawFormatter 

237 

238 

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 

246 

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 

254 

255 def getRawFormatter(self, dataId): 

256 # local import to prevent circular dependency 

257 from .rawFormatter import LatissRawFormatter 

258 return LatissRawFormatter