LSST Applications g180d380827+770a9040cc,g2079a07aa2+86d27d4dc4,g2305ad1205+09cfdadad9,g2bbee38e9b+c6a8a0fb72,g337abbeb29+c6a8a0fb72,g33d1c0ed96+c6a8a0fb72,g3a166c0a6a+c6a8a0fb72,g3ddfee87b4+1ea5e09c42,g48712c4677+7e2ea9cd42,g487adcacf7+301d09421d,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+96fcb956a6,g64a986408d+23540ee355,g858d7b2824+23540ee355,g864b0138d7+aa38e45daa,g95921f966b+d83dc58ecd,g991b906543+23540ee355,g99cad8db69+7f13b58a93,g9c22b2923f+e2510deafe,g9ddcbc5298+9a081db1e4,ga1e77700b3+03d07e1c1f,gb0e22166c9+60f28cb32d,gb23b769143+23540ee355,gba4ed39666+c2a2e4ac27,gbb8dafda3b+49e7449578,gbd998247f1+585e252eca,gc120e1dc64+1bbfa184e1,gc28159a63d+c6a8a0fb72,gc3e9b769f7+385ea95214,gcf0d15dbbd+1ea5e09c42,gdaeeff99f8+f9a426f77a,ge6526c86ff+1bccc98490,ge79ae78c31+c6a8a0fb72,gee10cc3b42+585e252eca,w.2024.18
LSST Data Management Base Package
Loading...
Searching...
No Matches
_exposureSummaryStats.py
Go to the documentation of this file.
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/>.
21from __future__ import annotations
22
23import dataclasses
24from typing import TYPE_CHECKING
25import yaml
26import warnings
27
28from ..typehandling import Storable, StorableHelperFactory
29
30if TYPE_CHECKING:
31 from ..table import BaseRecord, Schema
32
33__all__ = ("ExposureSummaryStats", )
34
35
37 return [float("nan")] * 4
38
39
40@dataclasses.dataclass
42 _persistence_name = 'ExposureSummaryStats'
43
44 _factory = StorableHelperFactory(__name__, _persistence_name)
45
46 version: int = 0
47
48 psfSigma: float = float('nan')
49 """PSF determinant radius (pixels)."""
50
51 psfArea: float = float('nan')
52 """PSF effective area (pixels**2)."""
53
54 psfIxx: float = float('nan')
55 """PSF shape Ixx (pixels**2)."""
56
57 psfIyy: float = float('nan')
58 """PSF shape Iyy (pixels**2)."""
59
60 psfIxy: float = float('nan')
61 """PSF shape Ixy (pixels**2)."""
62
63 ra: float = float('nan')
64 """Bounding box center Right Ascension (degrees)."""
65
66 dec: float = float('nan')
67 """Bounding box center Declination (degrees)."""
68
69 zenithDistance: float = float('nan')
70 """Bounding box center zenith distance (degrees)."""
71
72 zeroPoint: float = float('nan')
73 """Mean zeropoint in detector (mag)."""
74
75 skyBg: float = float('nan')
76 """Average sky background (ADU)."""
77
78 skyNoise: float = float('nan')
79 """Average sky noise (ADU)."""
80
81 meanVar: float = float('nan')
82 """Mean variance of the weight plane (ADU**2)."""
83
84 raCorners: list[float] = dataclasses.field(default_factory=_default_corners)
85 """Right Ascension of bounding box corners (degrees)."""
86
87 decCorners: list[float] = dataclasses.field(default_factory=_default_corners)
88 """Declination of bounding box corners (degrees)."""
89
90 astromOffsetMean: float = float('nan')
91 """Astrometry match offset mean."""
92
93 astromOffsetStd: float = float('nan')
94 """Astrometry match offset stddev."""
95
96 nPsfStar: int = 0
97 """Number of stars used for psf model."""
98
99 psfStarDeltaE1Median: float = float('nan')
100 """Psf stars median E1 residual (starE1 - psfE1)."""
101
102 psfStarDeltaE2Median: float = float('nan')
103 """Psf stars median E2 residual (starE2 - psfE2)."""
104
105 psfStarDeltaE1Scatter: float = float('nan')
106 """Psf stars MAD E1 scatter (starE1 - psfE1)."""
107
108 psfStarDeltaE2Scatter: float = float('nan')
109 """Psf stars MAD E2 scatter (starE2 - psfE2)."""
110
111 psfStarDeltaSizeMedian: float = float('nan')
112 """Psf stars median size residual (starSize - psfSize)."""
113
114 psfStarDeltaSizeScatter: float = float('nan')
115 """Psf stars MAD size scatter (starSize - psfSize)."""
116
117 psfStarScaledDeltaSizeScatter: float = float('nan')
118 """Psf stars MAD size scatter scaled by psfSize**2."""
119
120 psfTraceRadiusDelta: float = float('nan')
121 """Delta (max - min) of the model psf trace radius values evaluated on a
122 grid of unmasked pixels (pixels).
123 """
124
125 maxDistToNearestPsf: float = float('nan')
126 """Maximum distance of an unmasked pixel to its nearest model psf star
127 (pixels).
128 """
129
130 effTime: float = float('nan')
131 """Effective exposure time calculated from psfSigma, skyBg, and
132 zeroPoint (seconds).
133 """
134
135 effTimePsfSigmaScale: float = float('nan')
136 """PSF scaling of the effective exposure time."""
137
138 effTimeSkyBgScale: float = float('nan')
139 """Sky background scaling of the effective exposure time."""
140
141 effTimeZeroPointScale: float = float('nan')
142 """Zeropoint scaling of the effective exposure time."""
143
144 def __post_init__(self):
145 Storable.__init__(self)
146
147 def isPersistable(self):
148 return True
149
151 return self._persistence_name
152
154 return __name__
155
156 def _write(self):
157 return yaml.dump(dataclasses.asdict(self), encoding='utf-8')
158
159 @staticmethod
160 def _read(bytes):
161 yamlDict = yaml.load(bytes, Loader=yaml.SafeLoader)
162
163 # Special list of fields to forward to new names.
164 forwardFieldDict = {"decl": "dec"}
165
166 # For forwards compatibility, filter out any fields that are
167 # not defined in the dataclass.
168 droppedFields = []
169 for _field in list(yamlDict.keys()):
170 if _field not in ExposureSummaryStats.__dataclass_fields__:
171 if _field in forwardFieldDict and forwardFieldDict[_field] not in yamlDict:
172 yamlDict[forwardFieldDict[_field]] = yamlDict[_field]
173 else:
174 droppedFields.append(_field)
175 yamlDict.pop(_field)
176 if len(droppedFields) > 0:
177 droppedFieldString = ", ".join([str(f) for f in droppedFields])
178 plural = "s" if len(droppedFields) != 1 else ""
179 them = "them" if len(droppedFields) > 1 else "it"
180 warnings.warn(
181 f"Summary field{plural} [{droppedFieldString}] not recognized by this software version;"
182 f" ignoring {them}.",
183 FutureWarning,
184 stacklevel=2,
185 )
186 return ExposureSummaryStats(**yamlDict)
187
188 @classmethod
189 def update_schema(cls, schema: Schema) -> None:
190 """Update an schema to includes for all summary statistic fields.
191
192 Parameters
193 -------
194 schema : `lsst.afw.table.Schema`
195 Schema to add which fields will be added.
196 """
197 schema.addField(
198 "psfSigma",
199 type="F",
200 doc="PSF model second-moments determinant radius (center of chip) (pixel)",
201 units="pixel",
202 )
203 schema.addField(
204 "psfArea",
205 type="F",
206 doc="PSF model effective area (center of chip) (pixel**2)",
207 units='pixel**2',
208 )
209 schema.addField(
210 "psfIxx",
211 type="F",
212 doc="PSF model Ixx (center of chip) (pixel**2)",
213 units='pixel**2',
214 )
215 schema.addField(
216 "psfIyy",
217 type="F",
218 doc="PSF model Iyy (center of chip) (pixel**2)",
219 units='pixel**2',
220 )
221 schema.addField(
222 "psfIxy",
223 type="F",
224 doc="PSF model Ixy (center of chip) (pixel**2)",
225 units='pixel**2',
226 )
227 schema.addField(
228 "raCorners",
229 type="ArrayD",
230 size=4,
231 doc="Right Ascension of bounding box corners (degrees)",
232 units="degree",
233 )
234 schema.addField(
235 "decCorners",
236 type="ArrayD",
237 size=4,
238 doc="Declination of bounding box corners (degrees)",
239 units="degree",
240 )
241 schema.addField(
242 "ra",
243 type="D",
244 doc="Right Ascension of bounding box center (degrees)",
245 units="degree",
246 )
247 schema.addField(
248 "dec",
249 type="D",
250 doc="Declination of bounding box center (degrees)",
251 units="degree",
252 )
253 schema.addField(
254 "zenithDistance",
255 type="F",
256 doc="Zenith distance of bounding box center (degrees)",
257 units="degree",
258 )
259 schema.addField(
260 "zeroPoint",
261 type="F",
262 doc="Mean zeropoint in detector (mag)",
263 units="mag",
264 )
265 schema.addField(
266 "skyBg",
267 type="F",
268 doc="Average sky background (ADU)",
269 units="adu",
270 )
271 schema.addField(
272 "skyNoise",
273 type="F",
274 doc="Average sky noise (ADU)",
275 units="adu",
276 )
277 schema.addField(
278 "meanVar",
279 type="F",
280 doc="Mean variance of the weight plane (ADU**2)",
281 units="adu**2"
282 )
283 schema.addField(
284 "astromOffsetMean",
285 type="F",
286 doc="Mean offset of astrometric calibration matches (arcsec)",
287 units="arcsec",
288 )
289 schema.addField(
290 "astromOffsetStd",
291 type="F",
292 doc="Standard deviation of offsets of astrometric calibration matches (arcsec)",
293 units="arcsec",
294 )
295 schema.addField("nPsfStar", type="I", doc="Number of stars used for PSF model")
296 schema.addField(
297 "psfStarDeltaE1Median",
298 type="F",
299 doc="Median E1 residual (starE1 - psfE1) for psf stars",
300 )
301 schema.addField(
302 "psfStarDeltaE2Median",
303 type="F",
304 doc="Median E2 residual (starE2 - psfE2) for psf stars",
305 )
306 schema.addField(
307 "psfStarDeltaE1Scatter",
308 type="F",
309 doc="Scatter (via MAD) of E1 residual (starE1 - psfE1) for psf stars",
310 )
311 schema.addField(
312 "psfStarDeltaE2Scatter",
313 type="F",
314 doc="Scatter (via MAD) of E2 residual (starE2 - psfE2) for psf stars",
315 )
316 schema.addField(
317 "psfStarDeltaSizeMedian",
318 type="F",
319 doc="Median size residual (starSize - psfSize) for psf stars (pixel)",
320 units="pixel",
321 )
322 schema.addField(
323 "psfStarDeltaSizeScatter",
324 type="F",
325 doc="Scatter (via MAD) of size residual (starSize - psfSize) for psf stars (pixel)",
326 units="pixel",
327 )
328 schema.addField(
329 "psfStarScaledDeltaSizeScatter",
330 type="F",
331 doc="Scatter (via MAD) of size residual scaled by median size squared",
332 )
333 schema.addField(
334 "psfTraceRadiusDelta",
335 type="F",
336 doc="Delta (max - min) of the model psf trace radius values evaluated on a grid of "
337 "unmasked pixels (pixel).",
338 units="pixel",
339 )
340 schema.addField(
341 "maxDistToNearestPsf",
342 type="F",
343 doc="Maximum distance of an unmasked pixel to its nearest model psf star (pixel).",
344 units="pixel",
345 )
346 schema.addField(
347 "effTime",
348 type="F",
349 doc="Effective exposure time calculated from psfSigma, skyBg, and "
350 "zeroPoint (seconds).",
351 units="second",
352 )
353 schema.addField(
354 "effTimePsfSigmaScale",
355 type="F",
356 doc="PSF scaling of the effective exposure time."
357 )
358 schema.addField(
359 "effTimeSkyBgScale",
360 type="F",
361 doc="Sky background scaling of the effective exposure time."
362 )
363 schema.addField(
364 "effTimeZeroPointScale",
365 type="F",
366 doc="Zeropoint scaling of the effective exposure time."
367 )
368
369 def update_record(self, record: BaseRecord) -> None:
370 """Write summary-statistic columns into a record.
371
372 Parameters
373 ----------
374 record : `lsst.afw.table.BaseRecord`
375 Record to update. This is expected to frequently be an
376 `ExposureRecord` instance (with higher-level code adding other
377 columns and objects), but this method can work with any record
378 type.
379 """
380 for field in dataclasses.fields(self):
381 value = getattr(self, field.name)
382 if field.name == "version":
383 continue
384 elif field.type.startswith("list"):
385 record[field.name][:] = value
386 else:
387 record[field.name] = value
388
389 @classmethod
390 def from_record(cls, record: BaseRecord) -> ExposureSummaryStats:
391 """Read summary-statistic columns from a record into ``self``.
392
393 Parameters
394 ----------
395 record : `lsst.afw.table.BaseRecord`
396 Record to read from. This is expected to frequently be an
397 `ExposureRecord` instance (with higher-level code adding other
398 columns and objects), but this method can work with any record
399 type, ignoring any attributes or columns it doesn't recognize.
400
401 Returns
402 -------
403 summary : `ExposureSummaryStats`
404 Summary statistics object created from the given record.
405 """
406 return cls(
407 **{
408 field.name: (
409 record[field.name] if not field.type.startswith("list")
410 else [float(v) for v in record[field.name]]
411 )
412 for field in dataclasses.fields(cls)
413 if field.name != "version"
414 }
415 )
ExposureSummaryStats from_record(cls, BaseRecord record)
virtual bool isPersistable() const noexcept
Return true if this particular object can be persisted using afw::table::io.
Interface supporting iteration over heterogenous containers.
Definition Storable.h:58