Coverage for tests/test_calibrateImage.py: 17%

230 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-20 12:29 +0000

1# This file is part of pipe_tasks. 

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 

23import tempfile 

24 

25import astropy.units as u 

26from astropy.coordinates import SkyCoord 

27import numpy as np 

28 

29import lsst.afw.image as afwImage 

30import lsst.afw.table as afwTable 

31import lsst.daf.base 

32import lsst.daf.butler.tests as butlerTests 

33import lsst.geom 

34import lsst.meas.algorithms 

35from lsst.meas.algorithms import testUtils 

36import lsst.meas.extensions.psfex 

37import lsst.meas.base.tests 

38import lsst.pipe.base.testUtils 

39from lsst.pipe.tasks.calibrateImage import CalibrateImageTask 

40import lsst.utils.tests 

41 

42 

43class CalibrateImageTaskTests(lsst.utils.tests.TestCase): 

44 

45 def setUp(self): 

46 # Different x/y dimensions so they're easy to distinguish in a plot, 

47 # and non-zero minimum, to help catch xy0 errors. 

48 bbox = lsst.geom.Box2I(lsst.geom.Point2I(5, 4), lsst.geom.Point2I(205, 184)) 

49 self.sky_center = lsst.geom.SpherePoint(245.0, -45.0, lsst.geom.degrees) 

50 self.photo_calib = 12.3 

51 dataset = lsst.meas.base.tests.TestDataset(bbox, crval=self.sky_center, calibration=self.photo_calib) 

52 # sqrt of area of a normalized 2d gaussian 

53 psf_scale = np.sqrt(4*np.pi*(dataset.psfShape.getDeterminantRadius())**2) 

54 noise = 10.0 # stddev of noise per pixel 

55 # Sources ordered from faintest to brightest. 

56 self.fluxes = np.array((6*noise*psf_scale, 

57 12*noise*psf_scale, 

58 45*noise*psf_scale, 

59 150*noise*psf_scale, 

60 400*noise*psf_scale, 

61 1000*noise*psf_scale)) 

62 self.centroids = np.array(((162, 22), 

63 (40, 70), 

64 (100, 160), 

65 (50, 120), 

66 (92, 35), 

67 (175, 154)), dtype=np.float32) 

68 for flux, centroid in zip(self.fluxes, self.centroids): 

69 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(centroid[0], centroid[1])) 

70 

71 # Bright extended source in the center of the image: should not appear 

72 # in any of the output catalogs. 

73 center = lsst.geom.Point2D(100, 100) 

74 shape = lsst.afw.geom.Quadrupole(8, 9, 3) 

75 dataset.addSource(instFlux=500*noise*psf_scale, centroid=center, shape=shape) 

76 

77 schema = dataset.makeMinimalSchema() 

78 self.truth_exposure, self.truth_cat = dataset.realize(noise=noise, schema=schema) 

79 # To make it look like a version=1 (nJy fluxes) refcat 

80 self.truth_cat = self.truth_exposure.photoCalib.calibrateCatalog(self.truth_cat) 

81 self.ref_loader = testUtils.MockReferenceObjectLoaderFromMemory([self.truth_cat]) 

82 metadata = lsst.daf.base.PropertyList() 

83 metadata.set("REFCAT_FORMAT_VERSION", 1) 

84 self.truth_cat.setMetadata(metadata) 

85 

86 # TODO: a cosmic ray (need to figure out how to insert a fake-CR) 

87 # self.truth_exposure.image.array[10, 10] = 100000 

88 # self.truth_exposure.variance.array[10, 10] = 100000/noise 

89 

90 # Copy the truth exposure, because CalibrateImage modifies the input. 

91 # Post-ISR ccds only contain: initial WCS, VisitInfo, filter 

92 self.exposure = afwImage.ExposureF(self.truth_exposure.maskedImage) 

93 self.exposure.setWcs(self.truth_exposure.wcs) 

