Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from __future__ import with_statement 

2from builtins import zip 

3from builtins import range 

4import os 

5import copy 

6import math 

7import numpy as np 

8import unittest 

9import pickle 

10import galsim 

11import tempfile 

12import shutil 

13from collections import OrderedDict 

14import lsst.utils 

15import lsst.utils.tests 

16from lsst.utils import getPackageDir 

17import lsst.afw.cameraGeom.testUtils as camTestUtils 

18from lsst.sims.photUtils import BandpassDict 

19from lsst.sims.utils.CodeUtilities import sims_clean_up 

20from lsst.sims.utils import radiansFromArcsec 

21from lsst.sims.photUtils import Bandpass, calcSkyCountsPerPixelForM5, LSSTdefaults, PhotometricParameters 

22from lsst.sims.coordUtils import pixelCoordsFromPupilCoords 

23from lsst.sims.catUtils.utils import makePhoSimTestDB 

24from lsst.sims.utils import ObservationMetaData 

25from lsst.sims.GalSimInterface import (GalSimGalaxies, GalSimStars, GalSimAgn, 

26 SNRdocumentPSF, ExampleCCDNoise, 

27 Kolmogorov_and_Gaussian_PSF, 

28 GalSimInterpreter, GalSimCameraWrapper, 

29 make_galsim_detector, 

30 make_gs_interpreter, 

31 GalSimCelestialObject, 

32 LSSTCameraWrapper) 

33from lsst.sims.GalSimInterface.galSimInterpreter import getGoodPhotImageSize 

34from lsst.sims.catUtils.utils import (calcADUwrapper, testGalaxyBulgeDBObj, testGalaxyDiskDBObj, 

35 testGalaxyAgnDBObj, testStarsDBObj) 

36import lsst.afw.image as afwImage 

37from lsst.sims.coordUtils import clean_up_lsst_camera 

38 

39# Tell astropy not to download this file again, even if it's out of date. 

40from astropy.utils import iers 

41iers.conf.auto_max_age = None 

42 

43ROOT = os.path.abspath(os.path.dirname(__file__)) 

44 

45 

46def setup_module(module): 

47 lsst.utils.tests.init() 

48 

49 

50class testGalaxyCatalog(GalSimGalaxies): 

51 """ 

52 Wraps the GalSimGalaxies class. Adds columns to the output 

53 so that we can read the InstanceCatalog back in and verify that 

54 GalSim put the correct number of ADU in each FITS file. 

55 """ 

56 bandpassNames = ['u', 'g', 'r'] 

57 

58 column_outputs = copy.deepcopy(GalSimGalaxies.column_outputs) 

59 column_outputs.remove('fitsFiles') 

60 column_outputs.append('magNorm') 

61 column_outputs.append('redshift') 

62 column_outputs.append('internalAv') 

63 column_outputs.append('internalRv') 

64 column_outputs.append('galacticAv') 

65 column_outputs.append('galacticRv') 

66 column_outputs.append('fitsFiles') 

67 

68 PSF = SNRdocumentPSF() 

69 

70 

71class testStarCatalog(GalSimStars): 

72 """ 

73 Wraps the GalSimStars class. Adds columns to the output 

74 so that we can read the InstanceCatalog back in and verify that 

75 GalSim put the correct number of ADU in each FITS file. 

76 """ 

77 bandpassNames = ['u', 'g', 'r'] 

78 

79 column_outputs = copy.deepcopy(GalSimStars.column_outputs) 

80 column_outputs.remove('fitsFiles') 

81 column_outputs.append('magNorm') 

82 column_outputs.append('redshift') 

83 column_outputs.append('internalAv') 

84 column_outputs.append('internalRv') 

85 column_outputs.append('galacticAv') 

86 column_outputs.append('galacticRv') 

87 column_outputs.append('fitsFiles') 

88 

89 PSF = SNRdocumentPSF() 

90 

91 

92class testAgnCatalog(GalSimAgn): 

93 """ 

94 Wraps the GalSimAgn class. Adds columns to the output 

95 so that we can read the InstanceCatalog back in and verify that 

96 GalSim put the correct number of ADU in each FITS file. 

97 """ 

98 bandpassNames = ['u', 'g', 'r'] 

99 

100 column_outputs = copy.deepcopy(GalSimAgn.column_outputs) 

101 column_outputs.remove('fitsFiles') 

102 column_outputs.append('magNorm') 

103 column_outputs.append('redshift') 

104 column_outputs.append('internalAv') 

105 column_outputs.append('internalRv') 

106 column_outputs.append('galacticAv') 

107 column_outputs.append('galacticRv') 

108 column_outputs.append('fitsFiles') 

109 

110 PSF = SNRdocumentPSF() 

111 

112 

113class psfCatalog(testGalaxyCatalog): 

114 """ 

115 Adds a PSF to testGalaxyCatalog 

116 """ 

117 PSF = SNRdocumentPSF() 

118 

119 

120class backgroundCatalog(testGalaxyCatalog): 

121 """ 

122 Add sky background but no noise to testGalaxyCatalog 

123 """ 

124 PSF = SNRdocumentPSF() 

125 noise_and_background = ExampleCCDNoise(addNoise=False, seed=42) 

126 

127 

128class noisyCatalog(testGalaxyCatalog): 

129 """ 

130 Adds a noise and sky background wrapper to testGalaxyCatalog 

131 """ 

132 PSF = SNRdocumentPSF() 

133 noise_and_background = ExampleCCDNoise(seed=42) 

134 

135 

136class testFakeBandpassCatalog(testStarCatalog): 

137 """ 

138 tests the GalSim interface on fake bandpasses 

139 """ 

140 bandpassNames = ['x', 'y', 'z'] 

141 

142 bandpassDir = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'testThroughputs') 

143 bandpassRoot = 'fakeFilter_' 

144 componentList = ['fakeM1.dat', 'fakeM2.dat'] 

145 atmoTransmissionName = 'fakeAtmo.dat' 

146 skySEDname = 'fakeSky.dat' 

147 

148 

149class testFakeSedCatalog(testFakeBandpassCatalog): 

150 """ 

151 tests the GalSim interface on fake seds and bandpasses 

152 """ 

153 sedDir = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'testSeds') 

154 

155 def get_sedFilepath(self): 

156 """ 

157 map the sedFilenames created by makePhoSimTestDB to the SEDs in 

158 in testSeds/ 

159 """ 

160 

161 nameMap = {'km20_5750.fits_g40_5790': 'fakeSed1.dat', 

162 'm2.0Full.dat': 'fakeSed2.dat', 

163 'bergeron_6500_85.dat_6700': 'fakeSed3.dat'} 

164 

165 rawNames = self.column_by_name('sedFilename') 

166 return np.array([nameMap[nn] for nn in rawNames]) 

167 

168 

169class GalSimInterfaceTest(unittest.TestCase): 

170 

171 @classmethod 

172 def setUpClass(cls): 

173 cls.camera = camTestUtils.CameraWrapper().camera 

174 cls.scratch_dir = tempfile.mkdtemp(dir=ROOT, prefix='GalSimInterfaceTest-') 

175 cls.dbName = os.path.join(cls.scratch_dir, 'galSimTestDB.db') 

176 

177 deltaRA = np.array([72.0/3600.0]) 

178 deltaDec = np.array([0.0]) 

179 defaults = LSSTdefaults() 

180 cls.bandpassNameList = ['u', 'g', 'r', 'i', 'z', 'y'] 

181 cls.m5 = [16.0+ix for ix in range(len(cls.bandpassNameList))] 

