Coverage for python/lsst/images/psfs/_base.py: 2%

40 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-30 08:38 +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. 

11 

12from __future__ import annotations 

13 

14__all__ = ("PointSpreadFunction",) 

15 

16from abc import ABC, abstractmethod 

17from typing import Any 

18 

19from .._geom import Bounds, Box 

20from .._image import Image 

21from ..describe import DescribableMixin, DescribeOptions, FieldRole, Report, ReportField 

22 

23 

24class PointSpreadFunction(DescribableMixin, ABC): 

25 """Base class for point-spread function models.""" 

26 

27 @property 

28 @abstractmethod 

29 def bounds(self) -> Bounds: 

30 """The region where this PSF model is valid.""" 

31 raise NotImplementedError() 

32 

33 @property 

34 @abstractmethod 

35 def kernel_bbox(self) -> Box: 

36 """Bounding box of all images returned by `compute_kernel_image`.""" 

37 raise NotImplementedError() 

38 

39 @abstractmethod 

40 def compute_kernel_image(self, *, x: float, y: float) -> Image: 

41 """Evaluate the PSF model into an image suitable for convolution. 

42 

43 Parameters 

44 ---------- 

45 x 

46 Column position coordinate to evaluate at. 

47 y 

48 Row position coordinate to evaluate at. 

49 

50 Returns 

51 ------- 

52 Image 

53 An image of the PSF, centered on the center of the center pixel, 

54 which is defined to be ``(0, 0)`` by the image's origin. 

55 """ 

56 raise NotImplementedError() 

57 

58 @abstractmethod 

59 def compute_stellar_image(self, *, x: float, y: float) -> Image: 

60 """Evaluate the PSF model into an image suitable for comparison with 

61 the image of an astrophysical point source. 

62 

63 Parameters 

64 ---------- 

65 x 

66 Column position coordinate to evaluate at. 

67 y 

68 Row position coordinate to evaluate at. 

69 

70 Returns 

71 ------- 

72 Image 

73 An image of the PSF, centered on the given coordinates, just like 

74 the postage stamp of a star would be. 

75 """ 

76 raise NotImplementedError() 

77 

78 @abstractmethod 

79 def compute_stellar_bbox(self, *, x: float, y: float) -> Box: 

80 """Return the bounding box of the image that would be returned by 

81 `compute_stellar_image`. 

82 

83 Parameters 

84 ---------- 

85 x 

86 Column position coordinate to evaluate at. 

87 y 

88 Row position coordinate to evaluate at. 

89 

90 Returns 

91 ------- 

92 Box 

93 The bounding box of the image that would be returned by 

94 `compute_stellar_image` at the given point. 

95 """ 

96 raise NotImplementedError() 

97 

98 @classmethod 

99 def from_legacy(cls, legacy_psf: Any, bounds: Bounds) -> PointSpreadFunction: 

100 """Make a PSF object from a legacy `lsst.afw.detection.Psf` instance. 

101 

102 Parameters 

103 ---------- 

104 legacy_psf 

105 Legacy PSF object. 

106 bounds 

107 The region where this PSF model is valid. 

108 

109 Returns 

110 ------- 

111 `~lsst.images.psfs.PointSpreadFunction` 

112 The converted PSF object. 

113 

114 Notes 

115 ----- 

116 This base class method is a factory dispatch function that 

117 automatically selects the right 

118 `~lsst.images.psfs.PointSpreadFunction` subclass to use. When that is 

119 already known, a subclass `from_legacy` method can be called instead. 

120 """ 

121 from lsst.afw.detection import Psf 

122 from lsst.cell_coadds import StitchedPsf 

123 from lsst.meas.extensions.piff.piffPsf import PiffPsf 

124 

125 match legacy_psf: 

126 case PiffPsf(): 

127 from ._piff import PiffWrapper 

128 

129 return PiffWrapper.from_legacy(legacy_psf, bounds) 

130 case StitchedPsf(): 

131 from ..cells import CellPointSpreadFunction 

132 

133 return CellPointSpreadFunction.from_legacy(legacy_psf, bounds) 

134 case Psf(): 

135 from ._legacy import LegacyPointSpreadFunction 

136 

137 return LegacyPointSpreadFunction.from_legacy(legacy_psf, bounds) 

138 case _: 

139 raise TypeError(f"{type(legacy_psf).__name__!r} is not a recognized legacy PSF type.") 

140 

141 def _describe(self, options: DescribeOptions = DescribeOptions(), /) -> Report: 

142 """Return a `Report` describing this PSF. 

143 

144 Parameters 

145 ---------- 

146 options : `DescribeOptions`, optional 

147 Unused; accepted for interface compatibility. 

148 """ 

149 return Report( 

150 type_name=type(self).__name__, 

151 summary=f"{type(self).__name__} over {self.bounds}", 

152 fields=[ 

153 ReportField(label="bounds", value=self.bounds, role=FieldRole.DERIVED), 

154 ReportField(label="kernel_bbox", value=self.kernel_bbox, role=FieldRole.DERIVED), 

155 ], 

156 ) 

157 

158 def to_legacy(self) -> Any: 

159 """Convert to a legacy `lsst.afw.detection.Psf`, if possible.""" 

160 raise NotImplementedError("This PSF does not support conversion to lsst.afw.detection.Psf.")