diff --git a/doc/changes/DM-54482.feature.rst b/doc/changes/DM-54482.feature.rst new file mode 100644 index 00000000..6fdde17b --- /dev/null +++ b/doc/changes/DM-54482.feature.rst @@ -0,0 +1 @@ +Added psf star shapelet decomposition parameters and metrics to the ``ObservationSummaryStats`` class. diff --git a/python/lsst/images/_observation_summary_stats.py b/python/lsst/images/_observation_summary_stats.py index 3b6006a6..1138d70d 100644 --- a/python/lsst/images/_observation_summary_stats.py +++ b/python/lsst/images/_observation_summary_stats.py @@ -64,6 +64,69 @@ class ObservationSummaryStats(pydantic.BaseModel, ser_json_inf_nan="constants"): default_factory=_default_corners, description="Declination of bounding box corners (degrees)." ) + psfAdaptiveThresholdValue: float = pydantic.Field( + math.nan, + description="Threshold value used in the adaptive threshold detection pass for PSF modelling.", + ) + + psfAdaptiveIncludeThresholdMultiplier: float = pydantic.Field( + math.nan, + description="Threshold multiplier used in the adaptive threshold detection pass for PSF modelling.", + ) + + nShapeletsStar: int = pydantic.Field( + 0, + description="Number of sources used in the shapelet decomposition.", + ) + + shapeletsOnlyIqScore: float = pydantic.Field( + math.nan, + description=( + "The dimensionless image quality score as determined from the shapelets decomposition " + "that includes power only from the non-atmospheric decomposition coefficients. The " + "score spans the range [0.0, 1.0] with lower values indicating better image quality." + ), + ) + + shapeletsIqScore: float = pydantic.Field( + math.nan, + description=( + "The dimensionless image quality score as determined from the shapelets decomposition " + "that includes power from the median centroid offset between those used in the decomposition " + "and those of the centroid slot in addition to non-atmospheric decomposition coefficients. " + "The score spans the range [0.0, 1.0] with lower values indicating better image quality." + ), + ) + + shapeletsCoeffs: tuple[float, ...] = pydantic.Field( + default_factory=tuple, + description="Coefficients from the PSF star shapelet decomposition.", + ) + + centroidDiffShapeletsVsSlotMedian: float = pydantic.Field( + math.nan, + description=( + "Median centroid difference (sqrt((slot_x - shapelet_x)**2 + (slot_y - shapelet_y)**2)) for " + "sources used in the shapelet decomposition (pixels)." + ), + ) + + shapeletsStarEMedian: float = pydantic.Field( + math.nan, + description=( + "Median ellipticity (sqrt(starE1**2.0 + starE2**2.0)) of the sources used in the " + "shapelet decomposition." + ), + ) + + shapeletsStarUnNormalizedEMedian: float = pydantic.Field( + math.nan, + description=( + "Median un-normalized ellipticity (sqrt((starXX - starYY)**2.0 + (2.0*starXY)**2.0)) " + "of the sources used in the shapelet decomposition (pixel**2)." + ), + ) + astromOffsetMean: float = pydantic.Field(math.nan, description="Astrometry match offset mean.") astromOffsetStd: float = pydantic.Field(math.nan, description="Astrometry match offset stddev.") @@ -228,6 +291,8 @@ def __eq__(self, other: object) -> bool: a = getattr(self, name) b = getattr(other, name) if isinstance(a, tuple) and isinstance(b, tuple): + if len(a) != len(b): + return False for ai, bi in zip(a, b): if ai != bi and not (math.isnan(ai) and math.isnan(bi)): return False