Coverage for tests / test_fit_coadd.py: 36%

105 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-06 09:09 +0000

1# This file is part of meas_extensions_multiprofit. 

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 

22import os 

23 

24from astropy.table import Table 

25from lsst.afw.image import ExposureF 

26from lsst.afw.table import SourceCatalog 

27from lsst.daf.butler.formatters.parquet import arrow_to_astropy 

28import lsst.gauss2d.fit as g2f 

29import lsst.meas.extensions.multiprofit.fit_coadd_multiband as fitCMB 

30import lsst.meas.extensions.multiprofit.fit_coadd_psf as fitCP 

31from lsst.multiprofit.componentconfig import ( 

32 CentroidConfig, 

33 GaussianComponentConfig, 

34 ParameterConfig, 

35 SersicComponentConfig, 

36 SersicIndexParameterConfig, 

37) 

38from lsst.multiprofit.fitting.fit_psf import CatalogPsfFitterConfig 

39from lsst.multiprofit.modelconfig import ModelConfig 

40from lsst.multiprofit.sourceconfig import ComponentGroupConfig, SourceConfig 

41from lsst.pipe.tasks.fit_coadd_psf import CatalogExposurePsf 

42import numpy as np 

43import pytest 

44 

45ROOT = os.environ.get("TESTDATA_CI_IMSIM_MINI_DIR", None) 

46has_files = (ROOT is not None) and os.path.isdir(ROOT) 

47 

48filename_cat = os.path.join(ROOT, "data", "deepCoadd_meas_0_24_r_2k_ci_imsim.fits") if has_files else None 

49filename_exp = os.path.join(ROOT, "data", "deepCoadd_calexp_0_24_r_2k_ci_imsim.fits") if has_files else None 

50 

51band = "r" 

52channel = g2f.Channel.get(band) 

53dataId = {"band": band} 

54do_exp_fixedcen = False 

55include_ps = False 

56n_test = 5 

57 

58 

59@pytest.fixture(scope="module") 

60def catalog(): 

61 if not has_files: 

62 return None 

63 catalog = SourceCatalog.readFits(filename_cat) 

64 good = (catalog["detect_isPrimary"] == 1) & (catalog["merge_peak_sky"] == 0) 

65 good[np.where(good)[0][n_test:]] = False 

66 return catalog[good] 

67 

68 

69@pytest.fixture(scope="module") 

70def exposure(): 

71 if not has_files: 

72 return None 

73 return ExposureF.readFits(filename_exp) 

74 

75 

76@pytest.fixture(scope="module") 

77def psf_fit_config(): 

78 return fitCP.MultiProFitPsfConfig() 

79 

80 

81@pytest.fixture(scope="module") 

82def psf_fit_results(catalog, exposure, psf_fit_config): 

83 if not has_files: 

84 return None 

85 catexp = CatalogExposurePsf(dataId=dataId, catalog=catalog, exposure=exposure) 

86 task = fitCP.MultiProFitPsfTask(config=psf_fit_config) 

87 results = task.run(catexp).output 

88 return arrow_to_astropy(results) 

89 

90 

91@pytest.fixture(scope="module") 

92def source_fit_exp_fixedcen_config(): 

93 config = fitCMB.MultiProFitSourceConfig( 

94 bands_fit=(band,), 

95 config_model=ModelConfig( 

96 sources={ 

97 "": SourceConfig( 

98 component_groups={ 

99 "": ComponentGroupConfig( 

100 centroids={ 

101 "default": CentroidConfig( 

102 x=ParameterConfig(fixed=True), 

103 y=ParameterConfig(fixed=True), 

104 ) 

105 }, 

106 components_sersic={ 

107 "exp": SersicComponentConfig( 

108 sersic_index=SersicIndexParameterConfig(value_initial=1.0, fixed=True), 

109 ) 

110 }, 

111 ), 

112 } 

113 ), 

114 }, 

115 ), 

116 ) 

117 config.validate() 

118 return config 

119 

120 

121@pytest.fixture(scope="module") 

122def source_fit_ser_config(): 

123 config = fitCMB.MultiProFitSourceConfig( 

124 bands_fit=(band,), 

125 config_model=ModelConfig( 

126 sources={ 

127 "": SourceConfig( 

128 component_groups={ 

129 "": ComponentGroupConfig( 

130 components_gauss=( 

131 { 

132 "ps": GaussianComponentConfig( 

133 size_x=ParameterConfig(value_initial=0.0, fixed=True), 

134 size_y=ParameterConfig(value_initial=0.0, fixed=True), 

135 rho=ParameterConfig(value_initial=0.0, fixed=True), 

136 ) 

137 } 

138 if include_ps 

139 else {} 

140 ), 

141 components_sersic={ 

142 "ser": SersicComponentConfig( 

143 sersic_index=SersicIndexParameterConfig(value_initial=1.0), 

144 ) 

145 }, 

146 ), 

147 } 

148 ), 

149 }, 

150 ), 

151 ) 

