Coverage for tests/testGalSimInterface.py : 15%

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
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
43ROOT = os.path.abspath(os.path.dirname(__file__))
46def setup_module(module):
47 lsst.utils.tests.init()
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']
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')
68 PSF = SNRdocumentPSF()
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']
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')
89 PSF = SNRdocumentPSF()
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']
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')
110 PSF = SNRdocumentPSF()
113class psfCatalog(testGalaxyCatalog):
114 """
115 Adds a PSF to testGalaxyCatalog
116 """
117 PSF = SNRdocumentPSF()
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)
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)
136class testFakeBandpassCatalog(testStarCatalog):
137 """
138 tests the GalSim interface on fake bandpasses
139 """
140 bandpassNames = ['x', 'y', 'z']
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'
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')
155 def get_sedFilepath(self):
156 """
157 map the sedFilenames created by makePhoSimTestDB to the SEDs in
158 in testSeds/
159 """
161 nameMap = {'km20_5750.fits_g40_5790': 'fakeSed1.dat',
162 'm2.0Full.dat': 'fakeSed2.dat',
163 'bergeron_6500_85.dat_6700': 'fakeSed3.dat'}
165 rawNames = self.column_by_name('sedFilename')
166 return np.array([nameMap[nn] for nn in rawNames])
169class GalSimInterfaceTest(unittest.TestCase):
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')
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)
191 cls.driver = 'sqlite'
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)
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
209 def getFilesAndBandpasses(self, catalog, nameRoot=None,
210 bandpassDir=os.path.join(getPackageDir('throughputs'), 'baseline'),
211 bandpassRoot='total_',):
213 """
214 Take a GalSimCatalog. Return a list of fits files and and OrderedDict of bandpasses associated
215 with that catalog
217 @param [in] catalog is a GalSimCatalog instantiation
219 @param [in] nameRoot is the nameRoot prepended to the fits files output by that catalog
221 @param[in] bandpassDir is the directory where bandpass files can be found
223 @param [in] bandpassRoot is the root of the name of the bandpass files
225 @param [out] listOfFiles is a list of the names of the fits files written by this catalog
227 @param [out] bandpassDict is an OrderedDict of Bandpass instantiations corresponding to the
228 filters in this catalog.
229 """
231 # write the fits files
232 catalog.write_images(nameRoot=nameRoot)
234 # a list of bandpasses over which we are integraging
235 listOfFilters = []
236 listOfFiles = []
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
245 listOfFiles.append(name)
247 if name[-6] not in listOfFilters:
248 listOfFilters.append(name[-6])
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
257 return listOfFiles, bandpassDict
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).
271 @param [in] catName is the name of the InstanceCatalog that has been written to disk
273 @param [in] catalog is the actual InstanceCatalog instantiation
275 @param [in] nameRoot is a string appended to the names of the FITS files being written
277 @param [in] bandpassDir is the directory containing the bandpasses against which to test
279 @param [in] bandpassRoot is the root of the name of the bandpass files, i.e.
281 os.path.join(bandpassDir, bandpassRoot + bandpassName + '.dat')
282 """
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 = {}
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 = {}
293 listOfFiles, bandpassDict = self.getFilesAndBandpasses(catalog, nameRoot=nameRoot,
294 bandpassDir=bandpassDir,
295 bandpassRoot=bandpassRoot)
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)
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])
317 backgroundCounts[filterName] = cts
319 for name in controlCounts:
320 filterName = name[-6]
321 controlCounts[name] += backgroundCounts[filterName] * galsimPixels[name]
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 = []
340 for name in listOfFileNames:
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)
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()
358 fullName = nameRoot+'_'+chipName+'_'+filterName+'.fits'
360 fullSedName = os.path.join(sedDir, sedName)
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)
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)
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)
384 if catalog.noise_and_background is not None \
385 and catalog.noise_and_background.addBackground:
387 msg += 'background per pixel %e pixels %e %s' % \
388 (backgroundCounts[ff[-6]], galsimPixels[ff], ff)
390 self.assertLess(np.abs(controlCounts[ff] - galsimCounts[ff]), 5*countSigma,
391 msg=msg)
392 elif galsimCounts[ff] > 0.001:
393 unDrawnDetectors += 1
395 # to make sure we did not neglect more than one detector
396 self.assertGreater(drawnDetectors, 0)
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.
403 @param [in] cleanCatalog is the noiseless GalSimCatalog instantiation
405 @param [in] noisyCatalog is the noisy GalSimCatalog instantiation
407 @param [in] gain is the electrons per ADU for the GalSimCatalogs
409 @param [in] readnoise is the electrons per pixel per exposure of the GalSimCatalogs]
410 """
412 cleanFileList, cleanBandpassDict = self.getFilesAndBandpasses(cleanCatalog, nameRoot='clean')
413 noisyFileList, noisyBandpassDict = self.getFilesAndBandpasses(noisyCatalog, nameRoot='unclean')
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])
423 backgroundCounts[filterName] = cts
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
432 countedImages = 0
433 for noisyName, cleanName in zip(noisyFileList, cleanFileList):
434 noisyIm = afwImage.ImageF(noisyName).getArray()
435 cleanIm = afwImage.ImageF(cleanName).getArray()
437 totalVar = 0.0
438 totalMean = 0.0
439 ct = 0.0
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')
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
451 if totalMean >= 100.0:
452 countedImages += 1
453 self.assertLess(np.abs(totalVar-1.0), 0.05)
455 os.unlink(noisyName)
456 os.unlink(cleanName)
458 self.assertGreater(countedImages, 0)
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)
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)
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)
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)
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_')
523 if os.path.exists(catName):
524 os.unlink(catName)
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)
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)
552 if os.path.exists(catName):
553 os.unlink(catName)
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)
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)
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)
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')
605 gals = testGalaxyBulgeDBObj(driver=self.driver, database=self.dbName)
607 noisyCat = noisyCatalog(gals, obs_metadata=self.obs_metadata)
608 cleanCat = backgroundCatalog(gals, obs_metadata=self.obs_metadata)
610 noisyCat.camera_wrapper = GalSimCameraWrapper(self.camera)
611 cleanCat.camera_wrapper = GalSimCameraWrapper(self.camera)
613 noisyCat.write_catalog(noisyCatName)
614 cleanCat.write_catalog(cleanCatName)
616 self.compareCatalogs(cleanCat, noisyCat, PhotometricParameters().gain,
617 PhotometricParameters().readnoise)
619 if os.path.exists(noisyCatName):
620 os.unlink(noisyCatName)
621 if os.path.exists(cleanCatName):
622 os.unlink(cleanCatName)
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 """
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)
644 noisyImage = noise.addNoiseAndBackground(img, bandpass, m5=m5,
645 FWHMeff=lsstDefaults.FWHMeff('r'),
646 photParams=photParams)
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)
654 mean = mean/10000.0
656 for ix in range(1, 101):
657 for iy in range(1, 101):
658 var += (noisyImage(ix, iy) - mean)*(noisyImage(ix, iy) - mean)
660 var = var/9999.0
662 varElectrons = background*gain + readnoise
663 varADU = varElectrons/(gain*gain)
665 msg = 'background %e mean %e ' % (background, mean)
666 self.assertLess(np.abs(background/mean - 1.0), 0.05, msg=msg)
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)
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'
678 if os.path.exists(dbName):
679 os.unlink(dbName)
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)
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)
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)
706 if os.path.exists(dbName):
707 os.unlink(dbName)
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)
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)
726 dbName2 = os.path.join(self.scratch_dir, 'galSimTestCompound2DB.db')
727 if os.path.exists(dbName2):
728 os.unlink(dbName2)
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)
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)
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')
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)
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)
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)
776 dbName2 = os.path.join(self.scratch_dir, 'galSimTestCompound2DB_one_empty.db')
777 if os.path.exists(dbName2):
778 os.unlink(dbName2)
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)
787 gals = testGalaxyBulgeDBObj(driver=driver, database=dbName1)
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
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'))
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')
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)
817 def testPlacement(self):
818 """
819 Test that GalSimInterpreter puts objects on the right detectors.
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
828 a) the fluxes of the test and control images agree within some tolerance
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 """
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)
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)
849 stars = testStarsDBObj(driver=driver, database=dbName)
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
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]
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)
873 firstLine = False
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)
881 xPix, yPix = pixelCoordsFromPupilCoords(radiansFromArcsec(xPupil),
882 radiansFromArcsec(yPupil),
883 chipName = detector.name,
884 camera = detector._cameraWrapper.camera)
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)
895 controlImages['placementControl_' +
896 cat.galSimInterpreter._getFileName(detector=detector,
897 bandpassName=bp)] += localImage
899 self.assertGreater(len(controlImages), 0)
901 for name in controlImages:
902 controlImages[name].write(file_name=name)
904 # write the test images using the catalog infrastructure
905 testNames = cat.write_images(nameRoot='placementTest')
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)
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()
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)
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)
939 self.assertGreater(valid, 5)
940 self.assertGreater(zeroFlux, 0)
942 for testName in testNames:
943 if os.path.exists(testName):
944 os.unlink(testName)
946 for controlName in controlImages:
947 if os.path.exists(controlName):
948 os.unlink(controlName)
950 if os.path.exists(dbName):
951 os.unlink(dbName)
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 """
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
962 scale = 0.1 # arc-seconds per pixel
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
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
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)
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)
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)
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)
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()
1017 def name(self):
1018 return self.detname
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')
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)
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')
1049 detectors = [make_galsim_detector(camera_wrapper, dd.getName(),
1050 phot_params, obs_md)
1051 for dd in camera_wrapper.camera]
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
1058 nobj = 10
1059 gs_interpreter.nobj_checkpoint = nobj
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
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))
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))
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)
1092 self.assertEqual(new_interpreter.drawn_objects,
1093 gs_interpreter.drawn_objects)
1095 self.assertEqual(set(new_interpreter.detectorImages.keys()),
1096 set(gs_interpreter.detectorImages.keys()))
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))
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')
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)
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)
1152 gsobject = GalSimCelestialObject('pointSource', 0, 0, 1e-7, 1e-7, 1e-7,
1153 0, 1, 'none', dict(), None, 0, '',
1154 0.01, 0)
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)
1162 # Make a reference GSObject with default folding_threshold.
1163 ref_obj = gs_interpreter.drawPointSource(gsobject, psf=psf)
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)
1176 self.assertEqual(ref_obj.getGoodImageSize(pixel_scale),
1177 test_bounds.xmax - test_bounds.xmin)
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)
1185 self.assertLess(ref_obj.getGoodImageSize(pixel_scale),
1186 test_bounds.xmax - test_bounds.xmin)
1189class GetGoodImageSizeTestCase(unittest.TestCase):
1190 """TestCase class for getGoodPhotImageSize function."""
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)
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))
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)
1217 Nmax = 4096 # maximum image size
1218 N = getGoodPhotImageSize(obj, 0, pixel_scale=pixel_scale)
1219 self.assertLessEqual(N, Nmax)
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')
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)
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)
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)
1262 self.assertAlmostEqual(math.sin(math.radians(321.62974517903774)),
1263 math.sin(math.radians(gs_interpreter.getHourAngle(mjd, ra))),
1264 places=4)
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)
1273class MemoryTestClass(lsst.utils.tests.MemoryTestCase):
1274 pass
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()