Coverage for python/lsst/afw/cameraGeom/_cameraFactory.py: 22%

53 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-13 02:49 -0700

1# This file is part of afw. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 <https://www.gnu.org/licenses/>. 

21 

22__all__ = ["addDetectorBuilderFromConfig", 

23 "makeCameraFromPath", "makeCameraFromAmpLists"] 

24 

25import os.path 

26from lsst.afw.table import BaseCatalog 

27from ._cameraGeom import FOCAL_PLANE, FIELD_ANGLE, PIXELS, TAN_PIXELS, ACTUAL_PIXELS, CameraSys 

28from ._cameraGeom import Amplifier 

29from ._camera import Camera 

30from ._makePixelToTanPixel import makePixelToTanPixel 

31from .pupil import PupilFactory 

32 

33cameraSysList = [FIELD_ANGLE, FOCAL_PLANE, PIXELS, TAN_PIXELS, ACTUAL_PIXELS] 

34cameraSysMap = dict((sys.getSysName(), sys) for sys in cameraSysList) 

35 

36 

37def addDetectorBuilderFromConfig(cameraBuilder, detectorConfig, amplifiers, focalPlaneToField): 

38 """Build a dictionary of Detector constructor keyword arguments. 

39 

40 The returned dictionary can be passed as keyword arguments to the Detector 

41 constructor, providing all required arguments. However, using these 

42 arguments directly constructs a Detector with knowledge of only the 

43 coordinate systems that are *directly* mapped to its own PIXELS coordinate 

44 system. To construct Detectors with a shared TransformMap for the full 

45 Camera, use makeCameraFromCatalogs or makeCameraFromPath instead of 

46 calling this function or makeDetector directly. 

47 

48 Parameters 

49 ---------- 

50 cameraBuilder : `lsst.afw.cameraGeonm.Camera.Builder` 

51 Camera builder object to which the new Detector Builder 

52 should be added. 

53 detectorConfig : `lsst.pex.config.Config` 

54 Configuration for this detector. 

55 amplifiers : `list` [`~lsst.afw.cameraGeom.Amplifier`] 

56 amplifier information for this detector 

57 focalPlaneToField : `lsst.afw.geom.TransformPoint2ToPoint2` 

58 FOCAL_PLANE to FIELD_ANGLE Transform 

59 

60 Returns 

61 ------- 

62 detectorBuilder : `lsst.afw.cameraGeom.Detector.InCameraBuilder` 

63 A builder object for a detector corresponding to the given config, 

64 associated with the given camera builder object. 

65 """ 

66 detectorBuilder = cameraBuilder.add(detectorConfig.name, detectorConfig.id) 

67 detectorBuilder.fromConfig(detectorConfig) 

68 

69 for ampBuilder in amplifiers: 

70 detectorBuilder.append(ampBuilder) 

71 

72 transforms = makeTransformDict(detectorConfig.transformDict.transforms) 

73 

74 # It seems the C++ code has always assumed that the "nativeSys" for 

75 # detectors is PIXELS, despite the configs here giving the illusion of 

76 # choice. We'll use PIXELS if the config value is None, and assert that 

77 # the value is PIXELS otherwise. Note that we can't actually get rid of 

78 # the nativeSys config option without breaking lots of on-disk camera 

79 # configs. 

80 detectorNativeSysPrefix = cameraSysMap.get(detectorConfig.transformDict.nativeSys, PIXELS) 

81 assert detectorNativeSysPrefix == PIXELS, "Detectors with nativeSys != PIXELS are not supported." 

82 

83 for toSys, transform in transforms.items(): 

84 detectorBuilder.setTransformFromPixelsTo(toSys, transform) 

85 tanPixSys = CameraSys(TAN_PIXELS, detectorConfig.name) 

86 transforms[tanPixSys] = makePixelToTanPixel( 

87 bbox=detectorBuilder.getBBox(), 

88 orientation=detectorBuilder.getOrientation(), 

89 focalPlaneToField=focalPlaneToField, 

90 pixelSizeMm=detectorBuilder.getPixelSize(), 

91 ) 

92 

93 for toSys, transform in transforms.items(): 

94 detectorBuilder.setTransformFromPixelsTo(toSys, transform) 

95 

96 detectorBuilder.setCrosstalk(detectorConfig.getCrosstalk(len(amplifiers))) 

97 

98 return detectorBuilder 

99 

100 

101def makeTransformDict(transformConfigDict): 

