Coverage for tests/testPositionAngle.py : 33%

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 builtins import zip
2import numpy as np
3import os
4import unittest
5import tempfile
6import shutil
7import lsst.utils.tests
8from lsst.utils import getPackageDir
9import lsst.afw.image as afwImage
10from lsst.sims.utils.CodeUtilities import sims_clean_up
11from lsst.sims.utils import ObservationMetaData, radiansFromArcsec
12from lsst.sims.catalogs.db import fileDBObject
13from lsst.sims.GalSimInterface import GalSimGalaxies
14from lsst.sims.GalSimInterface import GalSimCameraWrapper
15from lsst.sims.coordUtils import _raDecFromPixelCoords
17#afrom lsst.sims.coordUtils.utils import ReturnCamera
19from testUtils import create_text_catalog
21ROOT = os.path.abspath(os.path.dirname(__file__))
24def setup_module(module):
25 lsst.utils.tests.init()
28class paFileDBObj(fileDBObject):
29 idColKey = 'test_id'
30 objectTypeId = 88
31 tableid = 'test'
32 raColName = 'ra'
33 decColName = 'dec'
34 # sedFilename
36 columns = [('raJ2000', 'ra*PI()/180.0', np.float),
37 ('decJ2000', 'dec*PI()/180.0', np.float),
38 ('halfLightRadius', 'hlr*PI()/648000.0', np.float),
39 ('magNorm', 'mag_norm', np.float),
40 ('positionAngle', 'pa*PI()/180.0', np.float)]
43class paCat(GalSimGalaxies):
44 bandpassNames = ['u']
45 default_columns = [('sedFilename', 'sed_flat.txt', (str, 12)),
46 ('magNorm', 21.0, float),
47 ('galacticAv', 0.1, float),
48 ('galacticRv', 3.1, float),
49 ('galSimType', 'sersic', (str, 11)),
50 ('internalAv', 0.1, float),
51 ('internalRv', 3.1, float),
52 ('redshift', 0.0, float),
53 ('majorAxis', radiansFromArcsec(1.0), float),
54 ('minorAxis', radiansFromArcsec(0.5), float),
55 ('sindex', 4.0, float),
56 ('npoints', 0, int),
57 ('gamma1', 0.0, float),
58 ('gamma2', 0.0, float),
59 ('kappa', 0.0, float),
60 ]
62@unittest.skip('ReturnCamera deprecated - need replacement')
63class GalSimPositionAngleTest(unittest.TestCase):
65 @classmethod
66 def tearDownClass(cls):
67 sims_clean_up()
69 def get_position_angle(self, imageName, afwCamera, afwDetector,
70 obs_metadata, epoch):
71 """
72 Read in a FITS image containing one extended object.
74 Determine its north and east axes by examining how RA and Dec change
75 with pixel position.
77 Determine the semi-major axis of the object by treating the distribution
78 of flux as a covariance matrix and finding its eigen vectors.
80 Return the angle between the semi-major axis and the north axis of
81 the image
83 @param [in] imageName is the name of the FITS image to be read
85 @param [in] afwCamera is an afw.cameraGeom.Camera
87 @param [in] afwDetector is an afw.cameraGeom.Detector
89 @param [in] obs_metadata is an ObservationMetaData describing the
90 pointing of the telescope
92 @param [in] epoch is the epoch in Julian years of the equinox against
93 which RA and Dec are measured
95 @param [out] the position angle of the object in the image in degrees
96 """
98 im = afwImage.ImageF(imageName).getArray()
99 activePixels = np.where(im > 1.0e-10)
100 self.assertGreater(len(activePixels), 0)
101 xPixList = activePixels[1]
102 yPixList = activePixels[0]
104 xCenterPix = np.array([im.shape[1]/2])
105 yCenterPix = np.array([im.shape[0]/2])
107 raCenter, decCenter = _raDecFromPixelCoords(xCenterPix, yCenterPix,
108 [afwDetector.getName()],
109 camera=afwCamera,
110 obs_metadata=obs_metadata,
111 epoch=epoch)
113 xCenterP1 = xCenterPix+1
114 yCenterP1 = yCenterPix+1
115 raCenterP1, decCenterP1 = _raDecFromPixelCoords(xCenterP1, yCenterP1,
116 [afwDetector.getName()],
117 camera=afwCamera,
118 obs_metadata=obs_metadata,
119 epoch=epoch)
121 # find the angle between the (1,1) vector in pixel space and the
122 # north axis of the image
123 theta = np.arctan2((raCenterP1[0]-raCenter[0]), decCenterP1[0]-decCenter[0])
125 # rotate the (1,1) vector in pixel space so that it is pointing
126 # along the north axis
127 north = np.array([np.cos(theta)-np.sin(theta), np.cos(theta)+np.sin(theta)])
128 north = north/np.sqrt(north[0]*north[0]+north[1]*north[1])
130 # find the east axis of the image
131 east = np.array([north[1], -1.0*north[0]])
133 # now find the covariance matrix of the x, y pixel space distribution
134 # of flux on the image
135 maxPixel = np.array([im.argmax()%im.shape[1], im.argmax()/im.shape[1]])
137 xx = np.array([im[iy][ix]*np.power(ix-maxPixel[0], 2)
138 for ix, iy in zip(xPixList, yPixList)]).sum()
140 xy = np.array([im[iy][ix]*(ix-maxPixel[0])*(iy-maxPixel[1])
141 for ix, iy in zip(xPixList, yPixList)]).sum()
143 yy = np.array([im[iy][ix]*(iy-maxPixel[1])*(iy-maxPixel[1])
144 for ix, iy in zip(xPixList, yPixList)]).sum()
146 covar = np.array([[xx, xy], [xy, yy]])
148 # find the eigen vectors of this covarinace matrix;
149 # treat the one with the largest eigen value as the
150 # semi-major axis of the object
151 eigenVals, eigenVecs = np.linalg.eig(covar)
153 iMax = eigenVals.argmax()
154 majorAxis = eigenVecs[:, iMax]
156 majorAxis = majorAxis/np.sqrt(majorAxis[0]*majorAxis[0]+majorAxis[1]*majorAxis[1])
158 # return the angle between the north axis of the image
159 # and the semi-major axis of the object
160 cosTheta = np.dot(majorAxis, north)
161 sinTheta = np.dot(majorAxis, east)
163 theta = np.arctan2(sinTheta, cosTheta)
165 return np.degrees(theta)
167 def testPositionAngle(self):
168 """
169 Test that GalSim is generating images with the correct position angle
170 by creating a FITS image with one extended source in it. Measure
171 the angle between the semi-major axis of the source and the north
172 axis of the image. Throw an exception if that angle differs
173 from the expected position angle by more than 2 degrees.
174 """
175 scratchDir = tempfile.mkdtemp(dir=ROOT, prefix='testPositionAngle-')
176 catName = os.path.join(scratchDir, 'pa_test_Catalog.dat')
177 imageRoot = os.path.join(scratchDir, 'pa_test_Image')
178 dbFileName = os.path.join(scratchDir, 'pa_test_InputCatalog.dat')
180 baseDir = os.path.join(getPackageDir('sims_GalSimInterface'), 'tests', 'cameraData')
181 camera = ReturnCamera(baseDir)
182 detector = camera[0]
183 detName = detector.getName()
185 rng = np.random.RandomState(42)
186 paList = rng.random_sample(7)*360.0
187 rotSkyPosList = rng.random_sample(77)*360.0
189 for pa, rotSkyPos in zip(paList, rotSkyPosList):
191 imageName = '%s_%s_u.fits' % (imageRoot, detName)
193 obs = ObservationMetaData(pointingRA = 75.0,
194 pointingDec = -12.0,
195 boundType = 'circle',
196 boundLength = 4.0,
197 rotSkyPos = rotSkyPos,
198 mjd = 49250.0)
200 create_text_catalog(obs, dbFileName,
201 rng.random_sample(1)*20.0-10.0,
202 rng.random_sample(1)*20.0-10.0,
203 pa=[pa],
204 mag_norm=[17.0])
206 db = paFileDBObj(dbFileName, runtable='test')
208 cat = paCat(db, obs_metadata=obs)
209 cat.camera_wrapper = GalSimCameraWrapper(camera)
211 cat.write_catalog(catName)
212 cat.write_images(nameRoot=imageRoot)
214 paTest = self.get_position_angle(imageName, camera, detector, obs, 2000.0)
216 # need to compare against all angles displaced by either 180 or 360 degrees
217 # from expected answer
218 deviation = np.abs(np.array([pa-paTest,
219 pa-180.0-paTest,
220 pa+180.0-paTest,
221 pa-360.0-paTest,
222 pa+360.0-paTest])).min()
224 self.assertLess(deviation, 3.0)
226 if os.path.exists(catName):
227 os.unlink(catName)
228 if os.path.exists(dbFileName):
229 os.unlink(dbFileName)
230 if os.path.exists(imageName):
231 os.unlink(imageName)
233 if os.path.exists(scratchDir):
234 shutil.rmtree(scratchDir)
237class MemoryTestClass(lsst.utils.tests.MemoryTestCase):
238 pass
240if __name__ == "__main__": 240 ↛ 241line 240 didn't jump to line 241, because the condition on line 240 was never true
241 lsst.utils.tests.init()
242 unittest.main()