Coverage for metadetect / lsst / tests / test_lsst_metadetect.py: 14%

293 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-17 16:57 -0800

1""" 

2test using lsst simple sim 

3""" 

4import sys 

5import numpy as np 

6import pytest 

7 

8import logging 

9import ngmix 

10import metadetect 

11from metadetect import procflags 

12from metadetect.lsst.metadetect import run_metadetect, get_fitter 

13from metadetect.lsst.configs import get_config 

14from metadetect.lsst import util 

15import lsst.afw.image as afw_image 

16from lsst.utils import getPackageDir 

17 

18try: 

19 getPackageDir('descwl_shear_sims') 

20 getPackageDir('descwl_coadd') 

21 skip_tests_on_simulations = False 

22except LookupError: 

23 skip_tests_on_simulations = True 

24 

25 

26ngmix_v = float(ngmix.__version__[:3]) 

27 

28logging.basicConfig( 

29 stream=sys.stdout, 

30 level=logging.INFO, 

31) 

32 

33 

34def make_lsst_sim(seed, mag=20, hlr=0.5, bands=None): 

35 import descwl_shear_sims 

36 

37 rng = np.random.RandomState(seed=seed) 

38 coadd_dim = 251 

39 

40 if bands is None: 

41 bands = ['i'] 

42 

43 galaxy_catalog = descwl_shear_sims.galaxies.FixedGalaxyCatalog( 

44 rng=rng, 

45 coadd_dim=coadd_dim, 

46 buff=20, 

47 layout='grid', 

48 mag=mag, 

49 hlr=hlr, 

50 ) 

51 

52 psf = descwl_shear_sims.psfs.make_fixed_psf(psf_type='gauss') 

53 

54 sim_data = descwl_shear_sims.make_sim( 

55 rng=rng, 

56 galaxy_catalog=galaxy_catalog, 

57 coadd_dim=coadd_dim, 

58 g1=0.02, 

59 g2=0.00, 

60 psf=psf, 

61 bands=bands, 

62 ) 

63 return sim_data 

64 

65 

66def do_coadding(rng, sim_data, nowarp): 

67 from descwl_coadd.coadd import make_coadd 

68 from descwl_coadd.coadd_nowarp import make_coadd_nowarp 

69 

70 bands = list(sim_data['band_data'].keys()) 

71 

72 if nowarp: 

73 coadd_data_list = [ 

74 make_coadd_nowarp( 

75 exp=sim_data['band_data'][band][0], 

76 psf_dims=sim_data['psf_dims'], 

77 rng=rng, 

78 remove_poisson=False, 

79 ) 

80 for band in bands 

81 ] 

82 else: 

83 coadd_data_list = [ 

84 make_coadd( 

85 exps=sim_data['band_data'][band], 

86 psf_dims=sim_data['psf_dims'], 

87 rng=rng, 

88 coadd_wcs=sim_data['coadd_wcs'], 

89 coadd_bbox=sim_data['coadd_bbox'], 

90 remove_poisson=False, 

91 ) 

92 for band in bands 

93 ] 

94 

95 return util.extract_multiband_coadd_data(coadd_data_list) 

96 

97 

98@pytest.mark.skipif( 

99 skip_tests_on_simulations, 

100 reason='descwl_shear_sims and/or descwl_coadd not available' 

101) 

102@pytest.mark.parametrize('meas_type', [None, 'wmom', 'ksigma', 'pgauss']) 

103@pytest.mark.parametrize('subtract_sky', [None, False, True]) 

104@pytest.mark.parametrize("metacal_types_option", [None, "1p1m", "full"]) 

105def test_lsst_metadetect_smoke(meas_type, subtract_sky, metacal_types_option): 

106 rng = np.random.RandomState(seed=116) 

107 

108 bands = ['r', 'i'] 

109 sim_data = make_lsst_sim(116, bands=bands) 

110 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=True) 

111 

112 config = {} 

113 

114 if subtract_sky is not None: 

115 config['subtract_sky'] = subtract_sky 

116 

117 if meas_type is not None: 

118 config['meas_type'] = meas_type 

119 

120 if metacal_types_option is not None: 

121 if metacal_types_option == "1p1m": 

122 metacal_types = ['noshear', '1p', '1m'] 