94 self.exposure.info.setVisitInfo(self.truth_exposure.visitInfo) 

95 # "truth" filter, to match the "truth" refcat. 

96 self.exposure.setFilter(lsst.afw.image.FilterLabel(physical='truth', band="truth")) 

97 

98 # Test-specific configuration: 

99 self.config = CalibrateImageTask.ConfigClass() 

100 # We don't have many sources, so have to fit simpler models. 

101 self.config.psf_detection.background.approxOrderX = 1 

102 self.config.star_detection.background.approxOrderX = 1 

103 # Only insert 2 sky sources, for simplicity. 

104 self.config.star_sky_sources.nSources = 2 

105 # Use PCA psf fitter, as psfex fails if there are only 4 stars. 

106 self.config.psf_measure_psf.psfDeterminer = 'pca' 

107 # We don't have many test points, so can't match on complicated shapes. 

108 self.config.astrometry.matcher.numPointsForShape = 3 

109 # ApFlux has more noise than PsfFlux (the latter unrealistically small 

110 # in this test data), so we need to do magnitude rejection at higher 

111 # sigma, otherwise we can lose otherwise good sources. 

112 # TODO DM-39203: Once we are using Compensated Gaussian Fluxes, we 

113 # will use those fluxes here, and hopefully can remove this. 

114 self.config.astrometry.magnitudeOutlierRejectionNSigma = 9.0 

115 

116 # find_stars needs an id generator. 

117 self.id_generator = lsst.meas.base.IdGenerator() 

118 

119 # Something about this test dataset prefers the older fluxRatio here. 

120 self.config.star_catalog_calculation.plugins['base_ClassificationExtendedness'].fluxRatio = 0.925 

121 

122 def _check_run(self, calibrate, result, *, photo_calib): 

123 """Test the result of CalibrateImage.run(). 

124 

125 Parameters 

126 ---------- 

127 calibrate : `lsst.pipe.tasks.calibrateImage.CalibrateImageTask` 

128 Configured task that had `run` called on it. 

129 result : `lsst.pipe.base.Struct` 

130 Result of calling calibrate.run(). 

131 photo_calib : `float` 

132 Expected value of the PhotoCalib mean. 

133 """ 

134 # Background should have 4 elements: 3 from compute_psf and one from 

135 # re-estimation during source detection. 

136 self.assertEqual(len(result.background), 4) 

137 

138 # Check that the summary statistics are reasonable. 

139 summary = result.output_exposure.info.getSummaryStats() 

140 self.assertFloatsAlmostEqual(summary.psfSigma, 2.0, rtol=1e-2) 

141 self.assertFloatsAlmostEqual(summary.ra, self.sky_center.getRa().asDegrees(), rtol=1e-7) 

142 self.assertFloatsAlmostEqual(summary.dec, self.sky_center.getDec().asDegrees(), rtol=1e-7) 

143 

144 # Returned photoCalib should be the applied value, not the ==1 one on the exposure. 

145 self.assertFloatsAlmostEqual(result.applied_photo_calib.getCalibrationMean(), 

146 photo_calib, rtol=2e-3) 

147 # Should have flux/magnitudes in the catalog. 

148 self.assertIn("slot_PsfFlux_flux", result.stars.schema) 

149 self.assertIn("slot_PsfFlux_mag", result.stars.schema) 

150 

151 # Check that all necessary fields are in the output. 

152 lsst.pipe.base.testUtils.assertValidOutput(calibrate, result) 

153 

154 def test_run(self): 

155 """Test that run() returns reasonable values to be butler put. 

156 """ 

157 calibrate = CalibrateImageTask(config=self.config) 

158 calibrate.astrometry.setRefObjLoader(self.ref_loader) 

159 calibrate.photometry.match.setRefObjLoader(self.ref_loader) 

160 result = calibrate.run(exposures=self.exposure) 

161 

162 self._check_run(calibrate, result, photo_calib=self.photo_calib) 

