Coverage for tests/test_coadds.py: 25%

213 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-25 11:26 -0700

1# This file is part of cell_coadds. 

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 unittest 

23from collections.abc import Iterable, Mapping 

24from itertools import product 

25 

26import lsst.cell_coadds.test_utils as test_utils 

27import lsst.geom as geom 

28import lsst.meas.base.tests 

29import lsst.utils.tests 

30import numpy as np 

31from lsst.afw.geom import Quadrupole 

32from lsst.afw.image import ExposureF, ImageF 

33from lsst.cell_coadds import ( 

34 CellCoaddFitsReader, 

35 CellIdentifiers, 

36 CoaddUnits, 

37 CommonComponents, 

38 ExplodedCoadd, 

39 MultipleCellCoadd, 

40 ObservationIdentifiers, 

41 OwnedImagePlanes, 

42 PatchIdentifiers, 

43 SingleCellCoadd, 

44 StitchedCoadd, 

45 UniformGrid, 

46) 

47from lsst.meas.algorithms import SingleGaussianPsf 

48from lsst.skymap import Index2D 

49 

50 

51class BaseMultipleCellCoaddTestCase(lsst.utils.tests.TestCase): 

52 """A base class that provides a common set of methods.""" 

53 

54 psf_size: int 

55 psf_sigmas: Mapping[Index2D, float] 

56 border_size: int 

57 inner_size: int 

58 outer_size: int 

59 test_positions: Iterable[tuple[geom.Point2D, Index2D]] 

60 exposures: Mapping[Index2D, lsst.afw.image.ExposureF] 

61 multiple_cell_coadd: MultipleCellCoadd 

62 

63 @classmethod 

64 def setUpClass(cls) -> None: 

65 """Set up a multiple cell coadd with 2x2 cells.""" 

66 np.random.seed(42) 

67 data_id = test_utils.generate_data_id() 

68 common = CommonComponents( 

69 units=CoaddUnits.legacy, # units here are arbitrary. 

70 wcs=test_utils.generate_wcs(), 

71 band=data_id["band"], 

72 identifiers=PatchIdentifiers.from_data_id(data_id), 

73 ) 

74 

75 cls.nx, cls.ny = 3, 2 

76 cls.psf_sigmas = { 

77 Index2D(x=0, y=0): 1.2, 

78 Index2D(x=0, y=1): 0.7, 

79 Index2D(x=1, y=0): 0.9, 

80 Index2D(x=1, y=1): 1.1, 

81 Index2D(x=2, y=0): 1.3, 

82 Index2D(x=2, y=1): 0.8, 

83 } 

84 

85 cls.border_size = 5 

86 # In practice, we expect this to be squares. 

87 # To check for any possible x, y swap, we use different values. 

88 cls.psf_size_x, cls.psf_size_y = 21, 23 

89 cls.inner_size_x, cls.inner_size_y = 17, 15 

90 cls.outer_size_x = cls.inner_size_x + 2 * cls.border_size 

91 cls.outer_size_y = cls.inner_size_y + 2 * cls.border_size 

92 # The origin should not be at (0, 0) for robust testing. 

93 cls.x0, cls.y0 = 5, 2 

94 

95 patch_outer_bbox = geom.Box2I( 

96 geom.Point2I(cls.x0, cls.y0), geom.Extent2I(cls.nx * cls.inner_size_x, cls.ny * cls.inner_size_y) 

97 ) 

98 patch_outer_bbox.grow(cls.border_size) 

99 

100 # Add one star and one galaxy per quadrant. 

101 # The mapping of positions to cell indices assume inner_size = (17, 15) 

102 # and border_size = 5. If that is changed, these values need an update. 