123 config['metacal'] = {} 

124 elif metacal_types_option == "full": 

125 metacal_types = ['noshear', '1p', '1m', '2p', '2m'] 

126 config['metacal'] = {} 

127 config['metacal']['types'] = metacal_types 

128 else: 

129 metacal_types = ['noshear', '1p', '1m'] 

130 

131 detected = afw_image.Mask.getPlaneBitMask('DETECTED') 

132 res = run_metadetect(rng=rng, config=config, **data) 

133 

134 # we remove the DETECTED bit 

135 assert np.all(res['noshear']['bmask'] & detected == 0) 

136 

137 for metacal_type in metacal_types: 

138 assert ( 

139 metacal_type in res.keys() 

140 ), f"metacal_type={metacal_type} not in res.keys()" 

141 

142 if meas_type is None: 

143 front = 'wmom' 

144 else: 

145 front = meas_type 

146 

147 gname = f'{front}_g' 

148 flux_name = f'{front}_band_flux' 

149 assert gname in res['noshear'].dtype.names 

150 

151 for shear in metacal_types: 

152 # 5x5 grid 

153 assert res[shear].size == 25 

154 

155 assert np.any(res[shear][f"{front}_flags"] == 0) 

156 assert np.all(res[shear]["mfrac"] == 0) 

157 

158 assert len(res[shear][flux_name].shape) == len(bands) 

159 assert len(res[shear][flux_name][0]) == len(bands) 

160 

161 

162@pytest.mark.skipif( 

163 skip_tests_on_simulations, 

164 reason='descwl_shear_sims and/or descwl_coadd not available' 

165) 

166@pytest.mark.skipif(ngmix_v < 2.1, reason="requires ngmix 2.1 or higher") 

167@pytest.mark.parametrize('meas_type', ['wmom', 'ksigma', 'pgauss']) 

168@pytest.mark.parametrize('fwhm_smooth', [None, 1.2]) 

169def test_lsst_metadetect_weight(meas_type, fwhm_smooth): 

170 rng = np.random.RandomState(seed=882) 

171 

172 bands = ['r', 'i'] 

173 sim_data = make_lsst_sim(116, bands=bands) 

174 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=True) 

175 

176 fwhm = 2.0 

177 config = { 

178 'meas_type': meas_type, 

179 'weight': { 

180 'fwhm': fwhm, 

181 } 

182 } 

183 if meas_type != 'wmom' and fwhm_smooth is not None: 

184 config['weight']['fwhm_smooth'] = fwhm_smooth 

185 

186 fitter = get_fitter(config=get_config(config), rng=rng) 

187 assert fitter.fwhm == fwhm 

188 if meas_type != 'wmom' and fwhm_smooth is not None: 

189 assert fitter.fwhm_smooth == fwhm_smooth 

190 

191 res = run_metadetect(rng=rng, config=config, **data) 

192 

193 gname = f'{meas_type}_g' 

194 flux_name = f'{meas_type}_band_flux' 

195 assert gname in res['noshear'].dtype.names 

196 

197 for shear in ('noshear', '1p', '1m'): 

198 # 5x5 grid 

199 assert res[shear].size == 25 

200 

201 assert np.any(res[shear][f"{meas_type}_flags"] == 0) 

202 assert np.all(res[shear]["mfrac"] == 0) 

203 

204 assert len(res[shear][flux_name].shape) == len(bands) 

205 assert len(res[shear][flux_name][0]) == len(bands) 

206 

207 

208@pytest.mark.skipif( 

209 skip_tests_on_simulations, 

210 reason='descwl_shear_sims and/or descwl_coadd not available' 

211) 

212@pytest.mark.parametrize('fwhm_reg', [0, 0.8]) 

213def test_lsst_metadetect_fwhm_reg(fwhm_reg): 

214 rng = np.random.RandomState(seed=882) 

215 

216 meas_type = 'pgauss' 

217 bands = ['r', 'i'] 

218 sim_data = make_lsst_sim(35, bands=bands) 

219 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=True) 

220 

221 fwhm = 2.0 

222 config = { 

223 'meas_type': meas_type, 

224 'weight': { 

225 'fwhm': fwhm, 

226 'fwhm_reg': fwhm_reg, 

227 } 

228 } 