163 

164 def test_run_2_snaps(self): 

165 """Test that run() returns reasonable values to be butler put, when 

166 passed two exposures to combine as snaps. 

167 """ 

168 calibrate = CalibrateImageTask(config=self.config) 

169 calibrate.astrometry.setRefObjLoader(self.ref_loader) 

170 calibrate.photometry.match.setRefObjLoader(self.ref_loader) 

171 result = calibrate.run(exposures=[self.exposure, self.exposure]) 

172 

173 self._check_run(calibrate, result, photo_calib=self.photo_calib/2) 

174 

175 def test_handle_snaps(self): 

176 calibrate = CalibrateImageTask(config=self.config) 

177 self.assertEqual(calibrate._handle_snaps(self.exposure), self.exposure) 

178 self.assertEqual(calibrate._handle_snaps((self.exposure, )), self.exposure) 

179 self.assertEqual(calibrate._handle_snaps(self.exposure), self.exposure) 

180 with self.assertRaisesRegex(RuntimeError, "Can only process 1 or 2 snaps, not 0."): 

181 calibrate._handle_snaps([]) 

182 with self.assertRaisesRegex(RuntimeError, "Can only process 1 or 2 snaps, not 3."): 

183 calibrate._handle_snaps(3*[self.exposure]) 

184 

185 def test_compute_psf(self): 

186 """Test that our brightest sources are found by _compute_psf(), 

187 that a PSF is assigned to the expopsure. 

188 """ 

189 calibrate = CalibrateImageTask(config=self.config) 

190 psf_stars, background, candidates = calibrate._compute_psf(self.exposure) 

191 

192 # Background should have 3 elements: initial subtraction, and two from 

193 # re-estimation during the two detection passes. 

194 self.assertEqual(len(background), 3) 

195 

196 # Only the point-sources with S/N > 50 should be in this output. 

197 self.assertEqual(psf_stars["calib_psf_used"].sum(), 3) 

198 # Sort in order of brightness, to easily compare with expected positions. 

199 psf_stars.sort(psf_stars.getPsfFluxSlot().getMeasKey()) 

200 for record, flux, center in zip(psf_stars[::-1], self.fluxes, self.centroids[self.fluxes > 50]): 

201 self.assertFloatsAlmostEqual(record.getX(), center[0], rtol=0.01) 

202 self.assertFloatsAlmostEqual(record.getY(), center[1], rtol=0.01) 

203 # PsfFlux should match the values inserted. 

204 self.assertFloatsAlmostEqual(record["slot_PsfFlux_instFlux"], flux, rtol=0.01) 

205 

206 # TODO: While debugging DM-32701, we're using PCA instead of psfex. 

207 # Check that we got a useable PSF. 

208 # self.assertIsInstance(self.exposure.psf, lsst.meas.extensions.psfex.PsfexPsf) 

209 self.assertIsInstance(self.exposure.psf, lsst.meas.algorithms.PcaPsf) 

210 # TestDataset sources have PSF radius=2 pixels. 

211 radius = self.exposure.psf.computeShape(self.exposure.psf.getAveragePosition()).getDeterminantRadius() 

212 self.assertFloatsAlmostEqual(radius, 2.0, rtol=1e-2) 

213 

214 # To look at images for debugging (`setup display_ds9` and run ds9): 

215 # import lsst.afw.display 

216 # display = lsst.afw.display.getDisplay() 

217 # display.mtv(self.exposure) 

218 

219 def test_measure_aperture_correction(self): 

220 """Test that _measure_aperture_correction() assigns an ApCorrMap to the 

221 exposure. 

222 """ 

223 calibrate = CalibrateImageTask(config=self.config) 

224 psf_stars, background, candidates = calibrate._compute_psf(self.exposure) 

225 

226 # First check that the exposure doesn't have an ApCorrMap. 

227 self.assertIsNone(self.exposure.apCorrMap) 

