Coverage for python/lsst/obs/hsc/transformRegistry.py: 37%

39 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-02-24 11:11 +0000

1__all__ = [] 

2 

3import numpy as np 

4import astshim as ast 

5 

6from lsst.geom import arcseconds 

7from lsst.afw.geom import transformRegistry, TransformPoint2ToPoint2 

8from lsst.pex.config import Config, Field, ListField 

9 

10 

11class HscDistortionConfig(Config): 

12 """Configuration for distortion calculation for HSC 

13 

14 All values here are as used in the original distortion calculation library 

15 used for HSC, distEst. 

16 

17 The coefficients come from a polynomial fit to the distortion using real 

18 HSC data. They were computed in units of Focal Plane "pixels", which 

19 effectively assumes a scaling of 1 mm per Focal Plane pixel. LSST prefers 

20 to use the proper (actual physical) scaling units, so the function 

21 makeHscDistortion() scales the coeffs from the original FP in "pixels" 

22 to their equivalent in the actual focal plane scale of HSC in mm. 

23 

24 According to Yuki Okura, a 9th order polynomial is required to model the 

25 rapid changes to the distortion at the edges of the field. 

26 """ 

27 ccdToSkyOrder = Field(dtype=int, 

28 doc="Polynomial order for conversion from focal plane position to field angle", 

29 default=9) 

30 xCcdToSky = ListField(dtype=float, 

31 doc=("Coefficients for converting x,y focal plane position to x field angle " 

32 "(computed assuming a focal plane scale of 1 mm/pixel)"), 

33 default=[-0.00047203560468, 

34 -1.5427988883e-05, 

35 -5.95865625284e-10, 

36 2.22594651446e-13, 

37 -1.51583805989e-17, 

38 -6.08317204253e-21, 

39 8.06561200947e-26, 

40 3.10689047143e-29, 

41 -1.2634302141e-34, 

42 -4.78814991762e-38, 

43 -0.000210433037672 + 1.0, 

44 4.66142798084e-09, 

45 -1.0957097072e-10, 

46 -4.6906678848e-17, 

47 4.22216001121e-20, 

48 1.87541306804e-25, 

49 -3.11027701152e-28, 

50 -1.25424492312e-34, 

51 3.70673801604e-37, 

52 -6.85371734365e-09, 

53 -1.26203995922e-14, 

54 -5.4517702042e-17, 

55 -9.94551136649e-22, 

56 9.09298367647e-25, 

57 2.45558081417e-29, 

58 -1.92962666077e-33, 

59 -6.7994922883e-38, 

60 -1.04551853816e-10, 

61 -1.95257983584e-17, 

62 2.66068980901e-20, 

63 2.05500547863e-25, 

64 -7.00557780822e-28, 

65 -1.27106133492e-33, 

66 1.17427552052e-36, 

67 5.48308544761e-17, 

68 -6.6977079558e-21, 

69 -3.88079500249e-26, 

70 -1.12430203404e-29, 

71 -2.3228732246e-33, 

72 5.83996936659e-38, 

73 -2.55808760342e-20, 

74 -5.51546375606e-26, 

75 -4.43037636885e-28, 

76 3.26328244851e-34, 

77 1.30831113179e-36, 

78 -2.3965595459e-25, 

79 7.38850410175e-29, 

80 6.31709084288e-34, 

81 -5.69997824021e-38, 

82 -3.49962832225e-30, 

83 9.21035992064e-35, 

84 4.48010296471e-37, 

85 2.36522390769e-34, 

86 -1.7686068989e-37, 

87 -8.6880691822e-38, 

88 ]) 

89 yCcdToSky = ListField(dtype=float, 

90 doc=("Coefficients for converting x,y focal plane position to y field angle " 

91 "(computed assuming a focal plane scale of 1 mm/pixel)"), 

92 default=[-2.27525408678e-05, 

93 -0.000149438556393 + 1.0, 

94 1.47288649136e-09, 

95 -1.07681558891e-10, 

96 -4.52745194926e-17, 

97 5.33446374932e-21, 

98 1.59765278412e-25, 

99 -1.35281754124e-28, 

100 -2.58952055468e-34, 

101 1.18384181522e-37, 

102 -1.54279888831e-05, 

103 5.10149451107e-09, 

104 -2.20369366154e-12, 

105 -8.12440053288e-17, 

106 3.16674570469e-20, 

107 2.36720490323e-25, 

108 -1.54887554063e-28, 

109 -2.18878587707e-34, 

110 2.42019175449e-37, 

111 -1.36641013138e-09, 

112 -1.08210753878e-10, 

113 3.24065404366e-17, 

114 2.21741676333e-20, 

115 -3.30404486918e-25, 

116 -4.7223051146e-28, 

117 8.48620744583e-34, 

118 5.84549240581e-37, 

119 1.65013522193e-12, 

120 -2.04698537311e-16, 

121 1.36596617211e-20, 

122 8.77160647683e-25, 

123 -1.36731060152e-28, 

124 -1.43968368509e-33, 

125 4.00898492827e-37, 

126 -2.27951193984e-17, 

127 1.16796604208e-20, 

128 -6.53927583976e-25, 

129 -4.41168731276e-28, 

130 1.38404520921e-33, 

131 8.26267449077e-37, 

132 -1.75167734408e-20, 

133 1.35671719277e-24, 

134 -5.56167978363e-29, 

135 -2.43608580718e-33, 

136 9.3744233119e-38, 

137 8.31436843296e-26, 

138 -1.73569476217e-28, 

139 1.90770699097e-33, 

140 4.98143401516e-37, 

141 6.57627509385e-29, 

142 -2.64064071957e-33, 

143 1.56461570921e-37, 

144 -1.50783715462e-34, 

145 1.98549941035e-37, 

146 -8.74305862185e-38, 

147 ]) 