229 

230 fitter = get_fitter(config=get_config(config), rng=rng) 

231 assert fitter.fwhm == fwhm 

232 

233 res = run_metadetect(rng=rng, config=config, **data) 

234 

235 gname = f'{meas_type}_g' 

236 flux_name = f'{meas_type}_band_flux' 

237 assert gname in res['noshear'].dtype.names 

238 

239 for shear in ('noshear', '1p', '1m'): 

240 # 5x5 grid 

241 assert res[shear].size == 25 

242 

243 assert np.any(res[shear][f"{meas_type}_flags"] == 0) 

244 assert np.all(res[shear]["mfrac"] == 0) 

245 

246 assert len(res[shear][flux_name].shape) == len(bands) 

247 assert len(res[shear][flux_name][0]) == len(bands) 

248 

249 

250@pytest.mark.skipif( 

251 skip_tests_on_simulations, 

252 reason='descwl_shear_sims and/or descwl_coadd not available' 

253) 

254def test_lsst_metadetect_fwhm_reg_shapenoise(): 

255 

256 sdevs = {} 

257 for fwhm_reg in [0, 0.8]: 

258 meas_type = 'pgauss' 

259 bands = ['r', 'i'] 

260 sim_data = make_lsst_sim(35, bands=bands) 

261 rng = np.random.RandomState(seed=882) 

262 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=True) 

263 

264 fwhm = 2.0 

265 config = { 

266 'meas_type': meas_type, 

267 'weight': { 

268 'fwhm': fwhm, 

269 'fwhm_reg': fwhm_reg, 

270 } 

271 } 

272 

273 fitter = get_fitter(config=get_config(config), rng=rng) 

274 assert fitter.fwhm == fwhm 

275 

276 res = run_metadetect(rng=rng, config=config, **data) 

277 

278 w, = np.where(res['noshear'][f'{meas_type}_flags'] == 0) 

279 

280 sdevs[fwhm_reg] = res['noshear'][f'{meas_type}_g'][w, 0].std() 

281 

282 assert sdevs[0.8] < 0.6 * sdevs[0] 

283 

284 

285@pytest.mark.skipif( 

286 skip_tests_on_simulations, 

287 reason='descwl_shear_sims and/or descwl_coadd not available' 

288) 

289def test_lsst_metadetect_am(): 

290 rng = np.random.RandomState(seed=882) 

291 

292 # only single band for am currently 

293 bands = ['i'] 

294 sim_data = make_lsst_sim(116, bands=bands) 

295 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=True) 

296 

297 meas_type = 'am' 

298 config = {'meas_type': meas_type} 

299 

300 res = run_metadetect(rng=rng, config=config, **data) 

301 

302 gname = f'{meas_type}_g' 

303 flux_name = f'{meas_type}_band_flux' 

304 assert gname in res['noshear'].dtype.names 

305 

306 for shear in ('noshear', '1p', '1m'): 

307 # 5x5 grid 

308 assert res[shear].size == 25 

309 

310 assert np.any(res[shear]["am_flags"] == 0) 

311 assert np.all(res[shear]["mfrac"] == 0) 

312 

313 assert len(res[shear][flux_name].shape) == len(bands) 

314 with pytest.raises(TypeError): 

315 len(res[shear][flux_name][0]) 

316 

317 

318@pytest.mark.skipif( 

319 skip_tests_on_simulations, 

320 reason='descwl_shear_sims and/or descwl_coadd not available' 

321) 

322def test_lsst_metadetect_gauss(): 

323 rng = np.random.RandomState(seed=918) 

324 

325 bands = ['r', 'i', 'z'] 

326 sim_data = make_lsst_sim(116, bands=bands) 

327 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=True) 

328 

329 meas_type = 'gauss' 

330 config = {'meas_type': meas_type} 

331 

332 res = run_metadetect(rng=rng, config=config, **data) 

333 

334 gname = f'{meas_type}_g' 

335 flux_name = f'{meas_type}_band_flux' 

336 assert gname in res['noshear'].dtype.names 

337 

338 for shear in ('noshear', '1p', '1m'): 

339 # 5x5 grid of sources 

340 assert res[shear].size == 25 

341 

342 assert np.all(res[shear][f"{meas_type}_flags"] == 0) 