228 calibrate._measure_aperture_correction(self.exposure, psf_stars) 

229 self.assertIsInstance(self.exposure.apCorrMap, afwImage.ApCorrMap) 

230 

231 def test_find_stars(self): 

232 """Test that _find_stars() correctly identifies the S/N>10 stars 

233 in the image and returns them in the output catalog. 

234 """ 

235 calibrate = CalibrateImageTask(config=self.config) 

236 psf_stars, background, candidates = calibrate._compute_psf(self.exposure) 

237 calibrate._measure_aperture_correction(self.exposure, psf_stars) 

238 

239 stars = calibrate._find_stars(self.exposure, background, self.id_generator) 

240 

241 # Background should have 4 elements: 3 from compute_psf and one from 

242 # re-estimation during source detection. 

243 self.assertEqual(len(background), 4) 

244 

245 # Only 5 psf-like sources with S/N>10 should be in the output catalog, 

246 # plus two sky sources. 

247 self.assertEqual(len(stars), 7) 

248 self.assertTrue(stars.isContiguous()) 

249 # Sort in order of brightness, to easily compare with expected positions. 

250 stars.sort(stars.getPsfFluxSlot().getMeasKey()) 

251 for record, flux, center in zip(stars[::-1], self.fluxes, self.centroids[self.fluxes > 50]): 

252 self.assertFloatsAlmostEqual(record.getX(), center[0], rtol=0.01) 

253 self.assertFloatsAlmostEqual(record.getY(), center[1], rtol=0.01) 

254 self.assertFloatsAlmostEqual(record["slot_PsfFlux_instFlux"], flux, rtol=0.01) 

255 

256 def test_astrometry(self): 

257 """Test that the fitted WCS gives good catalog coordinates. 

258 """ 

259 calibrate = CalibrateImageTask(config=self.config) 

260 calibrate.astrometry.setRefObjLoader(self.ref_loader) 

261 psf_stars, background, candidates = calibrate._compute_psf(self.exposure) 

262 calibrate._measure_aperture_correction(self.exposure, psf_stars) 

263 stars = calibrate._find_stars(self.exposure, background, self.id_generator) 

264 

265 calibrate._fit_astrometry(self.exposure, stars) 

266 

267 # Check that we got reliable matches with the truth coordinates. 

268 sky = stars["sky_source"] 

269 fitted = SkyCoord(stars[~sky]['coord_ra'], stars[~sky]['coord_dec'], unit="radian") 

270 truth = SkyCoord(self.truth_cat['coord_ra'], self.truth_cat['coord_dec'], unit="radian") 

271 idx, d2d, _ = fitted.match_to_catalog_sky(truth) 

272 np.testing.assert_array_less(d2d.to_value(u.milliarcsecond), 35.0) 

273 

274 def test_photometry(self): 

275 """Test that the fitted photoCalib matches the one we generated, 

276 and that the exposure is calibrated. 

277 """ 

278 calibrate = CalibrateImageTask(config=self.config) 

279 calibrate.astrometry.setRefObjLoader(self.ref_loader) 

280 calibrate.photometry.match.setRefObjLoader(self.ref_loader) 

281 psf_stars, background, candidates = calibrate._compute_psf(self.exposure) 

282 calibrate._measure_aperture_correction(self.exposure, psf_stars) 

283 stars = calibrate._find_stars(self.exposure, background, self.id_generator) 

284 calibrate._fit_astrometry(self.exposure, stars) 

285 

286 stars, matches, meta, photoCalib = calibrate._fit_photometry(self.exposure, stars) 

287 

288 # NOTE: With this test data, PhotoCalTask returns calibrationErr==0, 

289 # so we can't check that the photoCal error has been set. 

290 self.assertFloatsAlmostEqual(photoCalib.getCalibrationMean(), self.photo_calib, rtol=2e-3) 

291 # The exposure should be calibrated by the applied photoCalib. 