182 cls.seeing = [defaults._FWHMeff[bb] for bb in cls.bandpassNameList] 

183 cls.obs_metadata = makePhoSimTestDB(filename=cls.dbName, size=1, 

184 deltaRA=deltaRA, 

185 deltaDec=deltaDec, 

186 bandpass=cls.bandpassNameList, 

187 m5=cls.m5, 

188 seeing=cls.seeing, 

189 seedVal=65) 

190 

191 cls.driver = 'sqlite' 

192 

193 @classmethod 

194 def tearDownClass(cls): 

195 sims_clean_up() 

196 if os.path.exists(cls.dbName): 

197 os.unlink(cls.dbName) 

198 if os.path.exists(cls.scratch_dir): 

199 shutil.rmtree(cls.scratch_dir) 

200 

201 del cls.dbName 

202 del cls.driver 

203 del cls.obs_metadata 

204 del cls.bandpassNameList 

205 del cls.m5 

206 del cls.seeing 

207 del cls.camera 

208 

209 def getFilesAndBandpasses(self, catalog, nameRoot=None, 

210 bandpassDir=os.path.join(getPackageDir('throughputs'), 'baseline'), 

211 bandpassRoot='total_',): 

212 

213 """ 

214 Take a GalSimCatalog. Return a list of fits files and and OrderedDict of bandpasses associated 

215 with that catalog 

216 

217 @param [in] catalog is a GalSimCatalog instantiation 

218 

219 @param [in] nameRoot is the nameRoot prepended to the fits files output by that catalog 

220 

221 @param[in] bandpassDir is the directory where bandpass files can be found 

222 

223 @param [in] bandpassRoot is the root of the name of the bandpass files 

224 

225 @param [out] listOfFiles is a list of the names of the fits files written by this catalog 

226 

227 @param [out] bandpassDict is an OrderedDict of Bandpass instantiations corresponding to the 

228 filters in this catalog. 

229 """ 

230 

231 # write the fits files 

232 catalog.write_images(nameRoot=nameRoot) 

233 

234 # a list of bandpasses over which we are integraging 

235 listOfFilters = [] 

236 listOfFiles = [] 

237 

238 # read in the names of all of the written fits files directly from the 

239 # InstanceCatalog's GalSimInterpreter 

240 # Use AFW to read in the FITS files and calculate the ADU 

241 for name in catalog.galSimInterpreter.detectorImages: 

242 if nameRoot is not None: 

243 name = nameRoot+'_'+name 

244 

245 listOfFiles.append(name) 

246 

247 if name[-6] not in listOfFilters: 

248 listOfFilters.append(name[-6]) 

249 

250 bandpassDict = OrderedDict() 

251 for filterName in listOfFilters: 

252 bandpassName = os.path.join(bandpassDir, bandpassRoot + filterName + '.dat') 

253 bandpass = Bandpass() 

254 bandpass.readThroughput(bandpassName) 

255 bandpassDict[filterName] = bandpass 

256 

257 return listOfFiles, bandpassDict 

258 

259 def catalogTester(self, catName=None, catalog=None, nameRoot=None, 

260 bandpassDir=os.path.join(getPackageDir('throughputs'), 'baseline'), 

261 bandpassRoot='total_', 

262 sedDir=getPackageDir('sims_sed_library')): 

263 """ 

264 Reads in a GalSim Instance Catalog. Writes the images from that catalog. 

265 Then reads those images back in. Uses AFW to calculate the number of counts 

266 in each FITS image. Reads in the InstanceCatalog associated with those images. 

267 Uses sims_photUtils code to calculate the ADU for each object on the FITS images. 

268 Verifies that the two independent calculations of counts agree (to within a tolerance, 

269 since the GalSim images are generated in a pseudo-random way). 

270 

271 @param [in] catName is the name of the InstanceCatalog that has been written to disk 

272 

273 @param [in] catalog is the actual InstanceCatalog instantiation 

274 

275 @param [in] nameRoot is a string appended to the names of the FITS files being written 

276 

277 @param [in] bandpassDir is the directory containing the bandpasses against which to test 

278 

279 @param [in] bandpassRoot is the root of the name of the bandpass files, i.e. 

280 

281 os.path.join(bandpassDir, bandpassRoot + bandpassName + '.dat') 

282 """ 

283 

284 # a dictionary of ADU for each FITS file as calculated by GalSim 

285 # (indexed on the name of the FITS file) 

286 galsimCounts = {} 

287 galsimPixels = {} 

288 

289 # a dictionary of ADU for each FITS file as calculated by sims_photUtils 

290 # (indexed on the name of the FITS file) 

291 controlCounts = {} 

292 

293 listOfFiles, bandpassDict = self.getFilesAndBandpasses(catalog, nameRoot=nameRoot, 

294 bandpassDir=bandpassDir, 

295 bandpassRoot=bandpassRoot) 

296 

297 # read in the names of all of the written fits files directly from the 

298 # InstanceCatalog's GalSimInterpreter 

299 # Use AFW to read in the FITS files and calculate the ADU 

300 for name in listOfFiles: 

301 im = afwImage.ImageF(name) 

302 imArr = im.getArray() 

303 galsimCounts[name] = imArr.sum() 

304 galsimPixels[name] = imArr.shape[0]*imArr.shape[1] 

305 controlCounts[name] = 0.0 

306 os.unlink(name) 

307 

308 if catalog.noise_and_background is not None and catalog.noise_and_background.addBackground: 

309 # calculate the expected skyCounts in each filter 

310 backgroundCounts = {} 

311 for filterName in bandpassDict.keys(): 

312 cts = calcSkyCountsPerPixelForM5(catalog.obs_metadata.m5[filterName], 

313 bandpassDict[filterName], 

314 catalog.photParams, 

315 FWHMeff=catalog.obs_metadata.seeing[filterName]) 

316 

317 backgroundCounts[filterName] = cts 

318 

319 for name in controlCounts: 

320 filterName = name[-6] 

321 controlCounts[name] += backgroundCounts[filterName] * galsimPixels[name] 

322 

323 # Read in the InstanceCatalog. For each object in the catalog, use sims_photUtils 

324 # to calculate the ADU. Keep track of how many ADU should be in each FITS file. 

325 with open(catName, 'r') as testFile: 

326 lines = testFile.readlines() 

327 for line in lines: 

328 if line[0] != '#': 

329 gg = line.split('; ') 

330 sedName = gg[7] 

331 magNorm = float(gg[14]) 

332 redshift = float(gg[15]) 

333 internalAv = float(gg[16]) 

334 internalRv = float(gg[17]) 

335 galacticAv = float(gg[18]) 

336 galacticRv = float(gg[19]) 

337 listOfFileNames = gg[20].split('//') 

338 alreadyWritten = [] 

339 

340 for name in listOfFileNames: 

341 

342 # guard against objects being written on one 

343 # chip more than once 

344 msg = '%s was written on %s more than once' % (sedName, name) 

345 self.assertNotIn(name, alreadyWritten, msg=msg) 

346 alreadyWritten.append(name) 

347 

348 # loop over all of the detectors on which an object fell 

349 # (this is not a terribly great idea, since our conservative implementation 

350 # of GalSimInterpreter._doesObjectImpingeOnDetector means that some detectors 

351 # will be listed here even though the object does not illumine them) 

352 for filterName in bandpassDict.keys(): 

353 chipName = name.replace(':', '') 

354 chipName = chipName.replace(' ', '_') 

355 chipName = chipName.replace(',', '') 