152 config.validate() 

153 return config 

154 

155 

156@pytest.fixture(scope="module") 

157def source_fit_exp_fixedcen_results( 

158 catalog, 

159 exposure, 

160 psf_fit_results, 

161 psf_fit_config, 

162 source_fit_exp_fixedcen_config, 

163) -> Table: 

164 if not has_files: 

165 return None 

166 if not do_exp_fixedcen: 

167 return None 

168 catexp = fitCMB.CatalogExposurePsfs( 

169 dataId=dataId, 

170 catalog=catalog, 

171 exposure=exposure, 

172 table_psf_fits=psf_fit_results, 

173 channel=channel, 

174 config_fit=source_fit_exp_fixedcen_config, 

175 ) 

176 task = fitCMB.MultiProFitSourceTask(config=source_fit_exp_fixedcen_config) 

177 results = task.run(catalog_multi=catalog, catexps=[catexp]) 

178 return arrow_to_astropy(results.output) 

179 

180 

181@pytest.fixture(scope="module") 

182def source_fit_ser_results( 

183 catalog, 

184 exposure, 

185 psf_fit_results, 

186 psf_fit_config, 

187 source_fit_ser_config, 

188) -> Table: 

189 if not has_files: 

190 return None 

191 catexp = fitCMB.CatalogExposurePsfs( 

192 dataId=dataId, 

193 catalog=catalog, 

194 exposure=exposure, 

195 table_psf_fits=psf_fit_results, 

196 channel=channel, 

197 config_fit=source_fit_ser_config, 

198 ) 

199 task = fitCMB.MultiProFitSourceTask(config=source_fit_ser_config) 

200 results = task.run(catalog_multi=catalog, catexps=[catexp]) 

201 return arrow_to_astropy(results.output) 

202 

203 

204@pytest.fixture(scope="module") 

205def source_fit_ser_shapelet_psf_results( 

206 catalog, 

207 exposure, 

208 psf_fit_results, 

209 psf_fit_config, 

210 source_fit_ser_config, 

211) -> Table: 

212 if not has_files: 

213 return None 

214 table_psf = Table( 

215 meta=dict(config=CatalogPsfFitterConfig().toDict()), 

216 ) 

217 catexp = fitCMB.CatalogExposurePsfs( 

218 dataId=dataId, 

219 catalog=catalog, 

220 exposure=exposure, 

221 table_psf_fits=table_psf, 

222 channel=channel, 

223 config_fit=source_fit_ser_config, 

224 ) 

225 source_fit_ser_config.action_psf = fitCMB.SourceTablePsfComponentsAction() 

226 task = fitCMB.MultiProFitSourceTask(config=source_fit_ser_config) 

227 results = task.run(catalog_multi=catalog, catexps=[catexp]) 

228 return arrow_to_astropy(results.output) 

229 

230 

231@pytest.fixture(scope="module") 

232def source_fits_all( 

233 source_fit_exp_fixedcen_results, 

234 source_fit_ser_results, 

235 source_fit_ser_shapelet_psf_results, 

236): 

237 return ( 

238 source_fit_exp_fixedcen_results, 

239 source_fit_ser_results, 

240 source_fit_ser_shapelet_psf_results, 

241 ) 

242 

243 

244def test_psf_fits(psf_fit_results): 

245 if psf_fit_results is not None: 

246 assert len(psf_fit_results) == n_test 

247 for column in psf_fit_results.columns: 

248 assert column and np.all(np.isfinite(psf_fit_results[column])) 

249 # TODO: Determine what checks can be done against previous values 

250 

251 

252def test_source_fits(source_fits_all): 

253 for results in source_fits_all: 

254 if results is not None: 

255 assert len(results) == n_test 

256 prefix = results.meta["config"]["prefix_column"] 

257 good = ~results[f"{prefix}unknown_flag"] 

258 assert np.sum(~good) == 0 

259 for column in results.columns: 

260 if column.startswith(prefix): 

261 assert column and (np.sum(~np.isfinite(results[column][good])) == 0) 

262 # TODO: Determine what checks can be done against previous values