292 self.assertFloatsAlmostEqual(self.exposure.image.array/self.truth_exposure.image.array, 

293 self.photo_calib, rtol=2e-3) 

294 # PhotoCalib on the exposure must be identically 1. 

295 self.assertEqual(self.exposure.photoCalib.getCalibrationMean(), 1.0) 

296 

297 # Check that we got reliable magnitudes and fluxes vs. truth, ignoring 

298 # sky sources. 

299 sky = stars["sky_source"] 

300 fitted = SkyCoord(stars[~sky]['coord_ra'], stars[~sky]['coord_dec'], unit="radian") 

301 truth = SkyCoord(self.truth_cat['coord_ra'], self.truth_cat['coord_dec'], unit="radian") 

302 idx, _, _ = fitted.match_to_catalog_sky(truth) 

303 # Because the input variance image does not include contributions from 

304 # the sources, we can't use fluxErr as a bound on the measurement 

305 # quality here. 

306 self.assertFloatsAlmostEqual(stars[~sky]['slot_PsfFlux_flux'], 

307 self.truth_cat['truth_flux'][idx], 

308 rtol=0.1) 

309 self.assertFloatsAlmostEqual(stars[~sky]['slot_PsfFlux_mag'], 

310 self.truth_cat['truth_mag'][idx], 

311 rtol=0.01) 

312 

313 def test_match_psf_stars(self): 

314 """Test that _match_psf_stars() flags the correct stars as psf stars 

315 and candidates. 

316 """ 

317 calibrate = CalibrateImageTask(config=self.config) 

318 psf_stars, background, candidates = calibrate._compute_psf(self.exposure) 

319 calibrate._measure_aperture_correction(self.exposure, psf_stars) 

320 stars = calibrate._find_stars(self.exposure, background, self.id_generator) 

321 

322 # There should be no psf-related flags set at first. 

323 self.assertEqual(stars["calib_psf_candidate"].sum(), 0) 

324 self.assertEqual(stars["calib_psf_used"].sum(), 0) 

325 self.assertEqual(stars["calib_psf_reserved"].sum(), 0) 

326 

327 calibrate._match_psf_stars(psf_stars, stars) 

328 

329 # Sort in order of brightness; the psf stars are the 3 brightest, with 

330 # two sky sources as the faintest. 

331 stars.sort(stars.getPsfFluxSlot().getMeasKey()) 

332 # sort() above leaves the catalog non-contiguous. 

333 stars = stars.copy(deep=True) 

334 np.testing.assert_array_equal(stars["calib_psf_candidate"], 

335 [False, False, False, False, True, True, True]) 

336 np.testing.assert_array_equal(stars["calib_psf_used"], [False, False, False, False, True, True, True]) 

337 # Too few sources to reserve any in these tests. 

338 self.assertEqual(stars["calib_psf_reserved"].sum(), 0) 

339 

340 

341class CalibrateImageTaskRunQuantumTests(lsst.utils.tests.TestCase): 

342 """Tests of ``CalibrateImageTask.runQuantum``, which need a test butler, 

343 but do not need real images. 

344 """ 

345 def setUp(self): 

346 instrument = "testCam" 

347 exposure0 = 101 

348 exposure1 = 101 

349 visit = 100101 

350 detector = 42 

351 

352 # Create a and populate a test butler for runQuantum tests. 

353 self.repo_path = tempfile.TemporaryDirectory(ignore_cleanup_errors=True) 

354 self.repo = butlerTests.makeTestRepo(self.repo_path.name) 

355 

356 # A complete instrument record is necessary for the id generator. 

357 instrumentRecord = self.repo.dimensions["instrument"].RecordClass( 

358 name=instrument, visit_max=1e6, exposure_max=1e6, detector_max=128, 

359 class_name="lsst.obs.base.instrument_tests.DummyCam", 

360 ) 

361 self.repo.registry.syncDimensionData("instrument", instrumentRecord) 