356 chipName = chipName.strip() 

357 

358 fullName = nameRoot+'_'+chipName+'_'+filterName+'.fits' 

359 

360 fullSedName = os.path.join(sedDir, sedName) 

361 

362 controlCounts[fullName] += calcADUwrapper(sedName=fullSedName, 

363 bandpass=bandpassDict[filterName], 

364 redshift=redshift, magNorm=magNorm, 

365 internalAv=internalAv, 

366 internalRv=internalRv, 

367 galacticAv=galacticAv, 

368 galacticRv=galacticRv) 

369 

370 drawnDetectors = 0 

371 unDrawnDetectors = 0 

372 for ff in controlCounts: 

373 if controlCounts[ff] > 1000.0 and galsimCounts[ff] > 1000.0: 

374 countSigma = np.sqrt(controlCounts[ff]/catalog.photParams.gain) 

375 

376 # because, for really dim images, there could be enough 

377 # statistical imprecision in the GalSim drawing routine 

378 # to violate the condition below 

379 drawnDetectors += 1 

380 msg = 'controlCounts %e galsimCounts %e sigma %e; delta/sigma %e; %s ' % \ 

381 (controlCounts[ff], galsimCounts[ff], countSigma, 

382 (controlCounts[ff]-galsimCounts[ff])/countSigma, nameRoot) 

383 

384 if catalog.noise_and_background is not None \ 

385 and catalog.noise_and_background.addBackground: 

386 

387 msg += 'background per pixel %e pixels %e %s' % \ 

388 (backgroundCounts[ff[-6]], galsimPixels[ff], ff) 

389 

390 self.assertLess(np.abs(controlCounts[ff] - galsimCounts[ff]), 5*countSigma, 

391 msg=msg) 

392 elif galsimCounts[ff] > 0.001: 

393 unDrawnDetectors += 1 

394 

395 # to make sure we did not neglect more than one detector 

396 self.assertGreater(drawnDetectors, 0) 

397 

398 def compareCatalogs(self, cleanCatalog, noisyCatalog, gain, readnoise): 

399 """ 

400 Read in two catalogs (one with noise, one without). Compare the flux in each image 

401 pixel by pixel. Make sure that the variation between the two is within expected limits. 

402 

403 @param [in] cleanCatalog is the noiseless GalSimCatalog instantiation 

404 

405 @param [in] noisyCatalog is the noisy GalSimCatalog instantiation 

406 

407 @param [in] gain is the electrons per ADU for the GalSimCatalogs 

408 

409 @param [in] readnoise is the electrons per pixel per exposure of the GalSimCatalogs] 

410 """ 

411 

412 cleanFileList, cleanBandpassDict = self.getFilesAndBandpasses(cleanCatalog, nameRoot='clean') 

413 noisyFileList, noisyBandpassDict = self.getFilesAndBandpasses(noisyCatalog, nameRoot='unclean') 

414 

415 # calculate the expected skyCounts in each filter 

416 backgroundCounts = {} 

417 for filterName in noisyBandpassDict.keys(): 

418 cts = calcSkyCountsPerPixelForM5(noisyCatalog.obs_metadata.m5[filterName], 

419 noisyBandpassDict[filterName], 

420 noisyCatalog.photParams, 

421 FWHMeff=noisyCatalog.obs_metadata.seeing[filterName]) 

422 

423 backgroundCounts[filterName] = cts 

424 

425 # Go through each image pixel by pixel. 

426 # Treat the value in the clean image as the mean intensity for that pixel. 

427 # Sum up (noisy-clean)^2/var 

428 # where var is determined by Poisson statistics from mean and readnoise. 

429 # Divide by the number of pixel 

430 # Make sure that this average does not deviate from unity 

431 

432 countedImages = 0 

433 for noisyName, cleanName in zip(noisyFileList, cleanFileList): 

434 noisyIm = afwImage.ImageF(noisyName).getArray() 

435 cleanIm = afwImage.ImageF(cleanName).getArray() 

436 

437 totalVar = 0.0 

438 totalMean = 0.0 

439 ct = 0.0 

440 

441 self.assertEqual(cleanIm.shape[0], noisyIm.shape[0], msg='images not same shape') 

442 self.assertEqual(cleanIm.shape[1], noisyIm.shape[1], msg='images not same shape') 

443 

444 var = cleanIm/gain + readnoise/(gain*gain) 

445 totalVar = (np.power(noisyIm-cleanIm, 2)/var).sum() 

446 totalMean = cleanIm.sum() 

447 ct = float(cleanIm.shape[0]*cleanIm.shape[1]) 

448 totalVar = totalVar/ct 

449 totalMean = totalMean/ct 

450 

451 if totalMean >= 100.0: 

452 countedImages += 1 

453 self.assertLess(np.abs(totalVar-1.0), 0.05) 

454 

455 os.unlink(noisyName) 

456 os.unlink(cleanName) 

457 

458 self.assertGreater(countedImages, 0) 

459 

460 def testGalaxyBulges(self): 

461 """ 

462 Test that GalSimInterpreter puts the right number of counts on images of galaxy bulges 

463 """ 

464 catName = os.path.join(self.scratch_dir, 'testBulgeCat.sav') 

465 gals = testGalaxyBulgeDBObj(driver=self.driver, database=self.dbName) 

466 cat = testGalaxyCatalog(gals, obs_metadata = self.obs_metadata) 

467 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

468 cat.write_catalog(catName) 

469 self.catalogTester(catName=catName, catalog=cat, nameRoot='bulge') 

470 if os.path.exists(catName): 

471 os.unlink(catName) 

472 

473 def testGalaxyDisks(self): 

474 """ 

475 Test that GalSimInterpreter puts the right number of counts on images of galaxy disks 

476 """ 

477 catName = os.path.join(self.scratch_dir, 'testDiskCat.sav') 

478 gals = testGalaxyDiskDBObj(driver=self.driver, database=self.dbName) 

479 cat = testGalaxyCatalog(gals, obs_metadata = self.obs_metadata) 

480 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

481 cat.write_catalog(catName) 

482 self.catalogTester(catName=catName, catalog=cat, nameRoot='disk') 

483 if os.path.exists(catName): 

484 os.unlink(catName) 

485 

486 def testStars(self): 

487 """ 

488 Test that GalSimInterpreter puts the right number of counts on images of stars 

489 """ 

490 catName = os.path.join(self.scratch_dir, 'testStarCat.sav') 

491 stars = testStarsDBObj(driver=self.driver, database=self.dbName) 

492 cat = testStarCatalog(stars, obs_metadata = self.obs_metadata) 

493 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

494 cat.write_catalog(catName) 

495 self.catalogTester(catName=catName, catalog=cat, nameRoot='stars') 

496 if os.path.exists(catName): 

497 os.unlink(catName) 

498 

499 def testFakeBandpasses(self): 

500 """ 

501 Test GalSim catalog with alternate bandpasses 

502 """ 

503 catName = os.path.join(self.scratch_dir, 'testFakeBandpassCat.sav') 

504 m5 = [22.0, 23.0, 25.0] 

505 seeing = [0.6, 0.5, 0.7] 

506 bandpassNames = ['x', 'y', 'z'] 

507 obs_metadata = ObservationMetaData(pointingRA=self.obs_metadata.pointingRA, 

508 pointingDec=self.obs_metadata.pointingDec, 

509 rotSkyPos=self.obs_metadata.rotSkyPos, 

510 mjd=self.obs_metadata.mjd, 

511 bandpassName=bandpassNames, 

512 m5=m5, 

513 seeing=seeing) 