148 tolerance = Field(dtype=float, default=5.0e-3, doc="Tolerance for inversion (pixels)") # Much less than 1 

149 maxIter = Field(dtype=int, default=10, doc="Maximum iterations for inversion") # Usually sufficient 

150 plateScale = Field(dtype=float, default=11.2, doc="Plate scale (arcsec/mm)") 

151 pixelSize = Field(dtype=float, default=0.015, doc="Pixel scale (mm/pixel)") 

152 

153 

154def makeAstPolyMapCoeffs(order, xCoeffs, yCoeffs): 

155 """Convert polynomial coefficients in HSC format to AST PolyMap format 

156 

157 Paramaters 

158 ---------- 

159 order: `int` 

160 Polynomial order 

161 xCoeffs, yCoeffs: `list` of `float` 

162 Forward or inverse polynomial coefficients for the x and y axes 

163 of output, in this order: 

164 x0y0, x0y1, ...x0yN, x1y0, x1y1, ...x1yN-1, ... 

165 where N is the polynomial order. 

166 

167 Returns 

168 ------- 

169 Forward or inverse coefficients for `astshim.PolyMap` 

170 as a 2-d numpy array. 

171 """ 

172 nCoeffs = (order + 1) * (order + 2) // 2 

173 if len(xCoeffs) != nCoeffs: 

174 raise ValueError("found %s xCcdToSky params; need %s" % (len(xCoeffs), nCoeffs)) 

175 if len(yCoeffs) != nCoeffs: 

176 raise ValueError("found %s yCcdToSky params; need %s" % (len(yCoeffs), nCoeffs)) 

177 

178 coeffs = np.zeros([nCoeffs * 2, 4]) 

179 i = 0 

180 for nx in range(order + 1): 

181 for ny in range(order + 1 - nx): 

182 coeffs[i] = [xCoeffs[i], 1, nx, ny] 

183 coeffs[i + nCoeffs] = [yCoeffs[i], 2, nx, ny] 

184 i += 1 

185 assert i == nCoeffs 

186 return coeffs 

187 

188 

189def makeHscDistortion(config): 

190 """Make an HSC distortion transform 

191 

192 Note that inverse coefficients provided, but they are not accurate enough 

193 to use: test_distortion.py reports an error of 2.8 pixels 

194 (HSC uses pixels for its focal plane units) when transforming 

195 from pupil to focal plane. That explains why the original HSC model uses 

196 the inverse coefficients in conjunction with iteration. 

197 

198 Parameters 

199 ---------- 

200 config: `lsst.obs.subaru.HscDistortionConfig` 

201 Distortion coefficients 

202 

203 Returns 

204 ------- 

205 focalPlaneToPupil: `lsst.afw.geom.TransformPoint2ToPoint2` 

206 Transform from focal plane to field angle coordinates 

207 """ 

208 forwardCoeffs = makeAstPolyMapCoeffs(config.ccdToSkyOrder, config.xCcdToSky, config.yCcdToSky) 

209 

210 # Convert coefficients from assumed 1 mm plateScale to actual 11.2 

211 # plateScale 

212 pixelScale = config.plateScale*config.pixelSize*arcseconds # in arcsec/pixel 

213 for coeff in forwardCoeffs: 

214 coeff[0] = coeff[0]/(config.pixelSize**(coeff[2] + coeff[3])) 

215 

216 # Note that the actual error can be somewhat larger than TolInverse; 

217 # the max error I have seen is less than 2, so I scale conservatively 

218 ccdToSky = ast.PolyMap(forwardCoeffs, 2, "IterInverse=1, TolInverse=%s, NIterInverse=%s" % 

219 (config.tolerance / 2.0, config.maxIter)) 

220 fullMapping = ccdToSky.then(ast.ZoomMap(2, pixelScale.asRadians())) 

221 return TransformPoint2ToPoint2(fullMapping) 

222 

223 

224makeHscDistortion.ConfigClass = HscDistortionConfig 

225transformRegistry.register("hsc", makeHscDistortion)