Coverage for python / lsst / images / _observation_summary_stats.py: 94%

51 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-24 08:34 +0000

1# This file is part of lsst-images. 

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# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11from __future__ import annotations 

12 

13__all__ = ("ObservationSummaryStats",) 

14 

15import dataclasses 

16import math 

17from typing import Any, Self 

18 

19import pydantic 

20 

21 

22def _default_corners() -> tuple[float, float, float, float]: 

23 return (math.nan, math.nan, math.nan, math.nan) 

24 

25 

26class ObservationSummaryStats(pydantic.BaseModel, ser_json_inf_nan="constants"): 

27 version: int = pydantic.Field(0, description="Version of the model.") 

28 

29 psfSigma: float = pydantic.Field(math.nan, description="PSF determinant radius (pixels).") 

30 

31 psfArea: float = pydantic.Field(math.nan, description="PSF effective area (pixels**2).") 

32 

33 psfIxx: float = pydantic.Field(math.nan, description="PSF shape Ixx (pixels**2).") 

34 

35 psfIyy: float = pydantic.Field(math.nan, description="PSF shape Iyy (pixels**2).") 

36 

37 psfIxy: float = pydantic.Field(math.nan, description="PSF shape Ixy (pixels**2).") 

38 

39 ra: float = pydantic.Field(math.nan, description="Bounding box center Right Ascension (degrees).") 

40 

41 dec: float = pydantic.Field(math.nan, description="Bounding box center Declination (degrees).") 

42 

43 pixelScale: float = pydantic.Field(math.nan, description="Measured detector pixel scale (arcsec/pixel).") 

44 

45 zenithDistance: float = pydantic.Field( 

46 math.nan, description="Bounding box center zenith distance (degrees)." 

47 ) 

48 

49 expTime: float = pydantic.Field(math.nan, description="Exposure time of the exposure (seconds).") 

50 

51 zeroPoint: float = pydantic.Field(math.nan, description="Mean zeropoint in detector (mag).") 

52 

53 skyBg: float = pydantic.Field(math.nan, description="Average sky background (ADU).") 

54 

55 skyNoise: float = pydantic.Field(math.nan, description="Average sky noise (ADU).") 

56 

57 meanVar: float = pydantic.Field(math.nan, description="Mean variance of the weight plane (ADU**2).") 

58 

59 raCorners: tuple[float, float, float, float] = pydantic.Field( 

60 default_factory=_default_corners, description="Right Ascension of bounding box corners (degrees)." 

61 ) 

62 

63 decCorners: tuple[float, float, float, float] = pydantic.Field( 

64 default_factory=_default_corners, description="Declination of bounding box corners (degrees)." 

65 ) 

66 

67 astromOffsetMean: float = pydantic.Field(math.nan, description="Astrometry match offset mean.") 

68 

69 astromOffsetStd: float = pydantic.Field(math.nan, description="Astrometry match offset stddev.") 

70 

71 nPsfStar: int = pydantic.Field(0, description="Number of stars used for psf model.") 

72 

73 psfStarDeltaE1Median: float = pydantic.Field( 

74 math.nan, description="Psf stars median E1 residual (starE1 - psfE1)." 

75 ) 

76 

77 psfStarDeltaE2Median: float = pydantic.Field( 

78 math.nan, description="Psf stars median E2 residual (starE2 - psfE2)." 

79 ) 

80 

81 psfStarDeltaE1Scatter: float = pydantic.Field( 

82 math.nan, description="Psf stars MAD E1 scatter (starE1 - psfE1)." 

83 ) 

84 

85 psfStarDeltaE2Scatter: float = pydantic.Field( 

86 math.nan, description="Psf stars MAD E2 scatter (starE2 - psfE2)." 

87 ) 

88 

89 psfStarDeltaSizeMedian: float = pydantic.Field( 

90 math.nan, description="Psf stars median size residual (starSize - psfSize)." 

91 ) 

92 

93 psfStarDeltaSizeScatter: float = pydantic.Field( 

94 math.nan, description="Psf stars MAD size scatter (starSize - psfSize)." 

95 ) 

96 

97 psfStarScaledDeltaSizeScatter: float = pydantic.Field( 

98 math.nan, description="Psf stars MAD size scatter scaled by psfSize**2." 

99 ) 

100 

101 psfTraceRadiusDelta: float = pydantic.Field( 

102 math.nan, 

103 description=( 

104 "Delta (max - min) of the model psf trace radius values evaluated on a grid of " 

105 "unmasked pixels (pixels)." 

106 ), 

107 ) 

108 

109 psfApFluxDelta: float = pydantic.Field( 

110 math.nan, 

111 description=( 

112 "Delta (max - min) of the model psf aperture flux (with aperture radius of max(2, 3*psfSigma)) " 

113 "values evaluated on a grid of unmasked pixels." 

114 ), 

115 ) 

116 

117 psfApCorrSigmaScaledDelta: float = pydantic.Field( 

118 math.nan, 

119 description=( 

120 "Delta (max - min) of the psf flux aperture correction factors scaled (divided) by the " 

121 "psfSigma evaluated on a grid of unmasked pixels." 

122 ), 

123 ) 

124 

125 maxDistToNearestPsf: float = pydantic.Field( 

126 math.nan, 

127 description="Maximum distance of an unmasked pixel to its nearest model psf star (pixels).", 

128 ) 

129 

130 starEMedian: float = pydantic.Field( 

131 math.nan, 

132 description=( 

133 "Median ellipticity (sqrt(starE1**2.0 + starE2**2.0)) of the stars used in the PSF model." 

134 ), 

135 ) 

136 

137 starUnNormalizedEMedian: float = pydantic.Field( 

138 math.nan, 

139 description=( 

140 "Median un-normalized ellipticity (sqrt((starXX - starYY)**2.0 + " 

141 "(2.0*starXY)**2.0)) of the stars used in the PSF model." 

142 ), 

143 ) 

144 

145 effTime: float = pydantic.Field( 

146 math.nan, 

147 description="Effective exposure time calculated from psfSigma, skyBg, and zeroPoint (seconds).", 

148 ) 

149 

150 effTimePsfSigmaScale: float = pydantic.Field( 

151 math.nan, description="PSF scaling of the effective exposure time." 

152 ) 

153 

154 effTimeSkyBgScale: float = pydantic.Field( 

155 math.nan, description="Sky background scaling of the effective exposure time." 

156 ) 

157 

158 effTimeZeroPointScale: float = pydantic.Field( 

159 math.nan, description="Zeropoint scaling of the effective exposure time." 

160 ) 

161 

162 magLim: float = pydantic.Field( 

163 math.nan, 

164 description=( 

165 "Magnitude limit at fixed SNR (default SNR=5) calculated from psfSigma, skyBg," 

166 " zeroPoint, and readNoise." 

167 ), 

168 ) 

169 

170 @classmethod 

171 def from_legacy(cls, exposure_summary_stats: Any) -> Self: 

172 """Return an `ObservationSummaryStats` from a legacy 

173 `lsst.afw.image.ExposureSummaryStats`. 

174 """ 

175 # Assume that all the fields in an ExposureSummaryStats dataclass 

176 # are compatible with an ObservationSummaryStats. 

177 summary_stats = dataclasses.asdict(exposure_summary_stats) 

178 return cls.model_validate(summary_stats)