514 

515 stars = testStarsDBObj(driver=self.driver, database=self.dbName) 

516 cat = testFakeBandpassCatalog(stars, obs_metadata=obs_metadata) 

517 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

518 cat.write_catalog(catName) 

519 bandpassDir = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'testThroughputs') 

520 self.catalogTester(catName=catName, catalog=cat, nameRoot='fakeBandpass', 

521 bandpassDir=bandpassDir, bandpassRoot='fakeTotal_') 

522 

523 if os.path.exists(catName): 

524 os.unlink(catName) 

525 

526 def testFakeSeds(self): 

527 """ 

528 Test GalSim catalog with alternate Seds 

529 """ 

530 catName = os.path.join(self.scratch_dir, 'testFakeSedCat.sav') 

531 m5 = [22.0, 23.0, 25.0] 

532 seeing = [0.6, 0.5, 0.7] 

533 bandpassNames = ['x', 'y', 'z'] 

534 obs_metadata = ObservationMetaData(pointingRA=self.obs_metadata.pointingRA, 

535 pointingDec=self.obs_metadata.pointingDec, 

536 rotSkyPos=self.obs_metadata.rotSkyPos, 

537 mjd=self.obs_metadata.mjd, 

538 bandpassName=bandpassNames, 

539 m5=m5, 

540 seeing=seeing) 

541 

542 stars = testStarsDBObj(driver=self.driver, database=self.dbName) 

543 cat = testFakeSedCatalog(stars, obs_metadata=obs_metadata) 

544 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

545 cat.write_catalog(catName) 

546 bandpassDir = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'testThroughputs') 

547 sedDir = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'testSeds') 

548 self.catalogTester(catName=catName, catalog=cat, nameRoot='fakeSed', 

549 bandpassDir=bandpassDir, bandpassRoot='fakeTotal_', 

550 sedDir=sedDir) 

551 

552 if os.path.exists(catName): 

553 os.unlink(catName) 

554 

555 def testAgns(self): 

556 """ 

557 Test that GalSimInterpreter puts the right number of counts on images of AGN 

558 """ 

559 catName = os.path.join(self.scratch_dir, 'testAgnCat.sav') 

560 agn = testGalaxyAgnDBObj(driver=self.driver, database=self.dbName) 

561 cat = testAgnCatalog(agn, obs_metadata = self.obs_metadata) 

562 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

563 cat.write_catalog(catName) 

564 self.catalogTester(catName=catName, catalog=cat, nameRoot='agn') 

565 if os.path.exists(catName): 

566 os.unlink(catName) 

567 

568 def testPSFimages(self): 

569 """ 

570 Test that GalSimInterpreter puts the right number of counts on images of Galaxy bulges convolved 

571 with a PSF 

572 """ 

573 catName = os.path.join(self.scratch_dir, 'testPSFcat.sav') 

574 gals = testGalaxyBulgeDBObj(driver=self.driver, database=self.dbName) 

575 cat = psfCatalog(gals, obs_metadata = self.obs_metadata) 

576 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

577 cat.write_catalog(catName) 

578 self.catalogTester(catName=catName, catalog=cat, nameRoot='psf') 

579 if os.path.exists(catName): 

580 os.unlink(catName) 

581 

582 def testBackground(self): 

583 """ 

584 Test that GalSimInterpreter puts the right number of counts on images of Galaxy bulges with 

585 a sky background 

586 """ 

587 catName = os.path.join(self.scratch_dir, 'testBackgroundCat.sav') 

588 gals = testGalaxyBulgeDBObj(driver=self.driver, database=self.dbName) 

589 cat = backgroundCatalog(gals, obs_metadata = self.obs_metadata) 

590 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

591 cat.write_catalog(catName) 

592 self.catalogTester(catName=catName, catalog=cat, nameRoot='background') 

593 if os.path.exists(catName): 

594 os.unlink(catName) 

595 

596 def testNoisyCatalog(self): 

597 """ 

598 Compare noisy and noiseless images drawn from the same catalog. 

599 Make sure that the pixel-by-pixel difference between the two is 

600 as expected from Poisson statistics. 

601 """ 

602 noisyCatName = os.path.join(self.scratch_dir, 'testNoisyCatalog.sav') 

603 cleanCatName = os.path.join(self.scratch_dir, 'testCleanCatalog.sav') 

604 

605 gals = testGalaxyBulgeDBObj(driver=self.driver, database=self.dbName) 

606 

607 noisyCat = noisyCatalog(gals, obs_metadata=self.obs_metadata) 

608 cleanCat = backgroundCatalog(gals, obs_metadata=self.obs_metadata) 

609 

610 noisyCat.camera_wrapper = GalSimCameraWrapper(self.camera) 

611 cleanCat.camera_wrapper = GalSimCameraWrapper(self.camera) 

612 

613 noisyCat.write_catalog(noisyCatName) 

614 cleanCat.write_catalog(cleanCatName) 

615 

616 self.compareCatalogs(cleanCat, noisyCat, PhotometricParameters().gain, 

617 PhotometricParameters().readnoise) 

618 

619 if os.path.exists(noisyCatName): 

620 os.unlink(noisyCatName) 

621 if os.path.exists(cleanCatName): 

622 os.unlink(cleanCatName) 

623 

624 def testNoise(self): 

625 """ 

626 Test that ExampleCCDNoise puts the expected counts on an image 

627 by generating a flat image, adding noise and background to it, 

628 and calculating the variance of counts in the image. 

629 """ 

630 

631 lsstDefaults = LSSTdefaults() 

632 gain = 2.5 

633 readnoise = 6.0 

634 photParams = PhotometricParameters(gain=gain, readnoise=readnoise) 

635 img = galsim.Image(100, 100) 

636 noise = ExampleCCDNoise(seed=42) 

637 m5 = 24.5 

638 bandpass = Bandpass() 

639 bandpass.readThroughput(os.path.join(getPackageDir('throughputs'), 

640 'baseline', 'total_r.dat')) 

641 background = calcSkyCountsPerPixelForM5(m5, bandpass, FWHMeff=lsstDefaults.FWHMeff('r'), 

642 photParams=photParams) 

643 

644 noisyImage = noise.addNoiseAndBackground(img, bandpass, m5=m5, 

645 FWHMeff=lsstDefaults.FWHMeff('r'), 

646 photParams=photParams) 

647 

648 mean = 0.0 

649 var = 0.0 

650 for ix in range(1, 101): 

651 for iy in range(1, 101): 

652 mean += noisyImage(ix, iy) 

653 

654 mean = mean/10000.0 

655 

656 for ix in range(1, 101): 

657 for iy in range(1, 101): 

658 var += (noisyImage(ix, iy) - mean)*(noisyImage(ix, iy) - mean) 

659 

660 var = var/9999.0 

661 

662 varElectrons = background*gain + readnoise 

663 varADU = varElectrons/(gain*gain) 

664 

665 msg = 'background %e mean %e ' % (background, mean) 

666 self.assertLess(np.abs(background/mean - 1.0), 0.05, msg=msg) 

667 

668 msg = 'var %e varADU %e ; ratio %e ; background %e' % (var, varADU, var/varADU, background) 

669 self.assertLess(np.abs(var/varADU - 1.0), 0.05, msg=msg) 

670 

671 def testMultipleImages(self): 

672 """ 

673 Test that GalSimInterpreter puts the right number of counts on images of multiple objects 

674 """ 

675 dbName = os.path.join(self.scratch_dir, 'galSimTestMultipleDB.db') 

