Coverage for tests/scenes.py: 100%

19 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-05 01:45 -0700

1# This file is part of meas_extensions_scarlet. 

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 

22"""Named, atomic source layouts for the deblender test suite. 

23 

24Each :class:`Scene` carries a list of :class:`DeblenderTestModel` 

25instances plus the band ordering it is to be rendered in. Scenes 

26hold no rendered image data; rendering is a job for 

27``pipeline.build_image``, which caches its output keyed by scene 

28name. 

29 

30The ``"multi-blend"`` scene is the union of the four per-blend 

31scenes and reproduces the historic 8-source / 4-blend layout that 

32``test_deblend.py`` has used since the package was first written. 

33""" 

34 

35from dataclasses import dataclass 

36 

37import numpy as np 

38 

39from utils import DeblenderTestModel, PsfModel, SersicModel 

40 

41 

42# Band ordering shared by every scene (`tuple` [`str`]). 

43BANDS: tuple[str, ...] = tuple("gri") 

44 

45 

46@dataclass(frozen=True) 

47class Scene: 

48 """Named layout of synthetic sources for one deblender test case. 

49 

50 A scene is pure metadata: it names the layout, lists the bands it is 

51 to be rendered in, and carries the source models. Turning it into 

52 pixel data is the job of ``pipeline.build_image``. 

53 

54 Parameters 

55 ---------- 

56 name : `str` 

57 Identifier used as the rendering cache key and as the test ID 

58 in parametrized tests. 

59 bands : `tuple` [`str`] 

60 Band ordering for the rendered image; the model spectra are 

61 indexed in this order. 

62 description : `str` 

63 Short human-readable summary of the layout, surfaced in test 

64 failure messages. 

65 models : `list` [`DeblenderTestModel`] 

66 Source models that make up the scene. 

67 """ 

68 

69 name: str 

70 bands: tuple[str, ...] 

71 description: str 

72 models: list[DeblenderTestModel] 

73 

74 

75def _one_isolated_psf_models() -> list[DeblenderTestModel]: 

76 """Build the models for the ``one_isolated_psf`` scene.""" 

77 return [ 

78 PsfModel(center=(30, 15), spectrum=np.array([8, 2, 1]), bands=BANDS), 

79 ] 

80 

81 

82def _psf_plus_sersic_blend_models() -> list[DeblenderTestModel]: 

83 """Build the models for the ``psf_plus_sersic_blend`` scene.""" 

84 return [ 

85 SersicModel( 

86 center=(40, 20), major=5, minor=2, radius=15, theta=-np.pi / 4, 

87 n=1, spectrum=np.array([2, 4, 8]), bands=BANDS, 

88 ), 

89 PsfModel(center=(12, 20), spectrum=np.array([1, 2, 8]), bands=BANDS), 

90 ] 

91 

92 

93def _three_source_blend_models() -> list[DeblenderTestModel]: 

94 """Build the models for the ``three_source_blend`` scene.""" 

95 return [ 

96 SersicModel( 

97 center=(25, 70), major=5, minor=2, radius=20, theta=np.pi / 48, 

98 n=1, spectrum=np.array([2, 5, 8]), bands=BANDS, 

99 ), 

100 PsfModel(center=(32, 60), spectrum=np.array([1, 2, 8]), bands=BANDS), 

101 PsfModel(center=(16, 80), spectrum=np.array([8, 2, 1]), bands=BANDS), 

102 ] 

103 

104 

105def _large_two_sersic_models() -> list[DeblenderTestModel]: 

106 """Build the models for the ``large_two_sersic`` scene.""" 

107 return [ 

108 SersicModel( 

109 center=(70, 70), major=5, minor=2, radius=25, theta=0, 

110 n=1, spectrum=np.array([2, 10, 18]), bands=BANDS, 

111 ), 

112 SersicModel( 

113 center=(85, 85), major=5, minor=2, radius=25, theta=np.pi / 2, 

114 n=1, spectrum=np.array([5, 10, 20]), bands=BANDS, 

115 ), 

116 ] 

117 

118 

119# Registry of every available scene, keyed by scene name 

120SCENES: dict[str, Scene] = { 

121 "one_isolated_psf": Scene( 

122 name="one_isolated_psf", 

123 bands=BANDS, 

124 description="One isolated PSF source, away from any neighbor.", 

125 models=_one_isolated_psf_models(), 

126 ), 

127 "psf_plus_sersic_blend": Scene( 

128 name="psf_plus_sersic_blend", 

129 bands=BANDS, 

130 description="One Sersic with a nearby PSF overlapping its wing.", 

131 models=_psf_plus_sersic_blend_models(), 

132 ), 

133 "three_source_blend": Scene( 

134 name="three_source_blend", 

135 bands=BANDS, 

136 description="One Sersic plus two PSFs in a single blend.", 

137 models=_three_source_blend_models(), 

138 ), 

139 "large_two_sersic": Scene( 

140 name="large_two_sersic", 

141 bands=BANDS, 

142 description="Two large overlapping Sersics.", 

143 models=_large_two_sersic_models(), 

144 ), 

145 "multi-blend": Scene( 

146 name="multi-blend", 

147 bands=BANDS, 

148 description=( 

149 "Composite scene: one isolated PSF, one PSF+Sersic blend, " 

150 "one three-source blend, and one two-Sersic blend. " 

151 "Reproduces the historic 8-source / 4-blend layout from " 

152 "test_deblend.py." 

153 ), 

154 models=( 

155 _one_isolated_psf_models() 

156 + _psf_plus_sersic_blend_models() 

157 + _three_source_blend_models() 

158 + _large_two_sersic_models() 

159 ), 

160 ), 

161}