Coverage for tests/testStackers.py : 12%

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 print_function
2from builtins import str
3from builtins import zip
4import numpy as np
5import matplotlib
6import warnings
7import unittest
8import lsst.utils.tests
9import lsst.sims.maf.stackers as stackers
10from lsst.sims.utils import _galacticFromEquatorial, calcLmstLast, Site, _altAzPaFromRaDec, \
11 ObservationMetaData
12from lsst.sims.survey.fields import FieldsDatabase
14matplotlib.use("Agg")
17class TestStackerClasses(unittest.TestCase):
19 def testAddCols(self):
20 """Test that we can add columns as expected.
21 """
22 data = np.zeros(90, dtype=list(zip(['alt'], [float])))
23 data['alt'] = np.arange(0, 90)
24 stacker = stackers.ZenithDistStacker(altCol='alt', degrees=True)
25 newcol = stacker.colsAdded[0]
26 # First - are the columns added if they are not there.
27 data, cols_present = stacker._addStackerCols(data)
28 self.assertEqual(cols_present, False)
29 self.assertIn(newcol, data.dtype.names)
30 # Next - if they are present, is that information passed back?
31 with warnings.catch_warnings():
32 warnings.simplefilter('ignore')
33 data, cols_present = stacker._addStackerCols(data)
34 self.assertEqual(cols_present, True)
36 def testEQ(self):
37 """
38 Test that stackers can be compared
39 """
40 s1 = stackers.ParallaxFactorStacker()
41 s2 = stackers.ParallaxFactorStacker()
42 assert(s1 == s2)
44 s1 = stackers.RandomDitherFieldPerVisitStacker()
45 s2 = stackers.RandomDitherFieldPerVisitStacker()
46 assert(s1 == s2)
48 # Test if they have numpy array atributes
49 s1.ack = np.arange(10)
50 s2.ack = np.arange(10)
51 assert(s1 == s2)
53 # Change the array and test
54 s1.ack += 1
55 assert(s1 != s2)
57 s2 = stackers.RandomDitherFieldPerVisitStacker(decCol='blah')
58 assert(s1 != s2)
60 def testNormAirmass(self):
61 """
62 Test the normalized airmass stacker.
63 """
64 rng = np.random.RandomState(232)
65 data = np.zeros(600, dtype=list(zip(
66 ['airmass', 'fieldDec'], [float, float])))
67 data['airmass'] = rng.random_sample(600)
68 data['fieldDec'] = rng.random_sample(600) * np.pi - np.pi / 2.
69 data['fieldDec'] = np.degrees(data['fieldDec'])
70 stacker = stackers.NormAirmassStacker(degrees=True)
71 data = stacker.run(data)
72 for i in np.arange(data.size):
73 self.assertLessEqual(data['normairmass'][i], data['airmass'][i])
74 self.assertLess(np.min(data['normairmass'] - data['airmass']), 0)
76 def testParallaxFactor(self):
77 """
78 Test the parallax factor.
79 """
81 data = np.zeros(600, dtype=list(zip(['fieldRA', 'fieldDec', 'observationStartMJD'],
82 [float, float, float])))
83 data['fieldRA'] = data['fieldRA'] + .1
84 data['fieldDec'] = data['fieldDec'] - .1
85 data['observationStartMJD'] = np.arange(data.size) + 49000.
86 stacker = stackers.ParallaxFactorStacker(degrees=True)
87 data = stacker.run(data)
88 self.assertLess(max(np.abs(data['ra_pi_amp'])), 1.1)
89 self.assertLess(max(np.abs(data['dec_pi_amp'])), 1.1)
90 self.assertLess(
91 np.max(data['ra_pi_amp']**2 + data['dec_pi_amp']**2), 1.1)
92 self.assertGreater(min(np.abs(data['ra_pi_amp'])), 0.)
93 self.assertGreater(min(np.abs(data['dec_pi_amp'])), 0.)
95 def _tDitherRange(self, diffsra, diffsdec, ra, dec, maxDither):
96 self.assertLessEqual(np.abs(diffsra).max(), maxDither)
97 self.assertLessEqual(np.abs(diffsdec).max(), maxDither)
98 offsets = np.sqrt(diffsra**2 + diffsdec**2)
99 self.assertLessEqual(offsets.max(), maxDither)
100 self.assertGreater(diffsra.max(), 0)
101 self.assertGreater(diffsdec.max(), 0)
102 self.assertLess(diffsra.min(), 0)
103 self.assertLess(diffsdec.min(), 0)
105 def _tDitherPerNight(self, diffsra, diffsdec, ra, dec, nights):
106 n = np.unique(nights)
107 for ni in n:
108 match = np.where(nights == ni)[0]
109 dra_on_night = np.abs(np.diff(diffsra[match]))
110 ddec_on_night = np.abs(np.diff(diffsdec[match]))
111 if dra_on_night.max() > 0.0005:
112 print(ni)
113 m = np.where(dra_on_night > 0.0005)[0]
114 print(diffsra[match][m])
115 print(ra[match][m])
116 print(dec[match][m])
117 print(dra_on_night[m])
118 self.assertAlmostEqual(dra_on_night.max(), 0)
119 self.assertAlmostEqual(ddec_on_night.max(), 0)
121 def testSetupDitherStackers(self):
122 # Test that we get no stacker when using default columns.
123 raCol = 'fieldRA'
124 decCol = 'fieldDec'
125 degrees = True
126 stackerlist = stackers.setupDitherStackers(raCol, decCol, degrees)
127 self.assertEqual(len(stackerlist), 0)
128 # Test that we get one (and the right one) when using particular columns.
129 raCol = 'hexDitherFieldPerNightRa'
130 decCol = 'hexDitherFieldPerNightDec'
131 stackerlist = stackers.setupDitherStackers(raCol, decCol, degrees)
132 self.assertEqual(len(stackerlist), 1)
133 self.assertEqual(stackerlist[0], stackers.HexDitherFieldPerNightStacker())
134 # Test that kwargs are passed along.
135 stackerlist = stackers.setupDitherStackers(raCol, decCol, degrees, maxDither=0.5)
136 self.assertEqual(stackerlist[0].maxDither, np.radians(0.5))
138 def testBaseDitherStacker(self):
139 # Test that the base dither stacker matches the type of a stacker.
140 s = stackers.HexDitherFieldPerNightStacker()
141 self.assertTrue(isinstance(s, stackers.BaseDitherStacker))
142 s = stackers.ParallaxFactorStacker()
143 self.assertFalse(isinstance(s, stackers.BaseDitherStacker))
145 def testRandomDither(self):
146 """
147 Test the random dither pattern.
148 """
149 maxDither = .5
150 data = np.zeros(600, dtype=list(zip(
151 ['fieldRA', 'fieldDec'], [float, float])))
152 # Set seed so the test is stable
153 rng = np.random.RandomState(42)
154 # Restrict dithers to area where wraparound is not a problem for
155 # comparisons.
156 data['fieldRA'] = np.degrees(rng.random_sample(600) * (np.pi) + np.pi / 2.0)
157 data['fieldDec'] = np.degrees(rng.random_sample(600) * np.pi / 2.0 - np.pi / 4.0)
158 stacker = stackers.RandomDitherFieldPerVisitStacker(maxDither=maxDither)
159 data = stacker.run(data)
160 diffsra = (data['fieldRA'] - data['randomDitherFieldPerVisitRa']) \
161 * np.cos(np.radians(data['fieldDec']))
162 diffsdec = data['fieldDec'] - data['randomDitherFieldPerVisitDec']
163 # Check dithers within expected range.
164 self._tDitherRange(diffsra, diffsdec,
165 data['fieldRA'], data['fieldDec'], maxDither)
167 def testRandomDitherPerNight(self):
168 """
169 Test the per-night random dither pattern.
170 """
171 maxDither = 0.5
172 ndata = 600
173 # Set seed so the test is stable
174 rng = np.random.RandomState(42)
176 data = np.zeros(ndata, dtype=list(zip(['fieldRA', 'fieldDec', 'fieldId', 'night'],
177 [float, float, int, int])))
178 data['fieldRA'] = rng.rand(ndata) * (np.pi) + np.pi / 2.0
179 data['fieldDec'] = rng.rand(ndata) * np.pi / 2.0 - np.pi / 4.0
180 data['fieldId'] = np.floor(rng.rand(ndata) * ndata)
181 data['night'] = np.floor(rng.rand(ndata) * 10).astype('int')
182 stacker = stackers.RandomDitherPerNightStacker(maxDither=maxDither)
183 data = stacker.run(data)
184 diffsra = (np.radians(data['fieldRA']) - np.radians(data['randomDitherPerNightRa'])) \
185 * np.cos(np.radians(data['fieldDec']))
186 diffsdec = np.radians(data['fieldDec']) - np.radians(data['randomDitherPerNightDec'])
187 self._tDitherRange(diffsra, diffsdec,
188 data['fieldRA'], data['fieldDec'], maxDither)
189 # Check that dithers on the same night are the same.
190 self._tDitherPerNight(diffsra, diffsdec, data['fieldRA'],
191 data['fieldDec'], data['night'])
193 def testSpiralDitherPerNight(self):
194 """
195 Test the per-night spiral dither pattern.
196 """
197 maxDither = 0.5
198 ndata = 2000
199 # Set seed so the test is stable
200 rng = np.random.RandomState(42)
202 data = np.zeros(ndata, dtype=list(zip(['fieldRA', 'fieldDec', 'fieldId', 'night'],
203 [float, float, int, int])))
204 data['fieldRA'] = rng.rand(ndata) * (np.pi) + np.pi / 2.0
205 data['fieldRA'] = np.zeros(ndata) + np.pi / 2.0
206 data['fieldDec'] = rng.rand(ndata) * np.pi / 2.0 - np.pi / 4.0
207 data['fieldDec'] = np.zeros(ndata)
208 data['fieldId'] = np.floor(rng.rand(ndata) * ndata)
209 data['night'] = np.floor(rng.rand(ndata) * 20).astype('int')
210 stacker = stackers.SpiralDitherPerNightStacker(maxDither=maxDither)
211 data = stacker.run(data)
212 diffsra = (data['fieldRA'] - data['spiralDitherPerNightRa']) \
213 * np.cos(np.radians(data['fieldDec']))
214 diffsdec = data['fieldDec'] - data['spiralDitherPerNightDec']
215 self._tDitherRange(diffsra, diffsdec,
216 data['fieldRA'], data['fieldDec'], maxDither)
217 # Check that dithers on the same night are the same.
218 self._tDitherPerNight(diffsra, diffsdec, data['fieldRA'], data[
219 'fieldDec'], data['night'])
221 def testHexDitherPerNight(self):
222 """
223 Test the per-night hex dither pattern.
224 """
225 maxDither = 0.5
226 ndata = 2000
227 # Set seed so the test is stable
228 rng = np.random.RandomState(42)
230 data = np.zeros(ndata, dtype=list(zip(['fieldRA', 'fieldDec', 'fieldId', 'night'],
231 [float, float, int, int])))
232 data['fieldRA'] = rng.rand(ndata) * (np.pi) + np.pi / 2.0
233 data['fieldDec'] = rng.rand(ndata) * np.pi / 2.0 - np.pi / 4.0
234 data['fieldId'] = np.floor(rng.rand(ndata) * ndata)
235 data['night'] = np.floor(rng.rand(ndata) * 217).astype('int')
236 stacker = stackers.HexDitherPerNightStacker(maxDither=maxDither)
237 data = stacker.run(data)
238 diffsra = (data['fieldRA'] - data['hexDitherPerNightRa']) \
239 * np.cos(np.radians(data['fieldDec']))
240 diffsdec = data['fieldDec'] - data['hexDitherPerNightDec']
241 self._tDitherRange(diffsra, diffsdec,
242 data['fieldRA'], data['fieldDec'], maxDither)
243 # Check that dithers on the same night are the same.
244 self._tDitherPerNight(diffsra, diffsdec, data['fieldRA'],
245 data['fieldDec'], data['night'])
247 def testRandomRotDitherPerFilterChangeStacker(self):
248 """
249 Test the rotational dither stacker.
250 """
251 maxDither = 90
252 filt = np.array(['r', 'r', 'r', 'g', 'g', 'g', 'r', 'r'])
253 rotTelPos = np.array([0, 0, 0, 0, 0, 0, 0, 0], float)
254 # Test that have a dither in rot offset for every filter change.
255 odata = np.zeros(len(filt), dtype=list(zip(['filter', 'rotTelPos'], [(np.str_, 1), float])))
256 odata['filter'] = filt
257 odata['rotTelPos'] = rotTelPos
258 stacker = stackers.RandomRotDitherPerFilterChangeStacker(maxDither=maxDither, degrees=True,
259 randomSeed=99)
260 data = stacker.run(odata) # run the stacker
261 randomDithers = data['randomDitherPerFilterChangeRotTelPos']
262 # Check that first three visits have the same rotTelPos, etc.
263 rotOffsets = rotTelPos - randomDithers
264 self.assertEqual(rotOffsets[0], 0) # no dither w/o a filter change
265 offsetChanges = np.where(rotOffsets[1:] != rotOffsets[:-1])[0]
266 filtChanges = np.where(filt[1:] != filt[:-1])[0]
267 np.testing.assert_array_equal(offsetChanges, filtChanges) # dither after every filter
269 # now test to ensure that user-defined maxRotAngle value works and that
270 # visits in between filter changes for which no offset can be found are left undithered
271 # (g band visits span rotator range, so can't be dithered)
272 gvisits = np.where(filt == 'g')
273 maxrot = 30
274 rotTelPos[gvisits[0][0]] = -maxrot
275 rotTelPos[gvisits[0][-1]] = maxrot
276 odata['rotTelPos'] = rotTelPos
277 stacker = stackers.RandomRotDitherPerFilterChangeStacker(maxDither=maxDither,
278 degrees=True,
279 minRotAngle=-maxrot, maxRotAngle=maxrot,
280 randomSeed=19231)
281 data = stacker.run(odata)
282 randomDithers = data['randomDitherPerFilterChangeRotTelPos']
283 # Check that we respected the range.
284 self.assertEqual(randomDithers.max(), 30)
285 self.assertEqual(randomDithers.min(), -30)
286 # Check that g band visits weren't dithered.
287 rotOffsets = rotTelPos - randomDithers
288 self.assertEqual(rotOffsets[gvisits].all(), 0)
290 def testHAStacker(self):
291 """Test the Hour Angle stacker"""
292 data = np.zeros(100, dtype=list(zip(['observationStartLST', 'fieldRA'], [float, float])))
293 data['observationStartLST'] = np.arange(100) / 99. * np.pi * 2
294 stacker = stackers.HourAngleStacker(degrees=True)
295 data = stacker.run(data)
296 # Check that data is always wrapped
297 self.assertLess(np.max(data['HA']), 12.)
298 self.assertGreater(np.min(data['HA']), -12.)
299 # Check that HA is zero if lst == RA
300 data = np.zeros(1, dtype=list(zip(['observationStartLST', 'fieldRA'], [float, float])))
301 data = stacker.run(data)
302 self.assertEqual(data['HA'], 0.)
303 data = np.zeros(1, dtype=list(zip(['observationStartLST', 'fieldRA'], [float, float])))
304 data['observationStartLST'] = 20.
305 data['fieldRA'] = 20.
306 data = stacker.run(data)
307 self.assertEqual(data['HA'], 0.)
308 # Check a value
309 data = np.zeros(1, dtype=list(zip(['observationStartLST', 'fieldRA'], [float, float])))
310 data['observationStartLST'] = 0.
311 data['fieldRA'] = np.degrees(np.pi / 2.)
312 data = stacker.run(data)
313 np.testing.assert_almost_equal(data['HA'], -6.)
315 def testPAStacker(self):
316 """ Test the parallacticAngleStacker"""
317 data = np.zeros(100, dtype=list(zip(
318 ['observationStartMJD', 'fieldDec', 'fieldRA', 'observationStartLST'], [float] * 4)))
319 data['observationStartMJD'] = np.arange(100) * .2 + 50000
320 site = Site(name='LSST')
321 data['observationStartLST'], last = calcLmstLast(data['observationStartMJD'], site.longitude_rad)
322 data['observationStartLST'] = data['observationStartLST']*180./12.
323 stacker = stackers.ParallacticAngleStacker(degrees=True)
324 data = stacker.run(data)
325 # Check values are in good range
326 assert(data['PA'].max() <= 180)
327 assert(data['PA'].min() >= -180)
329 # Check compared to the util
330 check_pa = []
331 ras = np.radians(data['fieldRA'])
332 decs = np.radians(data['fieldDec'])
333 for ra, dec, mjd in zip(ras, decs, data['observationStartMJD']):
334 alt, az, pa = _altAzPaFromRaDec(ra, dec,
335 ObservationMetaData(mjd=mjd, site=site))
337 check_pa.append(pa)
338 check_pa = np.degrees(check_pa)
339 np.testing.assert_array_almost_equal(data['PA'], check_pa, decimal=0)
341 def testFilterColorStacker(self):
342 """Test the filter color stacker."""
343 data = np.zeros(60, dtype=list(zip(['filter'], ['<U1'])))
344 data['filter'][0:10] = 'u'
345 data['filter'][10:20] = 'g'
346 data['filter'][20:30] = 'r'
347 data['filter'][30:40] = 'i'
348 data['filter'][40:50] = 'z'
349 data['filter'][50:60] = 'y'
350 stacker = stackers.FilterColorStacker()
351 data = stacker.run(data)
352 # Check if re-run stacker raises warning (adding column twice).
353 with warnings.catch_warnings(record=True) as w:
354 warnings.simplefilter("always")
355 data = stacker.run(data)
356 assert len(w) > 1
357 assert "already present in simData" in str(w[-1].message)
358 # Check if use non-recognized filter raises exception.
359 data = np.zeros(600, dtype=list(zip(['filter'], ['<U1'])))
360 data['filter'] = 'q'
361 self.assertRaises(IndexError, stacker.run, data)
363 def testGalacticStacker(self):
364 """
365 Test the galactic coordinate stacker
366 """
367 ra, dec = np.degrees(np.meshgrid(np.arange(0, 2. * np.pi, 0.1),
368 np.arange(-np.pi, np.pi, 0.1)))
369 ra = np.ravel(ra)
370 dec = np.ravel(dec)
371 data = np.zeros(ra.size, dtype=list(zip(['ra', 'dec'], [float] * 2)))
372 data['ra'] += ra
373 data['dec'] += dec
374 s = stackers.GalacticStacker(raCol='ra', decCol='dec')
375 newData = s.run(data)
376 expectedL, expectedB = _galacticFromEquatorial(np.radians(ra), np.radians(dec))
377 np.testing.assert_array_equal(newData['gall'], expectedL)
378 np.testing.assert_array_equal(newData['galb'], expectedB)
380 # Check that we have all the quadrants populated
381 q1 = np.where((newData['gall'] < np.pi) & (newData['galb'] < 0.))[0]
382 q2 = np.where((newData['gall'] < np.pi) & (newData['galb'] > 0.))[0]
383 q3 = np.where((newData['gall'] > np.pi) & (newData['galb'] < 0.))[0]
384 q4 = np.where((newData['gall'] > np.pi) & (newData['galb'] > 0.))[0]
385 assert(q1.size > 0)
386 assert(q2.size > 0)
387 assert(q3.size > 0)
388 assert(q4.size > 0)
390 def testOpSimFieldStacker(self):
391 """
392 Test the OpSimFieldStacker
393 """
394 rng = np.random.RandomState(812351)
395 s = stackers.OpSimFieldStacker(raCol='ra', decCol='dec', degrees=False)
397 # First sanity check. Make sure the center of the fields returns the right field id
398 opsim_fields_db = FieldsDatabase()
400 # Returned RA/Dec coordinates in degrees
401 field_id, ra, dec = opsim_fields_db.get_id_ra_dec_arrays("select * from Field;")
403 data = np.array(list(zip(np.radians(ra),
404 np.radians(dec))),
405 dtype=list(zip(['ra', 'dec'], [float, float])))
406 new_data = s.run(data)
408 np.testing.assert_array_equal(field_id, new_data['opsimFieldId'])
410 # Cherry picked a set of coordinates that should belong to a certain list of fields.
411 # These coordinates are not exactly at the center of fields, but close enough that
412 # they should be classified as belonging to them.
413 ra_inside_2548 = (10. + 1. / 60 + 6.59 / 60. / 60.) * np.pi / 12. # 10:01:06.59
414 dec_inside_2548 = np.radians(-1. * (2. + 8. / 60. + 27.6 / 60. / 60.)) # -02:08:27.6
416 ra_inside_8 = (8. + 49. / 60 + 19.83 / 60. / 60.) * np.pi / 12. # 08:49:19.83
417 dec_inside_8 = np.radians(-1. * (85. + 19. / 60. + 04.7 / 60. / 60.)) # -85:19:04.7
419 ra_inside_1253 = (9. + 16. / 60 + 13.67 / 60. / 60.) * np.pi / 12. # 09:16:13.67
420 dec_inside_1253 = np.radians(-1. * (30. + 23. / 60. + 41.4 / 60. / 60.)) # -30:23:41.4
422 data = np.zeros(3, dtype=list(zip(['ra', 'dec'], [float, float])))
423 field_id = np.array([2548, 8, 1253], dtype=int)
424 data['ra'] = np.array([ra_inside_2548, ra_inside_8, ra_inside_1253])
425 data['dec'] = np.array([dec_inside_2548, dec_inside_8, dec_inside_1253])
427 new_data = s.run(data)
429 np.testing.assert_array_equal(field_id, new_data['opsimFieldId'])
431 # Now let's generate a set of random coordinates and make sure they are all assigned a fieldID.
432 data = np.array(list(zip(rng.rand(600) * 2. * np.pi,
433 rng.rand(600) * np.pi - np.pi / 2.)),
434 dtype=list(zip(['ra', 'dec'], [float, float])))
436 new_data = s.run(data)
438 self.assertGreater(new_data['opsimFieldId'].max(), 0)
441class TestMemory(lsst.utils.tests.MemoryTestCase):
442 pass
445def setup_module(module):
446 lsst.utils.tests.init()
449if __name__ == "__main__": 449 ↛ 450line 449 didn't jump to line 450, because the condition on line 449 was never true
450 lsst.utils.tests.init()
451 unittest.main()