LSST Applications g013ef56533+7c9321ec0f,g042eb84c57+c6cfa41bc3,g199a45376c+0ba108daf9,g1fd858c14a+fcad0d0313,g210f2d0738+c0f94c6586,g262e1987ae+a7e710680e,g29ae962dfc+fb55f2edb0,g2ac17093b6+61d6563b1e,g2b1d02342f+df6f932764,g2cef7863aa+aef1011c0b,g2f7ad74990+c0f94c6586,g35bb328faa+8c5ae1fdc5,g3fd5ace14f+53cf87ae69,g47891489e3+4316d04fff,g511e8cfd20+baa56acf6c,g53246c7159+8c5ae1fdc5,g54cd7ddccb+fd7ad03fde,g64539dfbff+c0f94c6586,g67b6fd64d1+4316d04fff,g67fd3c3899+c0f94c6586,g6985122a63+4316d04fff,g74acd417e5+ca833bee28,g786e29fd12+668abc6043,g81db2e9a8d+b2ec8e584f,g87389fa792+8856018cbb,g89139ef638+4316d04fff,g8d7436a09f+0a24083b20,g8ea07a8fe4+760ca7c3fc,g90f42f885a+033b1d468d,g97be763408+11eb8fd5b8,gbf99507273+8c5ae1fdc5,gcdda8b9158+e4c84c9d5c,gce8aa8abaa+8c5ae1fdc5,gd7ef33dd92+4316d04fff,gdab6d2f7ff+ca833bee28,ge410e46f29+4316d04fff,geaed405ab2+c4bbc419c6,gf9a733ac38+8c5ae1fdc5,w.2025.40
LSST Data Management Base Package
Loading...
Searching...
No Matches
flatGradient.py
Go to the documentation of this file.
1# This file is part of ip_isr.
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"""
22Flat gradient fit storage class.
23"""
24
25__all__ = ["FlatGradient"]
26
27from astropy.table import Table
28import numpy as np
29from scipy.interpolate import Akima1DInterpolator
30
31from lsst.ip.isr import IsrCalib
32
33
35 """Flat gradient measurements.
36
37 Parameters
38 ----------
39 log : `logging.Logger`, optional
40 Log to write messages to. If `None` a default logger will be used.
41 **kwargs :
42 Additional parameters.
43 """
44
45 _OBSTYPE = "flatGradient"
46 _SCHEMA = "FlatGradient"
47 _VERSION = 1.0
48
49 def __init__(self, **kwargs):
50
51 self.radialSplineNodes = np.zeros(1)
52 self.radialSplineValues = np.zeros(1)
53 self.itlRatio = 1.0
54 self.centroidX = 0.0
55 self.centroidY = 0.0
56 self.centroidDeltaX = 0.0
57 self.centroidDeltaY = 0.0
58 self.gradientX = 0.0
59 self.gradientY = 0.0
61
62 super().__init__(**kwargs)
63
64 self.requiredAttributes.update(
65 [
66 "radialSplineNodes",
67 "radialSplineValues",
68 "itlRatio",
69 "centroidX",
70 "centroidY",
71 "centroidDeltaX",
72 "centroidDeltaY",
73 "gradientX",
74 "gradientY",
75 "normalizationFactor",
76 ],
77 )
78
79 self.updateMetadata(setCalibInfo=True, setCalibId=True, **kwargs)
80
82 self,
83 *,
84 radialSplineNodes,
85 radialSplineValues,
86 itlRatio=1.0,
87 centroidX=0.0,
88 centroidY=0.0,
89 centroidDeltaX=0.0,
90 centroidDeltaY=0.0,
91 gradientX=0.0,
92 gradientY=0.0,
93 normalizationFactor=1.0,
94 ):
95 """Set the parameters for the gradient model.
96
97 Parameters
98 ----------
99 radialSplineNodes : `np.ndarray`
100 Array of spline nodes.
101 radialSplineValues : `np.ndarray`
102 Array of spline values (same length as ``radialSplineNodes``).
103 itlRatio : `float`, optional
104 Ratio of flat for ITL detectors to E2V detectors.
105 centroidX : `float`, optional
106 X centroid of the focal plane (mm). This will be used as the
107 pivot for the gradient plane.
108 centroidY : `float`, optional
109 Y centroid of the focal plane (mm). This will be used as the
110 pivot for the gradient plane.
111 centroidDeltaX : `float`, optional
112 Centroid offset (mm). This is used in the radial function to
113 allow for mis-centering in the illumination gradient.
114 centroidDeltaY : `float`, optional
115 Centroid offset (mm). This is used in the radial function to
116 allow for mis-centering in the illumination gradient.
117 gradientX : `float`, optional
118 Slope of gradient in x direction (throughput/mm).
119 gradientY : `float`, optional
120 Slope of gradient in y direction (throughput/mm).
121 normalizationFactor : `float`, optional
122 Overall normalization factor (used to, e.g. make the
123 center of the focal plane equal to 1.0 vs. a focal-plane
124 average.
125 """
126 if len(radialSplineNodes) != len(radialSplineValues):
127 raise ValueError("The number of spline nodes and values must be equal.")
128
129 self.radialSplineNodes = radialSplineNodes
130 self.radialSplineValues = radialSplineValues
131 self.itlRatio = itlRatio
132 self.centroidX = centroidX
133 self.centroidY = centroidY
134 self.centroidDeltaX = centroidDeltaX
135 self.centroidDeltaY = centroidDeltaY
136 self.gradientX = gradientX
137 self.gradientY = gradientY
138 self.normalizationFactor = normalizationFactor
139
141 """Compute the radial spline model values from x/y.
142
143 The spline model is a 1D Akima spline. When computed, the values
144 from the model describe the radial function of the full focal
145 plane flat-field. Dividing by this model will yield a radially
146 flattened flat-field.
147
148 Parameters
149 ----------
150 x : `np.ndarray`
151 Array of focal plane x values (mm).
152 y : `np.ndarray`
153 Array of focal plane y values (mm).
154
155 Returns
156 -------
157 splineModel : `np.ndarray`
158 Spline model values at the x/y positions.
159 """
160 centroidX = self.centroidX + self.centroidDeltaX
161 centroidY = self.centroidY + self.centroidDeltaY
162
163 radius = np.sqrt((x - centroidX)**2. + (y - centroidY)**2.)
164
165 return self.computeRadialSplineModel(radius)
166
167 def computeRadialSplineModel(self, radius):
168 """Compute the radial spline model values from radii.
169
170 The spline model is a 1D Akima spline. When computed, the values
171 from the model describe the radial function of the full focal
172 plane flat-field. Dividing by this model will yield a radially
173 flattened flat-field.
174
175 Parameters
176 ----------
177 radius : `np.ndarray`
178 Array of focal plane radii (mm).
179
180 Returns
181 -------
182 splineModel : `np.ndarray`
183 Spline model values at the radius positions.
184 """
185 spl = Akima1DInterpolator(self.radialSplineNodes, self.radialSplineValues)
186
187 return spl(np.clip(radius, self.radialSplineNodes[0], self.radialSplineNodes[-1]))
188
189 def computeGradientModel(self, x, y):
190 """Compute the gradient model values.
191
192 The gradient model is a plane constrained to be 1.0 at the
193 ``centroidX``, ``centroidY`` values. Dividing by this model will
194 remove the planar gradient in a flat field. Note that the planar
195 gradient pivot is always at the same position, and does not
196 move with the radial gradient centroid so as to keep the
197 model fit more stable.
198
199 Parameters
200 ----------
201 x : `np.ndarray`
202 Array of focal plane x values (mm).
203 y : `np.ndarray`
204 Array of focal plane y values (mm).
205
206 Returns
207 -------
208 gradientModel : `np.ndarray`
209 Gradient model values at the x/y positions.
210 """
211 gradient = 1 + self.gradientX*(x - self.centroidX) + self.gradientY*(y - self.centroidY)
212
213 return gradient
214
215 def computeFullModel(self, x, y, is_itl):
216 """Compute the full gradient model given x/y and itl booleans.
217
218 This returns the full model that can be applied directly
219 to data that was used in a fit.
220
221 Parameters
222 ----------
223 x : `np.ndarray`
224 Array of focal plane x values (mm).
225 y : `np.ndarray`
226 Array of focal plane y values (mm).
227 is_itl : `np.ndarray`
228 Boolean array of whether each point is from an ITL detector.
229
230 Returns
231 -------
232 model : `np.ndarray`
233 Model values at each position.
234 """
235 model = self.computeRadialSplineModelXY(x, y) / self.computeGradientModel(x, y)
236 model[is_itl] *= self.itlRatio
237
238 return model
239
240 @classmethod
241 def fromDict(cls, dictionary):
242 """Construct a FlatGradient from a dictionary of properties.
243
244 Parameters
245 ----------
246 dictionary : `dict`
247 Dictionary of properties.
248
249 Returns
250 -------
251 calib : `lsst.ip.isr.FlatGradient`
252 Constructed calibration.
253 """
254 calib = cls()
255
256 calib.setMetadata(dictionary["metadata"])
257
258 calib.radialSplineNodes = np.asarray(dictionary["radialSplineNodes"])
259 calib.radialSplineValues = np.asarray(dictionary["radialSplineValues"])
260 calib.itlRatio = dictionary["itlRatio"]
261 calib.centroidX = dictionary["centroidX"]
262 calib.centroidY = dictionary["centroidY"]
263 calib.centroidDeltaX = dictionary["centroidDeltaX"]
264 calib.centroidDeltaY = dictionary["centroidDeltaY"]
265 calib.gradientX = dictionary["gradientX"]
266 calib.gradientY = dictionary["gradientY"]
267 calib.normalizationFactor = dictionary["normalizationFactor"]
268
269 calib.updateMetadata()
270 return calib
271
272 def toDict(self):
273 """Return a dictionary containing the calibration properties.
274
275 Returns
276 -------
277 dictionary : `dict`
278 Dictionary of properties.
279 """
280 self.updateMetadata()
281
282 outDict = dict()
283 metadata = self.getMetadata()
284 outDict["metadata"] = metadata
285
286 outDict["radialSplineNodes"] = self.radialSplineNodes.tolist()
287 outDict["radialSplineValues"] = self.radialSplineValues.tolist()
288 outDict["itlRatio"] = float(self.itlRatio)
289 outDict["centroidX"] = float(self.centroidX)
290 outDict["centroidY"] = float(self.centroidY)
291 outDict["centroidDeltaX"] = float(self.centroidDeltaX)
292 outDict["centroidDeltaY"] = float(self.centroidDeltaY)
293 outDict["gradientX"] = float(self.gradientX)
294 outDict["gradientY"] = float(self.gradientY)
295 outDict["normalizationFactor"] = float(self.normalizationFactor)
296
297 return outDict
298
299 @classmethod
300 def fromTable(cls, tableList):
301 """Construct a calibration from a list of tables.
302
303 Parameters
304 ----------
305 tableList : `list` [`astropy.table.Table`]
306 List of table(s) to use to construct the FlatGradient.
307
308 Returns
309 -------
310 calib : `lsst.ip.isr.FlatGradient`
311 The calibration defined in the table(s).
312 """
313 gradientTable = tableList[0]
314
315 metadata = gradientTable.meta
316 inDict = dict()
317 inDict["metadata"] = metadata
318 inDict["radialSplineNodes"] = np.array(gradientTable[0]["RADIAL_SPLINE_NODES"], dtype=np.float64)
319 inDict["radialSplineValues"] = np.array(gradientTable[0]["RADIAL_SPLINE_VALUES"], dtype=np.float64)
320 inDict["itlRatio"] = float(gradientTable[0]["ITL_RATIO"][0])
321 inDict["centroidX"] = float(gradientTable[0]["CENTROID_X"][0])
322 inDict["centroidY"] = float(gradientTable[0]["CENTROID_Y"][0])
323 inDict["centroidDeltaX"] = float(gradientTable[0]["CENTROID_DELTA_X"][0])
324 inDict["centroidDeltaY"] = float(gradientTable[0]["CENTROID_DELTA_Y"][0])
325 inDict["gradientX"] = float(gradientTable[0]["GRADIENT_X"][0])
326 inDict["gradientY"] = float(gradientTable[0]["GRADIENT_Y"][0])
327 inDict["normalizationFactor"] = float(gradientTable[0]["NORMALIZATION_FACTOR"][0])
328
329 return cls().fromDict(inDict)
330
331 def toTable(self):
332 """Construct a list of table(s) containing the FlatGradient data.
333
334 Returns
335 -------
336 tableList : `list` [`astropy.table.Table`]
337 List of tables containing the FlatGradient information.
338 """
339 tableList = []
340 self.updateMetadata()
341
342 catalog = Table(
343 data=({
344 "RADIAL_SPLINE_NODES": self.radialSplineNodes,
345 "RADIAL_SPLINE_VALUES": self.radialSplineValues,
346 "ITL_RATIO": np.array([self.itlRatio]),
347 "CENTROID_X": np.array([self.centroidX]),
348 "CENTROID_Y": np.array([self.centroidY]),
349 "CENTROID_DELTA_X": np.array([self.centroidDeltaX]),
350 "CENTROID_DELTA_Y": np.array([self.centroidDeltaY]),
351 "GRADIENT_X": np.array([self.gradientX]),
352 "GRADIENT_Y": np.array([self.gradientY]),
353 "NORMALIZATION_FACTOR": np.array([self.normalizationFactor]),
354 },)
355 )
356
357 inMeta = self.getMetadata().toDict()
358 outMeta = {k: v for k, v in inMeta.items() if v is not None}
359 outMeta.update({k: "" for k, v in inMeta.items() if v is None})
360 catalog.meta = outMeta
361 tableList.append(catalog)
362
363 return tableList
updateMetadata(self, camera=None, detector=None, filterName=None, setCalibId=False, setCalibInfo=False, setDate=False, **kwargs)
Definition calibType.py:210
setParameters(self, *, radialSplineNodes, radialSplineValues, itlRatio=1.0, centroidX=0.0, centroidY=0.0, centroidDeltaX=0.0, centroidDeltaY=0.0, gradientX=0.0, gradientY=0.0, normalizationFactor=1.0)