362 

363 # dataIds for fake data 

364 butlerTests.addDataIdValue(self.repo, "exposure", exposure0) 

365 butlerTests.addDataIdValue(self.repo, "exposure", exposure1) 

366 butlerTests.addDataIdValue(self.repo, "visit", visit) 

367 butlerTests.addDataIdValue(self.repo, "detector", detector) 

368 

369 # inputs 

370 butlerTests.addDatasetType(self.repo, "postISRCCD", {"instrument", "exposure", "detector"}, 

371 "ExposureF") 

372 butlerTests.addDatasetType(self.repo, "gaia_dr3_20230707", {"htm7"}, "SimpleCatalog") 

373 butlerTests.addDatasetType(self.repo, "ps1_pv3_3pi_20170110", {"htm7"}, "SimpleCatalog") 

374 

375 # outputs 

376 butlerTests.addDatasetType(self.repo, "initial_pvi", {"instrument", "visit", "detector"}, 

377 "ExposureF") 

378 butlerTests.addDatasetType(self.repo, "initial_stars_footprints_detector", 

379 {"instrument", "visit", "detector"}, 

380 "SourceCatalog") 

381 butlerTests.addDatasetType(self.repo, "initial_photoCalib_detector", 

382 {"instrument", "visit", "detector"}, 

383 "PhotoCalib") 

384 # optional outputs 

385 butlerTests.addDatasetType(self.repo, "initial_pvi_background", {"instrument", "visit", "detector"}, 

386 "Background") 

387 butlerTests.addDatasetType(self.repo, "initial_psf_stars_footprints", 

388 {"instrument", "visit", "detector"}, 

389 "SourceCatalog") 

390 butlerTests.addDatasetType(self.repo, 

391 "initial_astrometry_match_detector", 

392 {"instrument", "visit", "detector"}, 

393 "Catalog") 

394 butlerTests.addDatasetType(self.repo, 

395 "initial_photometry_match_detector", 

396 {"instrument", "visit", "detector"}, 

397 "Catalog") 

398 

399 # dataIds 

400 self.exposure0_id = self.repo.registry.expandDataId( 

401 {"instrument": instrument, "exposure": exposure0, "detector": detector}) 

402 self.exposure1_id = self.repo.registry.expandDataId( 

403 {"instrument": instrument, "exposure": exposure1, "detector": detector}) 

404 self.visit_id = self.repo.registry.expandDataId( 

405 {"instrument": instrument, "visit": visit, "detector": detector}) 

406 self.htm_id = self.repo.registry.expandDataId({"htm7": 42}) 

407 

408 # put empty data 

409 self.butler = butlerTests.makeTestCollection(self.repo) 

410 self.butler.put(afwImage.ExposureF(), "postISRCCD", self.exposure0_id) 

411 self.butler.put(afwTable.SimpleCatalog(), "gaia_dr3_20230707", self.htm_id) 

412 self.butler.put(afwTable.SimpleCatalog(), "ps1_pv3_3pi_20170110", self.htm_id) 

413 

414 def tearDown(self): 

415 self.repo_path.cleanup() 

416 

417 def test_runQuantum(self): 

418 task = CalibrateImageTask() 

419 lsst.pipe.base.testUtils.assertValidInitOutput(task) 

420 

421 quantum = lsst.pipe.base.testUtils.makeQuantum( 

422 task, self.butler, self.visit_id, 

423 {"exposures": [self.exposure0_id], 

424 "astrometry_ref_cat": [self.htm_id], 

425 "photometry_ref_cat": [self.htm_id], 

426 # outputs 

427 "output_exposure": self.visit_id, 

428 "stars": self.visit_id, 

429 "background": self.visit_id, 

430 "psf_stars": self.visit_id, 

431 "applied_photo_calib": self.visit_id, 

432 "initial_pvi_background": self.visit_id, 

433 "astrometry_matches": self.visit_id, 

434 "photometry_matches": self.visit_id, 

435 }) 