676 driver = 'sqlite' 

677 

678 if os.path.exists(dbName): 

679 os.unlink(dbName) 

680 

681 deltaRA = np.array([72.0/3600.0, 55.0/3600.0, 75.0/3600.0]) 

682 deltaDec = np.array([0.0, 15.0/3600.0, -15.0/3600.0]) 

683 obs_metadata = makePhoSimTestDB(filename=dbName, size=1, 

684 deltaRA=deltaRA, deltaDec=deltaDec, 

685 bandpass=self.bandpassNameList, 

686 m5=self.m5, seeing=self.seeing) 

687 

688 gals = testGalaxyBulgeDBObj(driver=driver, database=dbName) 

689 cat = testGalaxyCatalog(gals, obs_metadata=obs_metadata) 

690 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

691 catName = os.path.join(self.scratch_dir, 'multipleCatalog.sav') 

692 cat.write_catalog(catName) 

693 self.catalogTester(catName=catName, catalog=cat, nameRoot='multiple') 

694 if os.path.exists(catName): 

695 os.unlink(catName) 

696 

697 stars = testStarsDBObj(driver=driver, database=dbName) 

698 cat = testStarCatalog(stars, obs_metadata=obs_metadata) 

699 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

700 catName = os.path.join(self.scratch_dir, 'multipleStarCatalog.sav') 

701 cat.write_catalog(catName) 

702 self.catalogTester(catName=catName, catalog=cat, nameRoot='multipleStars') 

703 if os.path.exists(catName): 

704 os.unlink(catName) 

705 

706 if os.path.exists(dbName): 

707 os.unlink(dbName) 

708 

709 def testCompoundFitsFiles(self): 

710 """ 

711 Test that GalSimInterpreter puts the right number of counts on images 

712 containing different types of objects 

713 """ 

714 driver = 'sqlite' 

715 dbName1 = os.path.join(self.scratch_dir, 'galSimTestCompound1DB.db') 

716 if os.path.exists(dbName1): 

717 os.unlink(dbName1) 

718 

719 deltaRA = np.array([72.0/3600.0, 55.0/3600.0, 75.0/3600.0]) 

720 deltaDec = np.array([0.0, 15.0/3600.0, -15.0/3600.0]) 

721 obs_metadata1 = makePhoSimTestDB(filename=dbName1, size=1, 

722 deltaRA=deltaRA, deltaDec=deltaDec, 

723 bandpass=self.bandpassNameList, 

724 m5=self.m5, seeing=self.seeing) 

725 

726 dbName2 = os.path.join(self.scratch_dir, 'galSimTestCompound2DB.db') 

727 if os.path.exists(dbName2): 

728 os.unlink(dbName2) 

729 

730 deltaRA = np.array([55.0/3600.0, 60.0/3600.0, 62.0/3600.0]) 

731 deltaDec = np.array([-3.0/3600.0, 10.0/3600.0, 10.0/3600.0]) 

732 obs_metadata2 = makePhoSimTestDB(filename=dbName2, size=1, 

733 deltaRA=deltaRA, deltaDec=deltaDec, 

734 bandpass=self.bandpassNameList, 

735 m5=self.m5, seeing=self.seeing) 

736 

737 gals = testGalaxyBulgeDBObj(driver=driver, database=dbName1) 

738 cat1 = testGalaxyCatalog(gals, obs_metadata=obs_metadata1) 

739 cat1.camera_wrapper = GalSimCameraWrapper(self.camera) 

740 catName = os.path.join(self.scratch_dir, 'compoundCatalog.sav') 

741 cat1.write_catalog(catName) 

742 

743 stars = testStarsDBObj(driver=driver, database=dbName2) 

744 cat2 = testStarCatalog(stars, obs_metadata=obs_metadata2) 

745 cat2.copyGalSimInterpreter(cat1) 

746 cat2.write_catalog(catName, write_header=False, write_mode='a') 

747 self.catalogTester(catName=catName, catalog=cat2, nameRoot='compound') 

748 

749 if os.path.exists(dbName1): 

750 os.unlink(dbName1) 

751 if os.path.exists(dbName2): 

752 os.unlink(dbName2) 

753 if os.path.exists(catName): 

754 os.unlink(catName) 

755 

756 def testCompoundFitsFiles_one_empty(self): 

757 """ 

758 Test that GalSimInterpreter puts the right number of counts on images 

759 containing different types of objects in the case where one of the 

760 input catalogs is empty (really, this is testing that we can 

761 successfully copy the GalSimInterpreter and all of the supporting 

762 properties from an empty GalSimCatalog to another GalSimCatalog) 

763 """ 

764 driver = 'sqlite' 

765 dbName1 = os.path.join(self.scratch_dir, 'galSimTestCompound1DB_one_empty.db') 

766 if os.path.exists(dbName1): 

767 os.unlink(dbName1) 

768 

769 deltaRA = np.array([72.0/3600.0, 55.0/3600.0, 75.0/3600.0]) 

770 deltaDec = np.array([0.0, 15.0/3600.0, -15.0/3600.0]) 

771 obs_metadata1 = makePhoSimTestDB(filename=dbName1, size=1, 

772 deltaRA=deltaRA, deltaDec=deltaDec, 

773 bandpass=self.bandpassNameList, 

774 m5=self.m5, seeing=self.seeing) 

775 

776 dbName2 = os.path.join(self.scratch_dir, 'galSimTestCompound2DB_one_empty.db') 

777 if os.path.exists(dbName2): 

778 os.unlink(dbName2) 

779 

780 deltaRA = np.array([55.0/3600.0, 60.0/3600.0, 62.0/3600.0]) 

781 deltaDec = np.array([-3.0/3600.0, 10.0/3600.0, 10.0/3600.0]) 

782 obs_metadata2 = makePhoSimTestDB(filename=dbName2, size=1, 

783 deltaRA=deltaRA, deltaDec=deltaDec, 

784 bandpass=self.bandpassNameList, 

785 m5=self.m5, seeing=self.seeing) 

786 

787 gals = testGalaxyBulgeDBObj(driver=driver, database=dbName1) 

788 

789 # shift the obs_metadata so that the catalog will not contain 

790 # any objects 

791 ra0 = obs_metadata1.pointingRA 

792 dec0 = obs_metadata1.pointingDec 

793 obs_metadata1.pointingRA = ra0 + 20.0 

794 

795 cat1 = testGalaxyCatalog(gals, obs_metadata=obs_metadata1) 

796 cat1.camera_wrapper = GalSimCameraWrapper(self.camera) 

797 catName = os.path.join(self.scratch_dir, 'compoundCatalog_one_empty.sav') 

798 cat1.write_catalog(catName) 

799 with open(catName, "r") as input_file: 

800 input_lines = input_file.readlines() 

801 self.assertEqual(len(input_lines), 1) # just the header 

802 self.assertFalse(hasattr(cat1, 'bandpassDict')) 

803 

804 stars = testStarsDBObj(driver=driver, database=dbName2) 

805 cat2 = testStarCatalog(stars, obs_metadata=obs_metadata2) 

806 cat2.copyGalSimInterpreter(cat1) 

807 cat2.write_catalog(catName, write_header=False, write_mode='a') 

808 self.catalogTester(catName=catName, catalog=cat2, nameRoot='compound_one_empty') 

809 

810 if os.path.exists(dbName1): 

811 os.unlink(dbName1) 

812 if os.path.exists(dbName2): 

813 os.unlink(dbName2) 

