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

107 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-01 03:22 -0700

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", "LsstCamImSim", "LsstCamPhoSim", "LsstTS8", 

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

24 

25import os.path 

26 

27import lsst.obs.base.yamlCamera as yamlCamera 

28from lsst.utils.introspection import get_full_type_name 

29from lsst.utils import getPackageDir 

30from lsst.obs.base import Instrument, VisitSystem 

31from .filters import (LSSTCAM_FILTER_DEFINITIONS, LATISS_FILTER_DEFINITIONS, 

32 LSSTCAM_IMSIM_FILTER_DEFINITIONS, TS3_FILTER_DEFINITIONS, 

33 TS8_FILTER_DEFINITIONS, COMCAM_FILTER_DEFINITIONS, 

34 ) 

35 

36from .translators import LatissTranslator, LsstCamTranslator, \ 

37 LsstUCDCamTranslator, LsstTS3Translator, LsstComCamTranslator, \ 

38 LsstCamPhoSimTranslator, LsstTS8Translator, LsstCamImSimTranslator 

39 

40PACKAGE_DIR = getPackageDir("obs_lsst") 

41 

42 

43class LsstCam(Instrument): 

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

45 

46 Parameters 

47 ---------- 

48 camera : `lsst.cameraGeom.Camera` 

49 Camera object from which to extract detector information. 

50 filters : `list` of `FilterDefinition` 

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

52 associated with this instrument in the registry. 

53 

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

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

56 representations defined by an Instrument do not. Instead: 

57 

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

59 camera, which should be static information that actually reflects 

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

61 the distinction between physical sensors and slots is unimportant in 

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

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

64 the future this distinction between static and time-dependent 

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

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

67 carries static content). 

68 

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

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

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

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

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

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

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

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

77 future. 

78 """ 

79 filterDefinitions = LSSTCAM_FILTER_DEFINITIONS 

80 instrument = "LSSTCam" 

81 policyName = "lsstCam" 

82 translatorClass = LsstCamTranslator 

83 obsDataPackage = "obs_lsst_data" 

84 visitSystem = VisitSystem.BY_SEQ_START_END 

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 {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, update=False): 

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_exposure_id() 

118 with registry.transaction(): 

119 registry.syncDimensionData( 

120 "instrument", 

121 { 

122 "name": self.getName(), 

123 "detector_max": self.translatorClass.DETECTOR_MAX, 

124 "visit_max": obsMax, 

125 "exposure_max": obsMax, 

126 "class_name": get_full_type_name(self), 

127 "visit_system": self.visitSystem.value, 

128 }, 

129 update=update 

130 ) 

131 for detector in self.getCamera(): 

132 registry.syncDimensionData("detector", self.extractDetectorRecord(detector), update=update) 

133 

134 self._registerFilters(registry, update=update) 

135 

136 def extractDetectorRecord(self, camGeomDetector): 

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

138 """ 

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

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

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

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

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

144 # They are separate in ObservationInfo 

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

146 

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

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

149 # prefixed by the enum type name). 

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

151 

152 return dict( 

153 instrument=self.getName(), 

154 id=camGeomDetector.getId(), 

155 full_name=camGeomDetector.getName(), 

156 name_in_raft=name, 

157 purpose=purpose, 

158 raft=group, 

159 ) 

160 

161 

162class LsstComCam(LsstCam): 

163 """Gen3 Butler specialization for ComCam data. 

164 """ 

165 

166 filterDefinitions = COMCAM_FILTER_DEFINITIONS 

167 instrument = "LSSTComCam" 

168 policyName = "comCam" 

169 translatorClass = LsstComCamTranslator 

170 

171 def getRawFormatter(self, dataId): 

172 # local import to prevent circular dependency 

173 from .rawFormatter import LsstComCamRawFormatter 

174 return LsstComCamRawFormatter 

175 

176 

177class LsstCamImSim(LsstCam): 

178 """Gen3 Butler specialization for ImSim simulations. 

179 """ 

180 

181 instrument = "LSSTCam-imSim" 

182 policyName = "imsim" 

183 translatorClass = LsstCamImSimTranslator 

184 filterDefinitions = LSSTCAM_IMSIM_FILTER_DEFINITIONS 

185 visitSystem = VisitSystem.ONE_TO_ONE 

186 

187 def getRawFormatter(self, dataId): 

188 # local import to prevent circular dependency 

189 from .rawFormatter import LsstCamImSimRawFormatter 

190 return LsstCamImSimRawFormatter 

191 

192 

193class LsstCamPhoSim(LsstCam): 

194 """Gen3 Butler specialization for Phosim simulations. 

195 """ 

196 

197 instrument = "LSSTCam-PhoSim" 

198 policyName = "phosim" 

199 translatorClass = LsstCamPhoSimTranslator 

200 visitSystem = VisitSystem.ONE_TO_ONE 

201 

202 def getRawFormatter(self, dataId): 

203 # local import to prevent circular dependency 

204 from .rawFormatter import LsstCamPhoSimRawFormatter 

205 return LsstCamPhoSimRawFormatter 

206 

207 

208class LsstTS8(LsstCam): 

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

210 """ 

211 

212 filterDefinitions = TS8_FILTER_DEFINITIONS 

213 instrument = "LSST-TS8" 

214 policyName = "ts8" 

215 translatorClass = LsstTS8Translator 

216 visitSystem = VisitSystem.ONE_TO_ONE 

217 

218 def getRawFormatter(self, dataId): 

219 # local import to prevent circular dependency 

220 from .rawFormatter import LsstTS8RawFormatter 

221 return LsstTS8RawFormatter 

222 

223 

224class LsstUCDCam(LsstCam): 

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

226 """ 

227 

228 instrument = "LSST-UCDCam" 

229 policyName = "ucd" 

230 translatorClass = LsstUCDCamTranslator 

231 visitSystem = VisitSystem.ONE_TO_ONE 

232 

233 def getRawFormatter(self, dataId): 

234 # local import to prevent circular dependency 

235 from .rawFormatter import LsstUCDCamRawFormatter 

236 return LsstUCDCamRawFormatter 

237 

238 

239class LsstTS3(LsstCam): 

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

241 """ 

242 

243 filterDefinitions = TS3_FILTER_DEFINITIONS 

244 instrument = "LSST-TS3" 

245 policyName = "ts3" 

246 translatorClass = LsstTS3Translator 

247 visitSystem = VisitSystem.ONE_TO_ONE 

248 

249 def getRawFormatter(self, dataId): 

250 # local import to prevent circular dependency 

251 from .rawFormatter import LsstTS3RawFormatter 

252 return LsstTS3RawFormatter 

253 

254 

255class Latiss(LsstCam): 

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

257 """ 

258 filterDefinitions = LATISS_FILTER_DEFINITIONS 

259 instrument = "LATISS" 

260 policyName = "latiss" 

261 translatorClass = LatissTranslator 

262 

263 def extractDetectorRecord(self, camGeomDetector): 

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

265 # detector. 

266 record = super().extractDetectorRecord(camGeomDetector) 

267 record["raft"] = None 

268 record["name_in_raft"] = record["full_name"] 

269 return record 

270 

271 def getRawFormatter(self, dataId): 

272 # local import to prevent circular dependency 

273 from .rawFormatter import LatissRawFormatter 

274 return LatissRawFormatter