103 sources = ( 

104 # flux, centroid, shape 

105 (1000.0, geom.Point2D(cls.x0 + 6.3, cls.y0 + 7.2), None), 

106 (2500.0, geom.Point2D(cls.x0 + 16.8, cls.y0 + 18.3), None), 

107 (1500.0, geom.Point2D(cls.x0 + 21.2, cls.y0 + 5.1), None), 

108 (3200.0, geom.Point2D(cls.x0 + 16.1, cls.y0 + 23.9), None), 

109 (1800.0, geom.Point2D(cls.x0 + 44.7, cls.y0 + 8.9), None), 

110 (2100.0, geom.Point2D(cls.x0 + 34.1, cls.y0 + 19.2), None), 

111 (900.0, geom.Point2D(cls.x0 + 9.1, cls.y0 + 13.9), Quadrupole(2.5, 1.5, 0.8)), 

112 (1250.0, geom.Point2D(cls.x0 + 19.3, cls.y0 + 11.2), Quadrupole(1.5, 2.5, 0.75)), 

113 (2100.0, geom.Point2D(cls.x0 + 5.1, cls.y0 + 21.2), Quadrupole(1.7, 1.9, 0.05)), 

114 (2800.0, geom.Point2D(cls.x0 + 24.1, cls.y0 + 19.2), Quadrupole(1.9, 1.7, 0.1)), 

115 (2350.0, geom.Point2D(cls.x0 + 40.3, cls.y0 + 13.9), Quadrupole(1.8, 1.8, -0.4)), 

116 (4999.0, geom.Point2D(cls.x0 + 45.8, cls.y0 + 22.0), Quadrupole(1.6, 1.2, 0.2)), 

117 ) 

118 

119 # The test points are chosen to cover various corner cases assuming 

120 # inner_size = (17, 15) and border_size = 5. If that is changed, the 

121 # test points should be updated to not fall outside the coadd and still 

122 # cover the description in the inline comments. 

123 test_points = ( 

124 geom.Point2D(cls.x0 + 5, cls.y0 + 4), # inner point in lower left 

125 geom.Point2D(cls.x0 + 6, cls.y0 + 24), # inner point in upper left 

126 geom.Point2D(cls.x0 + 25.2, cls.y0 + 7.8), # inner point in lower middle 

127 geom.Point2D(cls.x0 + 23, cls.y0 + 22), # inner point in upper middle 

128 geom.Point2D(cls.x0 + 39, cls.y0 + 9.4), # inner point in lower right 

129 geom.Point2D(cls.x0 + 44, cls.y0 + 24), # inner point in upper right 

130 # Some points that lie on the border 

131 geom.Point2D(cls.x0 + 33, cls.y0 + 24), # inner point in upper right 

132 geom.Point2D(cls.x0 + 46, cls.y0 + 0), # inner point in lower right 

133 geom.Point2D(cls.x0 + 19, cls.y0 + 16), # inner point in upper middle 

134 geom.Point2D(cls.x0 + 17, cls.y0 + 8), # inner point in lower middle 

135 geom.Point2D(cls.x0 + 0, cls.y0 + 29), # inner point in upper left 

136 geom.Point2D(cls.x0 + 0, cls.y0 + 0), # inner point in lower left 

137 ) 

138 # A tuple of (point, cell_index) pairs. 