814 if os.path.exists(catName): 

815 os.unlink(catName) 

816 

817 def testPlacement(self): 

818 """ 

819 Test that GalSimInterpreter puts objects on the right detectors. 

820 

821 Do so by creating a catalog of 3 closely-packed stars. Draw test FITS 

822 images of them using the GalSim Catalog infrastructure. Draw control FITS 

823 images of the detectors in the camera, paranoidly including every star 

824 in every control image (GalSim contains code such that it will not 

825 actually add flux to an image in cases where we try to include a 

826 star that does not actually fall on a detector). Compare that 

827 

828 a) the fluxes of the test and control images agree within some tolerance 

829 

830 b) the fluxes of control images that have no corresponding test image 

831 (i.e. detectors on which no star actually fell) are effectively zero 

832 """ 

833 

834 # generate the database 

835 np_rng = np.random.RandomState(32) 

836 gs_rng = galsim.UniformDeviate(112) 

837 catSize = 3 

838 dbName = 'galSimPlacementTestDB.db' 

839 driver = 'sqlite' 

840 if os.path.exists(dbName): 

841 os.unlink(dbName) 

842 

843 deltaRA = (-40.0 + np_rng.random_sample(catSize)*(120.0))/3600.0 

844 deltaDec = (-20.0 + np_rng.random_sample(catSize)*(80.0))/3600.0 

845 obs_metadata = makePhoSimTestDB(filename=dbName, deltaRA=deltaRA, deltaDec=deltaDec, 

846 bandpass=self.bandpassNameList, 

847 m5=self.m5, seeing=self.seeing) 

848 

849 stars = testStarsDBObj(driver=driver, database=dbName) 

850 

851 # create the catalog 

852 cat = testStarCatalog(stars, obs_metadata = obs_metadata) 

853 cat.camera_wrapper = GalSimCameraWrapper(self.camera) 

854 results = cat.iter_catalog() 

855 firstLine = True 

856 

857 # iterate over the catalog, giving every star a chance to 

858 # illumine every detector 

859 controlImages = {} 

860 for i, line in enumerate(results): 

861 xPupil = line[5] 

862 yPupil = line[6] 

863 

864 if firstLine: 

865 sedList = list(cat._calculateGalSimSeds()) 

866 for detector in cat.galSimInterpreter.detectors: 

867 for bandpass in cat.galSimInterpreter.bandpassDict: 

868 controlImages['placementControl_' + 

869 cat.galSimInterpreter._getFileName(detector=detector, 

870 bandpassName=bandpass)] = \ 

871 cat.galSimInterpreter.blankImage(detector=detector) 

872 

873 firstLine = False 

874 

875 for bp in cat.galSimInterpreter.bandpassDict: 

876 bandpass = cat.galSimInterpreter.bandpassDict[bp] 

877 adu = sedList[i].calcADU(bandpass, cat.photParams) 

878 for detector in cat.galSimInterpreter.detectors: 

879 centeredObj = cat.galSimInterpreter.PSF.applyPSF(xPupil=xPupil, yPupil=yPupil) 

880 

881 xPix, yPix = pixelCoordsFromPupilCoords(radiansFromArcsec(xPupil), 

882 radiansFromArcsec(yPupil), 

883 chipName = detector.name, 

884 camera = detector._cameraWrapper.camera) 

885 

886 dx = xPix - detector.xCenterPix 

887 dy = yPix - detector.yCenterPix 

888 obj = centeredObj.withFlux(adu*detector.photParams.gain) 

889 localImage = cat.galSimInterpreter.blankImage(detector=detector) 

890 localImage = obj.drawImage(wcs=detector.wcs, method='phot', 

891 gain=detector.photParams.gain, image=localImage, 

892 offset=galsim.PositionD(dx, dy), 

893 rng=gs_rng) 

894 

895 controlImages['placementControl_' + 

896 cat.galSimInterpreter._getFileName(detector=detector, 

897 bandpassName=bp)] += localImage 

898 

899 self.assertGreater(len(controlImages), 0) 

900 

901 for name in controlImages: 

902 controlImages[name].write(file_name=name) 

903 

904 # write the test images using the catalog infrastructure 

905 testNames = cat.write_images(nameRoot='placementTest') 

906 

907 # make sure that every test image has a corresponding control image 

908 for testName in testNames: 

909 controlName = testName.replace('Test', 'Control') 

910 msg = '%s has no counterpart ' % testName 

911 self.assertIn(controlName, controlImages, msg=msg) 

912 

913 # make sure that the test and control images agree to some tolerance 

914 zeroFlux = 0 

915 valid = 0 

916 for controlName in controlImages: 

917 controlImage = afwImage.ImageF(controlName) 

918 controlFlux = controlImage.getArray().sum() 

919 

920 testName = controlName.replace('Control', 'Test') 

921 if testName in testNames: 

922 testImage = afwImage.ImageF(testName) 

923 testFlux = testImage.getArray().sum() 

924 if controlFlux > 1000.0: 

925 countSigma = np.sqrt(controlFlux/cat.photParams.gain) 

926 msg = '%s: controlFlux = %e, testFlux = %e, sigma %e' \ 

927 % (controlName, controlFlux, testFlux, countSigma) 

928 

929 # the randomness of photon shooting means that faint images won't agree 

930 self.assertLess(np.abs(controlFlux-testFlux), 4.0*countSigma, msg=msg) 

931 valid += 1 

932 else: 

933 # make sure that controlImages that have no corresponding test image really do 

934 # have zero flux (because no star fell on them) 

935 zeroFlux += 1 

936 msg = '%s has flux %e but was not written by catalog' % (controlName, controlFlux) 

937 self.assertLess(controlFlux, 1.0, msg=msg) 

938 

939 self.assertGreater(valid, 5) 

940 self.assertGreater(zeroFlux, 0) 

941 

942 for testName in testNames: 

943 if os.path.exists(testName): 

944 os.unlink(testName) 

945 

946 for controlName in controlImages: 

947 if os.path.exists(controlName): 

948 os.unlink(controlName) 

949 

950 if os.path.exists(dbName): 

951 os.unlink(dbName) 

952 

953 def testPSF(self): 

954 """ 

955 This method will test that SNRdocumentPSF returns a PSF 

956 with the correct Full Width at Half Max 

957 """ 

958 

959 fwhm = 0.4 # in arc-seconds; make sure that it divides evenly by scale, so that rounding 

960 # half integer numbers of pixels does not affect the unit test 

961 

962 scale = 0.1 # arc-seconds per pixel 

963 

964 psf = SNRdocumentPSF(fwhm=fwhm) 

965 image = psf._cached_psf.drawImage(scale=scale) 

966 xCenter = (image.xmax + image.xmin)/2 

967 yCenter = (image.ymax + image.ymin)/2 

968 

969 maxValue = image(xCenter, yCenter) # because the default is to center GSObjects 

970 halfDex = int(np.round(0.5*fwhm/scale)) # the distance from the center corresponding to FWHM 

971 

972 # Test that pixel combinations bracketing the expected FWHM value behave 

973 # the way we expect them to 

974 midP1 = image(xCenter+halfDex+1, yCenter) 

975 midM1 = image(xCenter+halfDex-1, yCenter) 

976 msg = '%e is not > %e ' % (midM1, 0.5*maxValue) 

977 self.assertGreater(midM1, 0.5*maxValue, msg=msg) 

978 msg = '%e is not < %e ' % (midP1, 0.5*maxValue) 

979 self.assertLess(midP1, 0.5*maxValue, msg=msg) 