102 """Make a dictionary of CameraSys: lsst.afw.geom.Transform from a config dict. 

103 

104 Parameters 

105 ---------- 

106 transformConfigDict : value obtained from a `lsst.pex.config.ConfigDictField` 

107 registry; keys are camera system names. 

108 

109 Returns 

110 ------- 

111 transforms : `dict` [`CameraSys` or `CameraSysPrefix`, `lsst.afw.geom.Transform`] 

112 A dict of CameraSys or CameraSysPrefix: lsst.afw.geom.Transform 

113 """ 

114 resMap = dict() 

115 if transformConfigDict is not None: 

116 for key in transformConfigDict: 

117 transform = transformConfigDict[key].transform.apply() 

118 resMap[CameraSys(key)] = transform 

119 return resMap 

120 

121 

122def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc, 

123 pupilFactoryClass=PupilFactory): 

124 """Make a Camera instance from a directory of ampInfo files 

125 

126 The directory must contain one ampInfo fits file for each detector in cameraConfig.detectorList. 

127 The name of each ampInfo file must be shortNameFunc(fullDetectorName) + ".fits". 

128 

129 Parameters 

130 ---------- 

131 cameraConfig : `CameraConfig` 

132 Config describing camera and its detectors. 

133 ampInfoPath : `str` 

134 Path to ampInfo data files. 

135 shortNameFunc : callable 

136 A function that converts a long detector name to a short one. 

137 pupilFactoryClass : `type`, optional 

138 Class to attach to camera; default is `lsst.afw.cameraGeom.PupilFactory`. 

139 

140 Returns 

141 ------- 

142 camera : `lsst.afw.cameraGeom.Camera` 

143 New Camera instance. 

144 """ 

145 ampListDict = dict() 

146 for detectorConfig in cameraConfig.detectorList.values(): 

147 shortName = shortNameFunc(detectorConfig.name) 

148 ampCatPath = os.path.join(ampInfoPath, shortName + ".fits") 

149 catalog = BaseCatalog.readFits(ampCatPath) 

150 ampListDict[detectorConfig.name] = [Amplifier.Builder.fromRecord(record) 

151 for record in catalog] 

152 

153 return makeCameraFromAmpLists(cameraConfig, ampListDict, pupilFactoryClass) 

154 

155 

156def makeCameraFromAmpLists(cameraConfig, ampListDict, 

157 pupilFactoryClass=PupilFactory): 

158 """Construct a Camera instance from a dictionary of detector name: list of 

159 Amplifier.Builder 

160 

161 Parameters 

162 ---------- 

163 cameraConfig : `CameraConfig` 

164 Config describing camera and its detectors. 

165 ampListDict : `dict` [`str`, `list` [`Amplifier.Builder`]] 

166 A dictionary of detector name: list of Amplifier.Builder 

167 pupilFactoryClass : `type`, optional 

168 Class to attach to camera; `lsst.default afw.cameraGeom.PupilFactory`. 

169 

170 Returns 

171 ------- 

172 camera : `lsst.afw.cameraGeom.Camera` 

173 New Camera instance. 

174 """ 

175 nativeSys = cameraSysMap[cameraConfig.transformDict.nativeSys] 

176 

177 # nativeSys=FOCAL_PLANE seems is baked into the camera class definition, 

178 # despite CameraConfig providing the illusion that it's configurable. Note 

179 # that we can't actually get rid of the nativeSys config option without 

180 # breaking lots of on-disk camera configs. 

181 assert nativeSys == FOCAL_PLANE, "Cameras with nativeSys != FOCAL_PLANE are not supported." 

182 

183 cameraBuilder = Camera.Builder(cameraConfig.name) 

184 cameraBuilder.setPupilFactoryClass(pupilFactoryClass) 

185 

186 transformDict = makeTransformDict(cameraConfig.transformDict.transforms) 

187 focalPlaneToField = transformDict[FIELD_ANGLE] 

188 

189 for toSys, transform in transformDict.items(): 

190 cameraBuilder.setTransformFromFocalPlaneTo(toSys, transform) 

191 

192 for detectorConfig in cameraConfig.detectorList.values(): 

193 addDetectorBuilderFromConfig( 

194 cameraBuilder, 

195 detectorConfig=detectorConfig, 

196 amplifiers=ampListDict[detectorConfig.name], 

197 focalPlaneToField=focalPlaneToField, 

198 ) 

199 

200 return cameraBuilder.finish()