Coverage for python/lsst/obs/hsc/transformRegistry.py: 37%
39 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-30 10:51 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-30 10:51 +0000
1__all__ = []
3import numpy as np
4import astshim as ast
6from lsst.geom import arcseconds
7from lsst.afw.geom import transformRegistry, TransformPoint2ToPoint2
8from lsst.pex.config import Config, Field, ListField
11class HscDistortionConfig(Config):
12 """Configuration for distortion calculation for HSC
14 All values here are as used in the original distortion calculation library
15 used for HSC, distEst.
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.
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)")
154def makeAstPolyMapCoeffs(order, xCoeffs, yCoeffs):
155 """Convert polynomial coefficients in HSC format to AST PolyMap format
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.
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))
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
189def makeHscDistortion(config):
190 """Make an HSC distortion transform
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.
198 Parameters
199 ----------
200 config: `lsst.obs.subaru.HscDistortionConfig`
201 Distortion coefficients
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)
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]))
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)
224makeHscDistortion.ConfigClass = HscDistortionConfig
225transformRegistry.register("hsc", makeHscDistortion)