436 mock_run = lsst.pipe.base.testUtils.runTestQuantum(task, self.butler, quantum) 

437 

438 # Ensure the reference loaders have been configured. 

439 self.assertEqual(task.astrometry.refObjLoader.name, "gaia_dr3_20230707") 

440 self.assertEqual(task.photometry.match.refObjLoader.name, "ps1_pv3_3pi_20170110") 

441 # Check that the proper kwargs are passed to run(). 

442 self.assertEqual(mock_run.call_args.kwargs.keys(), {"exposures", "id_generator"}) 

443 

444 def test_runQuantum_2_snaps(self): 

445 task = CalibrateImageTask() 

446 lsst.pipe.base.testUtils.assertValidInitOutput(task) 

447 

448 quantum = lsst.pipe.base.testUtils.makeQuantum( 

449 task, self.butler, self.visit_id, 

450 {"exposures": [self.exposure0_id, self.exposure1_id], 

451 "astrometry_ref_cat": [self.htm_id], 

452 "photometry_ref_cat": [self.htm_id], 

453 # outputs 

454 "output_exposure": self.visit_id, 

455 "stars": self.visit_id, 

456 "background": self.visit_id, 

457 "psf_stars": self.visit_id, 

458 "applied_photo_calib": self.visit_id, 

459 "initial_pvi_background": self.visit_id, 

460 "astrometry_matches": self.visit_id, 

461 "photometry_matches": self.visit_id, 

462 }) 

463 mock_run = lsst.pipe.base.testUtils.runTestQuantum(task, self.butler, quantum) 

464 

465 # Ensure the reference loaders have been configured. 

466 self.assertEqual(task.astrometry.refObjLoader.name, "gaia_dr3_20230707") 

467 self.assertEqual(task.photometry.match.refObjLoader.name, "ps1_pv3_3pi_20170110") 

468 # Check that the proper kwargs are passed to run(). 

469 self.assertEqual(mock_run.call_args.kwargs.keys(), {"exposures", "id_generator"}) 

470 

471 def test_runQuantum_no_optional_outputs(self): 

472 config = CalibrateImageTask.ConfigClass() 

473 config.optional_outputs = None 

474 task = CalibrateImageTask(config=config) 

475 lsst.pipe.base.testUtils.assertValidInitOutput(task) 

476 

477 quantum = lsst.pipe.base.testUtils.makeQuantum( 

478 task, self.butler, self.visit_id, 

479 {"exposures": [self.exposure0_id], 

480 "astrometry_ref_cat": [self.htm_id], 

481 "photometry_ref_cat": [self.htm_id], 

482 # outputs 

483 "output_exposure": self.visit_id, 

484 "stars": self.visit_id, 

485 "applied_photo_calib": self.visit_id, 

486 "background": self.visit_id, 

487 }) 

488 mock_run = lsst.pipe.base.testUtils.runTestQuantum(task, self.butler, quantum) 

489 

490 # Ensure the reference loaders have been configured. 

491 self.assertEqual(task.astrometry.refObjLoader.name, "gaia_dr3_20230707") 

492 self.assertEqual(task.photometry.match.refObjLoader.name, "ps1_pv3_3pi_20170110") 

493 # Check that the proper kwargs are passed to run(). 

494 self.assertEqual(mock_run.call_args.kwargs.keys(), {"exposures", "id_generator"}) 

495 

496 def test_lintConnections(self): 

497 """Check that the connections are self-consistent. 

498 """ 

499 Connections = CalibrateImageTask.ConfigClass.ConnectionsClass 

500 lsst.pipe.base.testUtils.lintConnections(Connections) 

501 

502 

503def setup_module(module): 

504 lsst.utils.tests.init() 

505 

506 

507class MemoryTestCase(lsst.utils.tests.MemoryTestCase): 

508 pass 

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()