139 cls.test_positions = ( 

140 ( 

141 point, 

142 Index2D( 

143 x=int((point.getX() - cls.x0) // cls.inner_size_x), 

144 y=int((point.getY() - cls.y0) // cls.inner_size_y), 

145 ), 

146 ) 

147 for point in test_points 

148 ) 

149 

150 schema = lsst.meas.base.tests.TestDataset.makeMinimalSchema() 

151 

152 single_cell_coadds = [] 

153 cls.exposures = dict.fromkeys(cls.psf_sigmas.keys()) 

154 

155 for x in range(cls.nx): 

156 for y in range(cls.ny): 

157 identifiers = CellIdentifiers( 

158 cell=Index2D(x=x, y=y), 

159 skymap=common.identifiers.skymap, 

160 tract=common.identifiers.tract, 

161 patch=common.identifiers.patch, 

162 band=common.identifiers.band, 

163 ) 

164 

165 outer_bbox = geom.Box2I( 

166 geom.Point2I(cls.x0 + x * cls.inner_size_x, cls.y0 + y * cls.inner_size_y), 

167 geom.Extent2I(cls.inner_size_x, cls.inner_size_y), 

168 ) 

169 outer_bbox.grow(cls.border_size) 

170 

171 dataset = lsst.meas.base.tests.TestDataset( 

172 patch_outer_bbox, psfSigma=cls.psf_sigmas[identifiers.cell] 

173 ) 

174 

175 for inst_flux, position, shape in sources: 

176 dataset.addSource(inst_flux, position, shape) 

177 

178 # Create a spatially varying variance plane. 

179 variance = ImageF( 

180 # np.random.uniform returns an array with x-y flipped. 

181 np.random.uniform( 

182 0.8, 

183 1.2, 

184 ( 

185 cls.ny * cls.inner_size_y + 2 * cls.border_size, 

186 cls.nx * cls.inner_size_x + 2 * cls.border_size, 

187 ), 

188 ).astype(np.float32), 

189 xy0=outer_bbox.getMin(), 

190 ) 

191 exposure, _ = dataset.realize(variance.getArray() ** 0.5, schema, randomSeed=123456789) 

192 cls.exposures[identifiers.cell] = exposure 

193 exposure = exposure[outer_bbox] 

194 image_plane = OwnedImagePlanes( 

195 image=exposure.image, variance=exposure.variance, mask=exposure.mask 

196 ) 

197 

198 single_cell_coadds.append( 

199 SingleCellCoadd( 

200 outer=image_plane, 

201 psf=SingleGaussianPsf( 

202 cls.psf_size_x, cls.psf_size_y, cls.psf_sigmas[Index2D(x=x, y=y)] 

203 ).computeKernelImage(outer_bbox.getCenter()), 

204 inner_bbox=geom.Box2I( 

205 geom.Point2I(cls.x0 + x * cls.inner_size_x, cls.y0 + y * cls.inner_size_y), 

206 geom.Extent2I(cls.inner_size_x, cls.inner_size_y), 

207 ), 

208 inputs=( 

209 ObservationIdentifiers( 

210 instrument="dummy", 

211 physical_filter="dummy-I", 

212 visit=12345, 

213 detector=67, 

214 packed=13579, 

215 day_obs=20000101, 

216 ), 

217 ), 

218 common=common, 

219 identifiers=identifiers, 

220 ) 

221 ) 

222 

223 grid_bbox = geom.Box2I( 

224 geom.Point2I(cls.x0, cls.y0), geom.Extent2I(cls.nx * cls.inner_size_x, cls.ny * cls.inner_size_y) 

225 ) 

226 grid = UniformGrid.from_bbox_shape(grid_bbox, Index2D(x=cls.nx, y=cls.ny)) 

227 

228 cls.multiple_cell_coadd = MultipleCellCoadd( 

229 single_cell_coadds, 

230 grid=grid, 

231 outer_cell_size=geom.Extent2I(cls.outer_size_x, cls.outer_size_y), 

232 inner_bbox=None, 

233 common=common, 

234 psf_image_size=geom.Extent2I(cls.psf_size_x, cls.psf_size_y), 

235 ) 

236 

237 @classmethod 

238 def tearDownClass(cls) -> None: # noqa: D102 

239 # Docstring inherited 

240 del cls.multiple_cell_coadd 

241 del cls.exposures 

242 super().tearDownClass() 

243 

244 def assertMultipleCellCoaddsEqual(self, mcc1: MultipleCellCoadd, mcc2: MultipleCellCoadd) -> None: 

245 """Check the equality of two instances of `MultipleCellCoadd`. 

246 

247 Parameters 

248 ---------- 

249 mcc1 : `MultipleCellCoadd` 

250 The MultipleCellCoadd created by reading a FITS file. 

251 mcc2 : `MultipleCellCoadd` 

252 The reference MultipleCellCoadd for comparison. 

253 """ 

254 self.assertEqual(mcc1.band, mcc2.band) 

255 self.assertEqual(mcc1.identifiers, mcc2.identifiers) 

256 self.assertEqual(mcc1.inner_bbox, mcc2.inner_bbox) 

257 self.assertEqual(mcc1.outer_bbox, mcc2.outer_bbox) 

258 self.assertEqual(mcc1.outer_cell_size, mcc2.outer_cell_size) 

259 self.assertEqual(mcc1.mask_fraction_names, mcc2.mask_fraction_names) 

260 self.assertEqual(mcc1.n_noise_realizations, mcc2.n_noise_realizations) 

261 self.assertEqual(mcc1.psf_image_size, mcc2.psf_image_size) 

262 self.assertEqual(mcc1.units, mcc2.units) 

263 self.assertEqual(mcc1.wcs.getFitsMetadata().toString(), mcc2.wcs.getFitsMetadata().toString()) 

264 

265 # Check that the individual cells are identical. 

266 self.assertEqual(mcc1.cells.keys(), mcc2.cells.keys()) 

267 for idx in mcc1.cells.keys(): # noqa: SIM118 

268 self.assertImagesEqual(mcc1.cells[idx].outer.image, mcc2.cells[idx].outer.image) 

269 self.assertMasksEqual(mcc1.cells[idx].outer.mask, mcc2.cells[idx].outer.mask) 

270 self.assertImagesEqual(mcc1.cells[idx].outer.variance, mcc2.cells[idx].outer.variance) 

271 self.assertImagesEqual(mcc1.cells[idx].psf_image, mcc2.cells[idx].psf_image) 

272 

273 self.assertEqual(mcc1.cells[idx].band, mcc1.band) 

274 self.assertEqual(mcc1.cells[idx].common, mcc1.common) 

275 self.assertEqual(mcc1.cells[idx].units, mcc2.units) 

276 self.assertEqual(mcc1.cells[idx].wcs, mcc1.wcs) 

277 # Identifiers differ because of the ``cell`` component. 

278 # Check the other attributes within the identifiers. 

279 for attr in ("skymap", "tract", "patch", "band"): 

280 self.assertEqual(getattr(mcc1.cells[idx].identifiers, attr), getattr(mcc1.identifiers, attr)) 

281 

282 

283class MultipleCellCoaddTestCase(BaseMultipleCellCoaddTestCase): 

284 """Test the construction and interfaces of MultipleCellCoadd.""" 

285 

286 def test_fits(self): 

287 """Test that we can write a coadd to a FITS file and read it.""" 

288 with lsst.utils.tests.getTempFilePath(".fits") as filename: 

289 self.multiple_cell_coadd.write_fits(filename) 

290 mcc1 = MultipleCellCoadd.read_fits(filename) # Test the readFits method. 

291 

292 # Test the reader class. 

293 reader = CellCoaddFitsReader(filename) 

294 mcc2 = reader.readAsMultipleCellCoadd() 

295 

296 wcs = reader.readWcs() 

297 

298 self.assertMultipleCellCoaddsEqual(mcc1, self.multiple_cell_coadd) 

299 self.assertMultipleCellCoaddsEqual(mcc2, self.multiple_cell_coadd) 

300 # By transititve property of equality, mcc1 == mcc2. 

301 

302 self.assertEqual(self.multiple_cell_coadd.band, self.multiple_cell_coadd.common.band) 

303 self.assertEqual( 

304 wcs.getFitsMetadata().toString(), self.multiple_cell_coadd.wcs.getFitsMetadata().toString() 

305 ) 

306 

307 def test_visit_count(self): 

308 """Test the visit_count method.""" 

309 # Since we don't simulate coaddition from multiple warps, the cells are 

310 # all going to have just a single visit. 

311 for cellId, singleCellCoadd in self.multiple_cell_coadd.cells.items(): 

312 with self.subTest(x=cellId.x, y=cellId.y): 

313 self.assertEqual(singleCellCoadd.visit_count, 1) 

314 

315 

316class ExplodedCoaddTestCase(BaseMultipleCellCoaddTestCase): 

317 """Test the construction and methods of an ExplodedCoadd instance.""" 

318 

319 exploded_coadd: ExplodedCoadd 

320 

321 @classmethod 

322 def setUpClass(cls) -> None: # noqa: D102 

323 # Docstring inherited 

324 super().setUpClass() 

325 cls.exploded_coadd = cls.multiple_cell_coadd.explode() 

326 

327 @classmethod 

328 def tearDownClass(cls) -> None: # noqa: D102 

329 # Docstring inherited 

330 del cls.exploded_coadd 

331 super().tearDownClass() 

332 

333 def test_exploded_psf_image(self): 

334 """Show that psf_image sizes are absurd.""" 

335 self.assertEqual( 

336 self.exploded_coadd.psf_image.getBBox().getDimensions(), 

337 geom.Extent2I(self.nx * self.psf_size_x, self.ny * self.psf_size_y), 

338 ) 

339 for pad_psfs_with in (-999, -4, 0, 4, 8, 21, 40, 100): 

340 exploded_coadd = self.multiple_cell_coadd.explode(pad_psfs_with=pad_psfs_with) 

341 self.assertEqual( 

342 exploded_coadd.psf_image.getBBox().getDimensions(), 

343 geom.Extent2I(self.nx * self.outer_size_x, self.ny * self.outer_size_y), 

344 ) 

345 

346 def test_asMaskedImage(self): 

347 """Test the asMaskedImage method for an ExplodedCoadd object.""" 

348 masked_image = self.exploded_coadd.asMaskedImage() 

349 masked_image.setXY0(self.multiple_cell_coadd.outer_bbox.getMin()) 

350 base_bbox = self.multiple_cell_coadd.grid.bbox_of(Index2D(0, 0)).dilatedBy(self.border_size) 

351 for cell_x, cell_y in product(range(self.nx), range(self.ny)): 

352 bbox = base_bbox.shiftedBy(geom.Extent2I(cell_x * self.outer_size_x, cell_y * self.outer_size_y)) 

353 with self.subTest(cell_x=cell_x, cell_y=cell_y): 

354 self.assertMaskedImagesEqual( 

355 masked_image[bbox], 

356 self.multiple_cell_coadd.cells[Index2D(cell_x, cell_y)].outer.asMaskedImage(), 

357 ) 

358 

359 

360class StitchedCoaddTestCase(BaseMultipleCellCoaddTestCase): 

361 """Test the construction and methods of a StitchedCoadd instance.""" 

362 

363 stitched_coadd: StitchedCoadd 

364 

365 @classmethod 

366 def setUpClass(cls) -> None: # noqa: D102 

367 # Docstring inherited 

368 super().setUpClass() 

369 cls.stitched_coadd = cls.multiple_cell_coadd.stitch() 

370 

371 @classmethod 

372 def tearDownClass(cls) -> None: # noqa: D102 

373 # Docstring inherited 

374 del cls.stitched_coadd 

375 super().tearDownClass() 

376 

377 def test_computeBBox(self): 

378 """Test the computeBBox method for a StitchedPsf object.""" 

379 stitched_psf = self.stitched_coadd.psf 

380 

381 psf_bbox = geom.Box2I( 

382 geom.Point2I(-(self.psf_size_x // 2), -(self.psf_size_y // 2)), 

383 geom.Extent2I(self.psf_size_x, self.psf_size_y), 

384 ) 

385 

386 for position, _ in self.test_positions: 

387 bbox = stitched_psf.computeBBox(position) 

388 self.assertEqual(bbox, psf_bbox) 

389 

390 def test_computeShape(self): 

391 """Test the computeShape method for a StitchedPsf object.""" 

392 stitched_psf = self.stitched_coadd.psf 

393 for position, cell_index in self.test_positions: 

394 psf_shape = stitched_psf.computeShape(position) # check we can compute shape 

395 self.assertIsNot(psf_shape.getIxx(), np.nan) 

396 self.assertIsNot(psf_shape.getIyy(), np.nan) 

397 self.assertIsNot(psf_shape.getIxy(), np.nan) 

398 

399 # Moments measured from pixellated images are significantly 

400 # underestimated for small PSFs. 

401 if self.psf_sigmas[cell_index] >= 1.0: 

402 self.assertAlmostEqual(psf_shape.getIxx(), self.psf_sigmas[cell_index] ** 2, delta=1e-3) 

403 self.assertAlmostEqual(psf_shape.getIyy(), self.psf_sigmas[cell_index] ** 2, delta=1e-3) 

404 self.assertAlmostEqual(psf_shape.getIxy(), 0.0) 

405 

406 def test_computeKernelImage(self): 

407 """Test the computeKernelImage method for a StitchedPsf object.""" 

408 stitched_psf = self.stitched_coadd.psf 

409 psf_bbox = geom.Box2I( 

410 geom.Point2I(-(self.psf_size_x // 2), -(self.psf_size_y // 2)), 

411 geom.Extent2I(self.psf_size_x, self.psf_size_y), 

412 ) 

413 

414 for position, cell_index in self.test_positions: 

415 image1 = stitched_psf.computeKernelImage(position) 

416 image2 = SingleGaussianPsf( 

417 self.psf_size_x, self.psf_size_y, self.psf_sigmas[cell_index] 

418 ).computeKernelImage(position) 

419 self.assertImagesEqual(image1, image2) 

420 self.assertEqual(image1.getBBox(), psf_bbox) 

421 

422 def test_computeImage(self): 

423 """Test the computeImage method for a StitchedPsf object.""" 

424 stitched_psf = self.stitched_coadd.psf 

425 psf_extent = geom.Extent2I(self.psf_size_x, self.psf_size_y) 

426 

427 for position, cell_index in self.test_positions: 

428 image1 = stitched_psf.computeImage(position) 

429 image2 = SingleGaussianPsf( 

430 self.psf_size_x, self.psf_size_y, self.psf_sigmas[cell_index] 

431 ).computeImage(position) 

432 self.assertImagesEqual(image1, image2) 

433 self.assertEqual(image1.getBBox().getDimensions(), psf_extent) 

434 

435 def test_computeImage_computeKernelImage(self): 

436 """Test that computeImage called at integer points gives the same 

437 result as calling computeKernelImage. 

438 """ 

439 stitched_psf = self.stitched_coadd.psf 

440 for position, _cell_index in self.test_positions: 

441 pos = geom.Point2D(geom.Point2I(position)) # round to integer 

442 image1 = stitched_psf.computeKernelImage(pos) 

443 image2 = stitched_psf.computeImage(pos) 

444 self.assertImagesEqual(image1, image2) 

445 

446 def test_computeApetureFlux(self): 

447 """Test the computeApertureFlux method for a StitchedPsf object.""" 

448 stitched_psf = self.stitched_coadd.psf 

449 for position, cell_index in self.test_positions: 

450 flux1sigma = stitched_psf.computeApertureFlux(self.psf_sigmas[cell_index], position=position) 

451 self.assertAlmostEqual(flux1sigma, 0.39, delta=5e-2) 

452 

453 flux3sigma = stitched_psf.computeApertureFlux( 

454 3.0 * self.psf_sigmas[cell_index], position=position 

455 ) 

456 self.assertAlmostEqual(flux3sigma, 0.97, delta=2e-2) 

457 

458 def test_asExposure(self): 

459 """Test the asExposure method for a StitchedCoadd object.""" 

460 exposure = self.stitched_coadd.asExposure() 

461 

462 # Check that the bounding box is correct. 

463 bbox = exposure.getBBox() 

464 self.assertEqual(bbox.getWidth(), self.inner_size_x * self.nx + 2 * self.border_size) 

465 self.assertEqual(bbox.getHeight(), self.inner_size_y * self.ny + 2 * self.border_size) 

466 

467 for y in range(self.ny): 

468 for x in range(self.nx): 

469 bbox = geom.Box2I( 

470 geom.Point2I(self.x0 + x * self.inner_size_x, self.y0 + y * self.inner_size_y), 

471 geom.Extent2I(self.inner_size_x, self.inner_size_y), 

472 ) 

473 index = Index2D(x=x, y=y) 

474 self.assertImagesEqual(exposure.image[bbox], self.exposures[index].image[bbox]) 

475 self.assertImagesEqual(exposure.variance[bbox], self.exposures[index].variance[bbox]) 

476 self.assertImagesEqual(exposure.mask[bbox], self.exposures[index].mask[bbox]) 

477 

478 def test_fits(self): 

479 """Test that we can write an Exposure with StitchedPsf to a FITS file 

480 and read it. 

481 """ 

482 write_exposure = self.stitched_coadd.asExposure() 

483 with lsst.utils.tests.getTempFilePath(".fits") as filename: 

484 write_exposure.writeFits(filename) 

485 read_exposure = ExposureF.readFits(filename) # Test the readFits method. 

486 

487 # Test that the image planes are identical. 

488 self.assertImagesEqual(read_exposure.image, write_exposure.image) 

489 self.assertImagesEqual(read_exposure.variance, write_exposure.variance) 

490 self.assertImagesEqual(read_exposure.mask, write_exposure.mask) 

491 

492 # Test the PSF images in the StitchedPsf. 

493 for index in write_exposure.psf.images.indices(): 

494 self.assertImagesEqual(read_exposure.psf.images[index], write_exposure.psf.images[index]) 

495 

496 # Test that the WCSs are equal. 

497 self.assertEqual( 

498 read_exposure.wcs.getFitsMetadata().toString(), 

499 write_exposure.wcs.getFitsMetadata().toString(), 

500 ) 

501 

502 

503class TestMemory(lsst.utils.tests.MemoryTestCase): 

504 """Check for resource/memory leaks.""" 

505 

506 

507def setup_module(module): # noqa: D103 

508 lsst.utils.tests.init() 

509 

510 

511if __name__ == "__main__": 511 ↛ 512line 511 didn't jump to line 512, because the condition on line 511 was never true

512 lsst.utils.tests.init() 

513 unittest.main()