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

13 

14matplotlib.use("Agg") 

15 

16 

17class TestStackerClasses(unittest.TestCase): 

18 

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) 

35 

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) 

43 

44 s1 = stackers.RandomDitherFieldPerVisitStacker() 

45 s2 = stackers.RandomDitherFieldPerVisitStacker() 

46 assert(s1 == s2) 

47 

48 # Test if they have numpy array atributes 

49 s1.ack = np.arange(10) 

50 s2.ack = np.arange(10) 

51 assert(s1 == s2) 

52 

53 # Change the array and test 

54 s1.ack += 1 

55 assert(s1 != s2) 

56 

57 s2 = stackers.RandomDitherFieldPerVisitStacker(decCol='blah') 

58 assert(s1 != s2) 

59 

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) 

75 

76 def testParallaxFactor(self): 

77 """ 

78 Test the parallax factor. 

79 """ 

80 

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

94 

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) 

104 

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) 

120 

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

137 

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

144 

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) 

166 

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) 

175 

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']) 

192 

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) 

201 

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']) 

220 

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) 

229 

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']) 

246 

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 

268 

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) 

289 

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

314 

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) 

328 

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

336 

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) 

340 

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) 

362 

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) 

379 

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) 

389 

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) 

396 

397 # First sanity check. Make sure the center of the fields returns the right field id 

398 opsim_fields_db = FieldsDatabase() 

399 

400 # Returned RA/Dec coordinates in degrees 

401 field_id, ra, dec = opsim_fields_db.get_id_ra_dec_arrays("select * from Field;") 

402 

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) 

407 

408 np.testing.assert_array_equal(field_id, new_data['opsimFieldId']) 

409 

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 

415 

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 

418 

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 

421 

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

426 

427 new_data = s.run(data) 

428 

429 np.testing.assert_array_equal(field_id, new_data['opsimFieldId']) 

430 

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

435 

436 new_data = s.run(data) 

437 

438 self.assertGreater(new_data['opsimFieldId'].max(), 0) 

439 

440 

441class TestMemory(lsst.utils.tests.MemoryTestCase): 

442 pass 

443 

444 

445def setup_module(module): 

446 lsst.utils.tests.init() 

447 

448 

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