343 assert np.all(res[shear]["mfrac"] == 0) 

344 

345 assert res[shear][flux_name].shape[1] == len(bands) 

346 assert np.all(np.isfinite(res[shear][flux_name])) 

347 

348 

349@pytest.mark.skipif( 

350 skip_tests_on_simulations, 

351 reason='descwl_shear_sims and/or descwl_coadd not available' 

352) 

353def test_lsst_metadetect_fullcoadd_smoke(): 

354 rng = np.random.RandomState(seed=116) 

355 

356 bands = ['r', 'i'] 

357 sim_data = make_lsst_sim(882, bands=bands) 

358 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=False) 

359 

360 config = {'meas_type': 'pgauss'} 

361 res = run_metadetect(config=config, rng=rng, **data) 

362 

363 front = 'pgauss' 

364 gname = f'{front}_g' 

365 flux_name = f'{front}_band_flux' 

366 assert gname in res['noshear'].dtype.names 

367 

368 for shear in ('noshear', '1p', '1m'): 

369 # 5x5 grid 

370 assert res[shear].size == 25 

371 

372 assert np.any(res[shear]["pgauss_flags"] == 0) 

373 assert np.all(res[shear]["mfrac"] == 0) 

374 

375 assert len(res[shear][flux_name].shape) == len(bands) 

376 assert len(res[shear][flux_name][0]) == len(bands) 

377 

378 

379@pytest.mark.skipif( 

380 skip_tests_on_simulations, 

381 reason='descwl_shear_sims and/or descwl_coadd not available' 

382) 

383def test_lsst_zero_weights(show=False): 

384 """ 

385 At time of writing, DM stack will still detect in regions with inf 

386 variance. Test this continues to be true. 

387 

388 However, we don't have detections in BRIGHT, see test 

389 test_lsst_masked_as_bright 

390 """ 

391 nobj = [] 

392 seed = 55 

393 for do_zero in [False, True]: 

394 rng = np.random.RandomState(seed) 

395 sim_data = make_lsst_sim(seed, mag=23) 

396 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=False) 

397 

398 if do_zero: 

399 data['mbexp']['i'].variance.array[50:100, 50:100] = np.inf 

400 data['noise_mbexp']['i'].variance.array[50:100, 50:100] = np.inf 

401 

402 if show: 

403 import matplotlib.pyplot as mplt 

404 fig, axs = mplt.subplots(ncols=2) 

405 axs[0].imshow(data['mbexp']['i'].image.array) 

406 axs[1].imshow(data['mbexp']['i'].variance.array) 

407 mplt.show() 

408 

409 resdict = run_metadetect(rng=rng, config=None, **data) 

410 

411 if do_zero: 

412 for shear_type, tres in resdict.items(): 

413 assert np.any(tres['wmom_flags'] & procflags.ZERO_WEIGHTS != 0) 

414 assert np.any(tres['wmom_psf_flags'] & procflags.NO_ATTEMPT != 0) 

415 else: 

416 for shear_type, tres in resdict.items(): 

417 # 5x5 grid 

418 assert tres.size == 25 

419 

420 nobj.append(resdict['noshear'].size) 

421 

422 assert nobj[0] == nobj[1] 

423 

424 

425@pytest.mark.skipif( 

426 skip_tests_on_simulations, 

427 reason='descwl_shear_sims and/or descwl_coadd not available' 

428) 

429def test_lsst_masked_as_bright(show=False): 

430 """ 

431 Make sure we don't detect in areas marked BRIGHT 

432 """ 

433 seed = 55 

434 afw_image.Mask.addMaskPlane('BRIGHT') 

435 bright = afw_image.Mask.getPlaneBitMask('BRIGHT') 

436 for do_zero in [False, True]: 

437 rng = np.random.RandomState(seed) 

438 sim_data = make_lsst_sim(seed, mag=23) 

439 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=False) 

440 

441 if do_zero: 

442 data['mbexp']['i'].variance.array[50:100, 50:100] = np.inf 

443 data['mbexp']['i'].mask.array[50:100, 50:100] |= bright 

444 data['noise_mbexp']['i'].variance.array[50:100, 50:100] = np.inf 

445 data['noise_mbexp']['i'].mask.array[50:100, 50:100] |= bright 