980 

981 midP1 = image(xCenter-halfDex-1, yCenter) 

982 midM1 = image(xCenter-halfDex+1, yCenter) 

983 msg = '%e is not > %e ' % (midM1, 0.5*maxValue) 

984 self.assertGreater(midM1, 0.5*maxValue, msg=msg) 

985 msg = '%e is not < %e ' % (midP1, 0.5*maxValue) 

986 self.assertLess(midP1, 0.5*maxValue, msg=msg) 

987 

988 midP1 = image(xCenter, yCenter+halfDex+1) 

989 midM1 = image(xCenter, yCenter+halfDex-1) 

990 msg = '%e is not > %e ' % (midM1, 0.5*maxValue) 

991 self.assertGreater(midM1, 0.5*maxValue, msg=msg) 

992 msg = '%e is not < %e ' % (midP1, 0.5*maxValue) 

993 self.assertLess(midP1, 0.5*maxValue, msg=msg) 

994 

995 midP1 = image(xCenter, yCenter-halfDex-1) 

996 midM1 = image(xCenter, yCenter-halfDex+1) 

997 msg = '%e is not > %e ' % (midM1, 0.5*maxValue) 

998 self.assertGreater(midM1, 0.5*maxValue, msg=msg) 

999 msg = '%e is not < %e ' % (midP1, 0.5*maxValue) 

1000 self.assertLess(midP1, 0.5*maxValue, msg=msg) 

1001 

1002 

1003class GsDetector(object): 

1004 """ 

1005 Minimal implementation of an interface-compatible version 

1006 of GalSimDetector for testing the GalSimInterpreter checkpointing 

1007 functions. 

1008 """ 

1009 def __init__(self, detname): 

1010 self.detname = detname 

1011 self.xMaxPix = 4000 

1012 self.yMaxPix = 4000 

1013 self.xMinPix = 1 

1014 self.yMinPix = 1 

1015 self.wcs = galsim.wcs.BaseWCS() 

1016 

1017 def name(self): 

1018 return self.detname 

1019 

1020 

1021class CheckPointingTestCase(unittest.TestCase): 

1022 """ 

1023 TestCase class for testing the GalSimInterpreter checkpointing functions. 

1024 """ 

1025 def setUp(self): 

1026 self.output_dir = os.path.join(getPackageDir('sims_GalSimInterface'), 

1027 'tests', 'checkpoint_dir') 

1028 if not os.path.exists(self.output_dir): 

1029 os.makedirs(self.output_dir) 

1030 self.cp_file = os.path.join(self.output_dir, 'checkpoint_test.pkl') 

1031 

1032 def tearDown(self): 

1033 if os.path.exists(self.cp_file): 

1034 os.remove(self.cp_file) 

1035 if os.path.exists(self.output_dir): 

1036 os.rmdir(self.output_dir) 

1037 

1038 def test_checkpointing(self): 

1039 "Test checkpointing of .detectorImages data." 

1040 camera = camTestUtils.CameraWrapper().camera 

1041 camera_wrapper = GalSimCameraWrapper(camera) 

1042 phot_params = PhotometricParameters() 

1043 obs_md = ObservationMetaData(pointingRA=23.0, 

1044 pointingDec=12.0, 

1045 rotSkyPos=13.2, 

1046 mjd=59580.0, 

1047 bandpassName='r') 

1048 

1049 detectors = [make_galsim_detector(camera_wrapper, dd.getName(), 

1050 phot_params, obs_md) 

1051 for dd in camera_wrapper.camera] 

1052 

1053 # Create a GalSimInterpreter object and set the checkpoint 

1054 # attributes. 

1055 gs_interpreter = GalSimInterpreter(detectors=detectors) 

1056 gs_interpreter.checkpoint_file = self.cp_file 

1057 

1058 nobj = 10 

1059 gs_interpreter.nobj_checkpoint = nobj 

1060 

1061 # Set the image data by hand. 

1062 key = "R00_S00_r.fits" 

1063 detname = "R:0,0 S:0,0" 

1064 detector = make_galsim_detector(camera_wrapper, detname, 

1065 phot_params, obs_md) 

1066 image = gs_interpreter.blankImage(detector=detector) 

1067 image += 17 

1068 gs_interpreter.detectorImages[key] = image 

1069 

1070 # Add some drawn objects and check that the checkpoint file is 

1071 # written at the right cadence. 

1072 for uniqueId in range(1, nobj+1): 

1073 gs_interpreter.drawn_objects.add(uniqueId) 

1074 gs_interpreter.write_checkpoint() 

1075 if uniqueId < nobj: 

1076 self.assertFalse(os.path.isfile(self.cp_file)) 

1077 else: 

1078 self.assertTrue(os.path.isfile(self.cp_file)) 

1079 

1080 # Verify that the checkpointed data has the expected content. 

1081 with open(self.cp_file, 'rb') as input_: 

1082 cp_data = pickle.load(input_) 

1083 self.assertTrue(np.array_equal(cp_data['images'][key], image.array)) 

1084 

1085 # Check the restore_checkpoint function. 

1086 new_interpreter = GalSimInterpreter(detectors=detectors) 

1087 new_interpreter.checkpoint_file = self.cp_file 

1088 new_interpreter.restore_checkpoint(camera_wrapper, 

1089 phot_params, 

1090 obs_md) 

1091 

1092 self.assertEqual(new_interpreter.drawn_objects, 

1093 gs_interpreter.drawn_objects) 

1094 

1095 self.assertEqual(set(new_interpreter.detectorImages.keys()), 

1096 set(gs_interpreter.detectorImages.keys())) 

1097 

1098 for det_name in new_interpreter.detectorImages.keys(): 

1099 new_img = new_interpreter.detectorImages[det_name] 

1100 gs_img = gs_interpreter.detectorImages[det_name] 

1101 np.testing.assert_array_equal(new_img.array, 

1102 gs_img.array) 

1103 self.assertEqual(new_img.bounds, gs_img.bounds) 

1104 self.assertEqual(new_img.wcs.crpix1, gs_img.wcs.crpix1) 

1105 self.assertEqual(new_img.wcs.crpix2, gs_img.wcs.crpix2) 

1106 self.assertEqual(new_img.wcs.crval1, gs_img.wcs.crval1) 

1107 self.assertEqual(new_img.wcs.crval2, gs_img.wcs.crval2) 

1108 self.assertEqual(new_img.wcs.detectorName, gs_img.wcs.detectorName) 

1109 for name in new_img.wcs.fitsHeader.names(): 

1110 self.assertEqual(new_img.wcs.fitsHeader.getScalar(name), 

1111 gs_img.wcs.fitsHeader.getScalar(name)) 

1112 

1113 

1114class GetStampBoundsTestCase(unittest.TestCase): 

1115 """ 

1116 TestCase class for the GalSimInterpreter.getStampBounds 

1117 function. 

1118 """ 

1119 def setUp(self): 

1120 self.scratch_dir = tempfile.mkdtemp(dir=ROOT, prefix='GetStampBounds') 

1121 self.db_name = os.path.join(self.scratch_dir, 'galsim_test_db') 

1122 

1123 def tearDown(self): 

1124 clean_up_lsst_camera() 

1125 if os.path.exists(self.db_name): 

1126 os.remove(self.db_name) 

1127 if os.path.exists(self.scratch_dir): 

1128 os.rmdir(self.scratch_dir) 

1129 

1130 def test_getStampBounds(self): 

1131 """Test the getStampBounds function.""" 

1132 seeing = 0.5059960 