446 

447 resdict = run_metadetect(rng=rng, config=None, **data) 

448 

449 if show: 

450 import matplotlib.pyplot as mplt 

451 fig, axs = mplt.subplots(ncols=2) 

452 axs[0].imshow(data['mbexp']['i'].image.array) 

453 axs[1].imshow(data['mbexp']['i'].variance.array) 

454 

455 axs[0].scatter( 

456 resdict['noshear']['col'] - resdict['noshear']['col0'], 

457 resdict['noshear']['row'] - resdict['noshear']['row0'], 

458 s=4, 

459 c='red', 

460 ) 

461 mplt.show() 

462 

463 if do_zero: 

464 for shear_type, tres in resdict.items(): 

465 assert tres.size == 24 

466 else: 

467 for shear_type, tres in resdict.items(): 

468 # 5x5 grid 

469 assert tres.size == 25 

470 

471 

472@pytest.mark.skipif( 

473 skip_tests_on_simulations, 

474 reason='descwl_shear_sims and/or descwl_coadd not available' 

475) 

476@pytest.mark.parametrize('meas_type', ['ksigma', 'pgauss']) 

477def test_lsst_metadetect_prepsf_stars(meas_type): 

478 seed = 55 

479 rng = np.random.RandomState(seed=seed) 

480 

481 sim_data = make_lsst_sim(seed, hlr=1.0e-4, mag=23) 

482 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=True) 

483 

484 config = {'meas_type': meas_type} 

485 

486 res = run_metadetect(rng=rng, config=config, **data) 

487 

488 n = metadetect.util.Namer(front=meas_type) 

489 

490 data = res['noshear'] 

491 

492 wlowT, = np.where(data[f'{meas_type}_flags'] != 0) 

493 wgood, = np.where(data[f'{meas_type}_flags'] == 0) 

494 

495 # some will have T < 0 due to noise. Expect some with flags set 

496 assert wlowT.size > 0 

497 

498 assert np.any((data[n('flags')][wlowT] & ngmix.flags.NONPOS_SIZE) != 0) 

499 

500 assert np.any(np.isnan(data[n('g')][wlowT])) 

501 for field in data.dtype.names: 

502 if field != "shear_bands": 

503 assert np.all(np.isfinite(data[field][wgood])), field 

504 

505 

506@pytest.mark.skipif( 

507 skip_tests_on_simulations, 

508 reason='descwl_shear_sims and/or descwl_coadd not available' 

509) 

510def test_lsst_metadetect_mfrac_ormask(show=False): 

511 rng = np.random.RandomState(seed=116) 

512 

513 ntrial = 1 

514 flag = 2**30 

515 

516 for trial in range(ntrial): 

517 sim_data = make_lsst_sim(rng.randint(0, 2**30)) 

518 data = do_coadding(rng=rng, sim_data=sim_data, nowarp=False) 

519 

520 data['mfrac_mbexp']['i'].image.array[:, :] = rng.uniform( 

521 size=data['mbexp']['i'].image.array.shape, low=0.2, high=0.8 

522 ) 

523 

524 for ormask in data['ormasks']: 

525 ormask[30:150, 30:150] = flag 

526 if show: 

527 import matplotlib.pyplot as mplt 

528 fig, axs = mplt.subplots(ncols=2) 

529 axs[0].imshow(data['mbexp']['i'].image.array) 

530 axs[1].imshow(ormask) 

531 mplt.show() 

532 

533 res = run_metadetect(config=None, rng=rng, **data) 

534 

535 for shear in ('noshear', '1p', '1m'): 

536 assert np.any(res[shear]["wmom_flags"] == 0) 

537 assert np.any( 

538 (res[shear]["mfrac"] > 0.40) 

539 & (res[shear]["mfrac"] < 0.60) 

540 ) 

541 assert np.any(res[shear]["ormask"] & flag != 0) 

542 

543 

544if __name__ == '__main__': 

545 # test_lsst_metadetect_am() 

546 test_lsst_metadetect_gauss() 

547 # test_lsst_masked_as_bright(show=True) 

548 # test_lsst_metadetect_smoke('wmom', 'False') 

549 # test_lsst_metadetect_mfrac_ormask(show=True)