1133 altitude = 52.54 

1134 FWHMgeom = 0.7343 

1135 band = 'r' 

1136 obs_md = makePhoSimTestDB(filename=self.db_name, size=1, 

1137 deltaRA=np.array([72/3600]), 

1138 deltaDec=np.array([0]), 

1139 bandpass=band, m5=16, seeing=seeing, 

1140 seedVal=100) 

1141 obs_md.OpsimMetaData['FWHMgeom'] = FWHMgeom 

1142 obs_md.OpsimMetaData['FWHMeff'] = (FWHMgeom - 0.052)/0.822 

1143 obs_md.OpsimMetaData['altitude'] = altitude 

1144 obs_md.OpsimMetaData['rawSeeing'] = seeing 

1145 camera_wrapper = LSSTCameraWrapper() 

1146 detector = make_galsim_detector(camera_wrapper, 'R:2,2 S:1,1', 

1147 PhotometricParameters(), obs_md) 

1148 gs_interpreter = make_gs_interpreter(obs_md, [detector], 

1149 BandpassDict.loadTotalBandpassesFromFiles(), 

1150 None, apply_sensor_model=True) 

1151 

1152 gsobject = GalSimCelestialObject('pointSource', 0, 0, 1e-7, 1e-7, 1e-7, 

1153 0, 1, 'none', dict(), None, 0, '', 

1154 0.01, 0) 

1155 

1156 # Make a reference psf that should be the same as used in 

1157 # .getStampBounds. 

1158 psf = Kolmogorov_and_Gaussian_PSF(airmass=gs_interpreter._airmass, 

1159 rawSeeing=seeing, 

1160 band=band) 

1161 

1162 # Make a reference GSObject with default folding_threshold. 

1163 ref_obj = gs_interpreter.drawPointSource(gsobject, psf=psf) 

1164 

1165 # Set object flux and sky level so that the default folding threshold 

1166 # is used inside .getStampBounds. 

1167 flux = 1e6 

1168 gs_interpreter.sky_bg_per_pixel = 0.006*flux 

1169 image_pos = galsim.PositionD(2000, 2000) 

1170 keep_sb_level = 9 # not used for pointSources 

1171 pixel_scale = 0.2 

1172 test_bounds = gs_interpreter.getStampBounds(gsobject, flux, image_pos, 

1173 keep_sb_level, 

1174 3*keep_sb_level) 

1175 

1176 self.assertEqual(ref_obj.getGoodImageSize(pixel_scale), 

1177 test_bounds.xmax - test_bounds.xmin) 

1178 

1179 # Set a sky level that will produce a larger stamp size. 

1180 gs_interpreter.sky_bg_per_pixel = 0.001*flux 

1181 test_bounds = gs_interpreter.getStampBounds(gsobject, flux, image_pos, 

1182 keep_sb_level, 

1183 3*keep_sb_level) 

1184 

1185 self.assertLess(ref_obj.getGoodImageSize(pixel_scale), 

1186 test_bounds.xmax - test_bounds.xmin) 

1187 

1188 

1189class GetGoodImageSizeTestCase(unittest.TestCase): 

1190 """TestCase class for getGoodPhotImageSize function.""" 

1191 

1192 def test_getGoodPhotImageSize(self): 

1193 """ 

1194 Test the function that computes postage stamp sizes based 

1195 on a minimum surface brightness to enclose. 

1196 """ 

1197 # Make a broad, bright object to test the expected image sizes 

1198 # at different surface brightness levels. 

1199 flux = 1e7 

1200 obj = galsim.Gaussian(sigma=100) 

1201 obj = obj.withFlux(flux) 

1202 

1203 # Test that various keep_sb_levels are bracketed as expected. 

1204 pixel_scale = 0.2 

1205 factor = 1.1 # growth factor used to find image sizes. 

1206 sb_values = np.linspace(10, 150, 5) 

1207 for keep_sb_level in sb_values: 

1208 N = getGoodPhotImageSize(obj, keep_sb_level, pixel_scale=pixel_scale) 

1209 self.assertLess(obj.xValue(N/2*pixel_scale, 0), keep_sb_level) 

1210 self.assertLess(keep_sb_level, obj.xValue(N/2*pixel_scale/factor, 0)) 

1211 

1212 # Test that the min and max limits are applied. 

1213 Nmin = 64 # minimum image size 

1214 N = getGoodPhotImageSize(obj, obj.xValue(0, 0), pixel_scale=pixel_scale) 

1215 self.assertLess(Nmin, N) 

1216 

1217 Nmax = 4096 # maximum image size 

1218 N = getGoodPhotImageSize(obj, 0, pixel_scale=pixel_scale) 

1219 self.assertLessEqual(N, Nmax) 

1220 

1221 

1222class HourAngleTestCase(unittest.TestCase): 

1223 """ 

1224 Test the hour angle calculation given the MJD and pointing 

1225 direction of the observation. 

1226 """ 

1227 def setUp(self): 

1228 self.scratch_dir = tempfile.mkdtemp(dir=ROOT, prefix='HourAngle') 

1229 self.db_name = os.path.join(self.scratch_dir, 'galsim_test_db') 

1230 

1231 def tearDown(self): 

1232 if os.path.exists(self.db_name): 

1233 os.remove(self.db_name) 

1234 if os.path.exists(self.scratch_dir): 

1235 os.rmdir(self.scratch_dir) 

1236 

1237 def test_hour_angle(self): 

1238 """Test the hour angle calculation for LSST.""" 

1239 seeing = 0.5059960 

1240 altitude = 52.54 

1241 FWHMgeom = 0.7343 

1242 band = 'r' 

1243 obs_md = makePhoSimTestDB(filename=self.db_name, size=1, 

1244 deltaRA=np.array([72/3600]), 

1245 deltaDec=np.array([0]), 

1246 bandpass=band, m5=16, seeing=seeing, 

1247 seedVal=100) 

1248 obs_md.OpsimMetaData['FWHMgeom'] = FWHMgeom 

1249 obs_md.OpsimMetaData['FWHMeff'] = (FWHMgeom - 0.052)/0.822 

1250 obs_md.OpsimMetaData['altitude'] = altitude 

1251 obs_md.OpsimMetaData['rawSeeing'] = seeing 

1252 gs_interpreter = make_gs_interpreter(obs_md, [], 

1253 BandpassDict.loadTotalBandpassesFromFiles(), 

1254 None, apply_sensor_model=False) 

1255 

1256 mjd = 59877.15107861111027887 

1257 ra = 55.52107440528638449 

1258 self.assertAlmostEqual(math.cos(math.radians(321.62974517903774)), 

1259 math.cos(math.radians(gs_interpreter.getHourAngle(mjd, ra))), 

1260 places=4) 

1261 

1262 self.assertAlmostEqual(math.sin(math.radians(321.62974517903774)), 

1263 math.sin(math.radians(gs_interpreter.getHourAngle(mjd, ra))), 

1264 places=4) 

1265 

1266 # Pick a MJD such that GAST = -observatory geodetic longitude, 

1267 # so that local hour angle = 360 - ra. 

1268 mjd = 58265.3194197049 - gs_interpreter.observatory.getLongitude().asDegrees()/360. 

1269 self.assertAlmostEqual(-ra, gs_interpreter.getHourAngle(mjd, ra), 

1270 places=4) 

1271 

1272 

1273class MemoryTestClass(lsst.utils.tests.MemoryTestCase): 

1274 pass 

1275 

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

1277 lsst.utils.tests.init() 

1278 unittest.main()