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

1""" 

2Some of the data in this unit test will appear abitrary. That is 

3because, in addition to testing the execution of all of the functionality 

4provided in the sims_coordUtils package, this unit test validates 

5the outputs of PALPY against the outputs of pySLALIB v 1.0.2 

6(it was written when we were making the transition from pySLALIB to PALPY). 

7 

8There will be some difference, as the two libraries are based on slightly 

9different conventions (for example, the prenut routine which calculates 

10the matrix of precession and nutation is based on the IAU 2006/2000A 

11standard in PALPY and on SF2001 in pySLALIB; however, the two outputs 

12still agree to within one part in 10^5) 

13 

14""" 

15 

16from __future__ import with_statement 

17from __future__ import division 

18from builtins import zip 

19from builtins import range 

20 

21import numpy as np 

22 

23import unittest 

24import palpy as pal 

25import lsst.utils.tests 

26 

27from lsst.sims.utils import ObservationMetaData 

28from lsst.sims.utils import _getRotTelPos, _raDecFromAltAz, \ 

29 radiansFromArcsec, arcsecFromRadians, Site, \ 

30 raDecFromAltAz, haversine, ModifiedJulianDate, \ 

31 _getRotSkyPos, _angularSeparation 

32 

33from lsst.sims.utils import solarRaDec, _solarRaDec, distanceToSun, _distanceToSun 

34from lsst.sims.utils import _applyPrecession, _applyProperMotion 

35from lsst.sims.utils import _appGeoFromICRS, _observedFromAppGeo 

36from lsst.sims.utils import _observedFromICRS, _icrsFromObserved 

37from lsst.sims.utils import _appGeoFromObserved, _icrsFromAppGeo 

38from lsst.sims.utils import refractionCoefficients, applyRefraction 

39from lsst.sims.utils import observedFromICRS, applyProperMotion, sphericalFromCartesian 

40 

41# Tell astropy not to download the IERS tables 

42from astropy.utils import iers 

43iers.conf.auto_download=False 

44 

45def setup_module(module): 

46 lsst.utils.tests.init() 

47 

48 

49def makeObservationMetaData(): 

50 # create a cartoon ObservationMetaData object 

51 mjd = 52000.0 

52 alt = np.pi / 2.0 

53 az = 0.0 

54 testSite = Site(latitude=np.degrees(0.5), longitude=np.degrees(1.1), height=3000, 

55 temperature=260.0, pressure=725.0, lapseRate=0.005, humidity=0.4) 

56 obsTemp = ObservationMetaData(site=testSite, mjd=mjd) 

57 centerRA, centerDec = _raDecFromAltAz(alt, az, obsTemp) 

58 rotTel = _getRotTelPos(centerRA, centerDec, obsTemp, 0.0) 

59 rotSky = _getRotSkyPos(centerRA, centerDec, obsTemp, rotTel) 

60 

61 radius = 0.1 

62 

63 obs_metadata = ObservationMetaData(pointingRA=np.degrees(centerRA), 

64 pointingDec=np.degrees(centerDec), 

65 rotSkyPos=np.degrees(rotSky), 

66 mjd=mjd, 

67 boundType='circle', boundLength=2.0 * radius, 

68 site=testSite) 

69 

70 return obs_metadata 

71 

72 

73def makeRandomSample(raCenter=None, decCenter=None, radius=None): 

74 # create a random sample of object data 

75 

76 nsamples = 100 

77 rng = np.random.RandomState(32) 

78 

79 if raCenter is None or decCenter is None or radius is None: 

80 ra = rng.random_sample(nsamples) * 2.0 * np.pi 

81 dec = (rng.random_sample(nsamples) - 0.5) * np.pi 

82 else: 

83 rr = rng.random_sample(nsamples) * radius 

84 theta = rng.random_sample(nsamples) * 2.0 * np.pi 

85 ra = raCenter + rr * np.cos(theta) 

86 dec = decCenter + rr * np.cos(theta) 

87 

88 pm_ra = (rng.random_sample(nsamples) - 0.5) * 0.1 

89 pm_dec = (rng.random_sample(nsamples) - 0.5) * 0.1 

90 parallax = rng.random_sample(nsamples) * 0.01 

91 v_rad = rng.random_sample(nsamples) * 1000.0 

92 

93 return ra, dec, pm_ra, pm_dec, parallax, v_rad 

94 

95 

96class astrometryUnitTest(unittest.TestCase): 

97 """ 

98 The bulk of this unit test involves inputting a set list of input values 

99 and comparing the astrometric results to results derived from SLALIB run 

100 with the same input values. We have to create a test catalog artificially (rather than 

101 querying the database) because SLALIB was originally run on values that did not correspond 

102 to any particular Opsim run. 

103 """ 

104 

105 def setUp(self): 

106 self.metadata = {} 

107 

108 # these were the LSST site parameters as coded when this unit test was 

109 # written 

110 self.test_site = Site(longitude=np.degrees(-1.2320792), 

111 latitude=np.degrees(-0.517781017), 

112 height=2650.0, 

113 temperature=11.505, 

114 pressure=749.3, 

115 lapseRate=0.0065, 

116 humidity=0.4) 

117 

118 # Inputs are consistent with the baseline SLALIB run 

119 # used to create this unit test 

120 self.obs_metadata = ObservationMetaData(pointingRA=200.0, 

121 pointingDec=-30.0, 

122 rotSkyPos=1.0, 

123 mjd=50984.371741, 

124 boundType='circle', 

125 boundLength=0.05, 

126 site=self.test_site) 

127 

128 self.tol = 1.0e-5 

129 

130 def tearDown(self): 

131 del self.obs_metadata 

132 del self.metadata 

133 del self.tol 

134 

135 def testDistanceToSun(self): 

136 """ 

137 Test _distanceToSun using solar RA, Dec calculated from 

138 

139 http://aa.usno.navy.mil/data/docs/JulianDate.php 

140 http://aa.usno.navy.mil/data/docs/geocentric.php 

141 """ 

142 

143 hour = np.radians(360.0 / 24.0) 

144 minute = hour / 60.0 

145 second = minute / 60.0 

146 

147 mjd_list = [57026.0, 57543.625] 

148 

149 sun_ra_list = [18.0 * hour + 56.0 * minute + 51.022 * second, 

150 4.0 * hour + 51.0 * minute + 22.776 * second] 

151 

152 sun_dec_list = [np.radians(-22.0 - 47.0 / 60.0 - 40.27 / 3600.0), 

153 np.radians(22.0 + 30.0 / 60.0 + 0.73 / 3600.0)] 

154 

155 for raS, decS, tai in zip(sun_ra_list, sun_dec_list, mjd_list): 

156 

157 mjd = ModifiedJulianDate(TAI=tai) 

158 

159 # first, verify that the Sun is where we think it is to within 5 

160 # arc seconds 

161 self.assertLess(arcsecFromRadians( 

162 _distanceToSun(raS, decS, mjd)), 5.0) 

163 

164 # find Sun's Cartesian coordinates 

165 sun_x = np.cos(decS) * np.cos(raS) 

166 sun_y = np.cos(decS) * np.sin(raS) 

167 sun_z = np.sin(decS) 

168 

169 # now choose positions that are a set distance away from the Sun, and make sure 

170 # that _distanceToSun returns the expected result 

171 for theta in (np.pi / 2.0, np.pi / 4.0, -np.pi / 3.0): 

172 

173 # displace by rotating about z axis 

174 new_x = sun_x * np.cos(theta) + sun_y * np.sin(theta) 

175 new_y = -sun_x * np.sin(theta) + sun_y * np.cos(theta) 

176 new_z = sun_z 

177 

178 new_ra = np.arctan2(new_y, new_x) 

179 new_dec = np.arctan2(new_z, np.sqrt( 

180 new_x * new_x + new_y * new_y)) 

181 

182 dd = _distanceToSun(new_ra, new_dec, mjd) 

183 hh = haversine(raS, decS, new_ra, new_dec) 

184 self.assertLess(np.abs(arcsecFromRadians(dd - hh)), 5.0) 

185 

186 # displace by rotating about y axis 

187 new_x = sun_x * np.cos(theta) + sun_z * np.sin(theta) 

188 new_y = sun_y 

189 new_z = -sun_x * np.sin(theta) + sun_z * np.cos(theta) 

190 

191 new_ra = np.arctan2(new_y, new_x) 

192 new_dec = np.arctan2(new_z, np.sqrt( 

193 new_x * new_x + new_y * new_y)) 

194 dd = _distanceToSun(new_ra, new_dec, mjd) 

195 hh = haversine(raS, decS, new_ra, new_dec) 

196 self.assertLess(np.abs(arcsecFromRadians(dd - hh)), 5.0) 

197 

198 # displace by rotating about x axis 

199 new_x = sun_x 

200 new_y = sun_y * np.cos(theta) + sun_z * np.sin(theta) 

201 new_z = -sun_y * np.sin(theta) + sun_z * np.cos(theta) 

202 

203 new_ra = np.arctan2(new_y, new_x) 

204 new_dec = np.arctan2(new_z, np.sqrt( 

205 new_x * new_x + new_y * new_y)) 

206 dd = _distanceToSun(new_ra, new_dec, mjd) 

207 hh = haversine(raS, decS, new_ra, new_dec) 

208 self.assertLess(np.abs(arcsecFromRadians(dd - hh)), 5.0) 

209 

210 # Test passing in numpy arrays of RA, Dec 

211 rng = np.random.RandomState(87) 

212 nSamples = 100 

213 ra = rng.random_sample(nSamples) * 2.0 * np.pi 

214 dec = (rng.random_sample(nSamples) - 0.5) * np.pi 

215 mjd = ModifiedJulianDate(TAI=59580.0) 

216 control_distance = _distanceToSun(ra, dec, mjd) 

217 self.assertIsInstance(control_distance, np.ndarray) 

218 for ix, (rr, dd) in enumerate(zip(ra, dec)): 

219 dd = _distanceToSun(rr, dd, mjd) 

220 self.assertIsInstance(dd, np.float) 

221 self.assertAlmostEqual(dd, control_distance[ix], 12) 

222 

223 def testDistanceToSunDeg(self): 

224 """ 

225 Test that distanceToSun is consistent with _distanceToSun 

226 """ 

227 

228 for tai, ra, dec in zip((57632.1, 45623.4, 55682.3), (112.0, 24.0, 231.2), (-25.0, 23.4, -60.0)): 

229 mjd = ModifiedJulianDate(TAI=tai) 

230 dd_deg = distanceToSun(ra, dec, mjd) 

231 dd_rad = _distanceToSun(np.radians(ra), np.radians(dec), mjd) 

232 self.assertAlmostEqual(np.radians(dd_deg), dd_rad, 10) 

233 

234 def testSolarRaDecDeg(self): 

235 """ 

236 Test that solarRaDec is consistent with _solarRaDec 

237 """ 

238 

239 for tai in (57664.2, 53478.9, 45672.1): 

240 mjd = ModifiedJulianDate(TAI=tai) 

241 ra_deg, dec_deg = solarRaDec(mjd) 

242 ra_rad, dec_rad = _solarRaDec(mjd) 

243 self.assertAlmostEqual(np.radians(ra_deg), ra_rad, 10) 

244 self.assertAlmostEqual(np.radians(dec_deg), dec_rad, 10) 

245 

246 def testDistanceToSunArray(self): 

247 """ 

248 Test _distanceToSun on numpy arrays of RA, Dec using solar RA, Dec calculated from 

249 

250 http://aa.usno.navy.mil/data/docs/JulianDate.php 

251 http://aa.usno.navy.mil/data/docs/geocentric.php 

252 """ 

253 

254 rng = np.random.RandomState(77) 

255 nStars = 100 

256 

257 hour = np.radians(360.0 / 24.0) 

258 minute = hour / 60.0 

259 second = minute / 60.0 

260 

261 mjd_list = [57026.0, 57543.625] 

262 

263 sun_ra_list = [18.0 * hour + 56.0 * minute + 51.022 * second, 

264 4.0 * hour + 51.0 * minute + 22.776 * second] 

265 

266 sun_dec_list = [np.radians(-22.0 - 47.0 / 60.0 - 40.27 / 3600.0), 

267 np.radians(22.0 + 30.0 / 60.0 + 0.73 / 3600.0)] 

268 

269 for tai, raS, decS in zip(mjd_list, sun_ra_list, sun_dec_list): 

270 mjd = ModifiedJulianDate(TAI=tai) 

271 ra_list = rng.random_sample(nStars) * 2.0 * np.pi 

272 dec_list = (rng.random_sample(nStars) - 0.5) * np.pi 

273 distance_list = _distanceToSun(ra_list, dec_list, mjd) 

274 distance_control = haversine(ra_list, dec_list, raS, decS) 

275 np.testing.assert_array_almost_equal( 

276 distance_list, distance_control, 5) 

277 

278 def testAstrometryExceptions(self): 

279 """ 

280 Test to make sure that stand-alone astrometry methods raise an exception when they are called without 

281 the necessary arguments 

282 """ 

283 obs_metadata = makeObservationMetaData() 

284 ra, dec, pm_ra, pm_dec, parallax, v_rad = makeRandomSample() 

285 

286 raShort = np.array([1.0]) 

287 decShort = np.array([1.0]) 

288 

289 # test refractionCoefficients 

290 self.assertRaises(RuntimeError, refractionCoefficients) 

291 site = obs_metadata.site 

292 x, y = refractionCoefficients(site=site) 

293 

294 # test applyRefraction 

295 zd = 0.1 

296 applyRefraction(zd, x, y) 

297 

298 zd = [0.1, 0.2] 

299 self.assertRaises(RuntimeError, applyRefraction, zd, x, y) 

300 

301 zd = np.array([0.1, 0.2]) 

302 applyRefraction(zd, x, y) 

303 

304 # test _applyPrecession 

305 # test without mjd 

306 self.assertRaises(RuntimeError, _applyPrecession, ra, dec) 

307 

308 # test mismatches 

309 self.assertRaises(RuntimeError, _applyPrecession, raShort, dec, 

310 mjd=ModifiedJulianDate(TAI=52000.0)) 

311 self.assertRaises(RuntimeError, _applyPrecession, ra, decShort, 

312 mjd=ModifiedJulianDate(TAI=52000.0)) 

313 

314 # test that it runs 

315 _applyPrecession(ra, dec, mjd=ModifiedJulianDate(TAI=52000.0)) 

316 

317 # test _applyProperMotion 

318 raList = list(ra) 

319 decList = list(dec) 

320 pm_raList = list(pm_ra) 

321 pm_decList = list(pm_dec) 

322 parallaxList = list(parallax) 

323 v_radList = list(v_rad) 

324 

325 pm_raShort = np.array([pm_ra[0]]) 

326 pm_decShort = np.array([pm_dec[0]]) 

327 parallaxShort = np.array([parallax[0]]) 

328 v_radShort = np.array([v_rad[0]]) 

329 

330 # test without mjd 

331 self.assertRaises(RuntimeError, _applyProperMotion, 

332 ra, dec, pm_ra, pm_dec, parallax, v_rad) 

333 

334 # test passing lists 

335 self.assertRaises(RuntimeError, _applyProperMotion, 

336 raList, dec, pm_ra, pm_dec, parallax, v_rad, 

337 mjd=ModifiedJulianDate(TAI=52000.0)) 

338 self.assertRaises(RuntimeError, _applyProperMotion, 

339 ra, decList, pm_ra, pm_dec, parallax, v_rad, 

340 mjd=ModifiedJulianDate(TAI=52000.0)) 

341 self.assertRaises(RuntimeError, _applyProperMotion, 

342 ra, dec, pm_raList, pm_dec, parallax, v_rad, 

343 mjd=ModifiedJulianDate(TAI=52000.0)) 

344 self.assertRaises(RuntimeError, _applyProperMotion, 

345 ra, dec, pm_ra, pm_decList, parallax, v_rad, 

346 mjd=ModifiedJulianDate(TAI=52000.0)) 

347 self.assertRaises(RuntimeError, _applyProperMotion, 

348 ra, dec, pm_ra, pm_dec, parallaxList, v_rad, 

349 mjd=ModifiedJulianDate(TAI=52000.0)) 

350 self.assertRaises(RuntimeError, _applyProperMotion, 

351 ra, dec, pm_ra, pm_dec, parallax, v_radList, 

352 mjd=ModifiedJulianDate(TAI=52000.0)) 

353 

354 # test mismatches 

355 self.assertRaises(RuntimeError, _applyProperMotion, 

356 raShort, dec, pm_ra, pm_dec, parallax, v_rad, 

357 mjd=ModifiedJulianDate(TAI=52000.0)) 

358 self.assertRaises(RuntimeError, _applyProperMotion, 

359 ra, decShort, pm_ra, pm_dec, parallax, v_rad, 

360 mjd=ModifiedJulianDate(TAI=52000.0)) 

361 self.assertRaises(RuntimeError, _applyProperMotion, 

362 ra, dec, pm_raShort, pm_dec, parallax, v_rad, 

363 mjd=ModifiedJulianDate(TAI=52000.0)) 

364 self.assertRaises(RuntimeError, _applyProperMotion, 

365 ra, dec, pm_ra, pm_decShort, parallax, v_rad, 

366 mjd=ModifiedJulianDate(TAI=52000.0)) 

367 self.assertRaises(RuntimeError, _applyProperMotion, 

368 ra, dec, pm_ra, pm_dec, parallaxShort, v_rad, 

369 mjd=ModifiedJulianDate(TAI=52000.0)) 

370 self.assertRaises(RuntimeError, _applyProperMotion, 

371 ra, dec, pm_ra, pm_dec, parallax, v_radShort, 

372 mjd=ModifiedJulianDate(TAI=52000.0)) 

373 

374 # test that it actually runs 

375 _applyProperMotion(ra, dec, pm_ra, pm_dec, parallax, v_rad, 

376 mjd=ModifiedJulianDate(TAI=52000.0)) 

377 _applyProperMotion(ra[0], dec[0], pm_ra[0], pm_dec[0], parallax[0], v_rad[0], 

378 mjd=ModifiedJulianDate(TAI=52000.0)) 

379 

380 # test _appGeoFromICRS 

381 # test without mjd 

382 self.assertRaises(RuntimeError, _appGeoFromICRS, ra, dec) 

383 

384 # test with mismatched ra, dec 

385 self.assertRaises(RuntimeError, _appGeoFromICRS, ra, decShort, 

386 mjd=ModifiedJulianDate(TAI=52000.0)) 

387 self.assertRaises(RuntimeError, _appGeoFromICRS, raShort, dec, 

388 mjd=ModifiedJulianDate(TAI=52000.0)) 

389 

390 # test that it actually urns 

391 _appGeoFromICRS(ra, dec, mjd=obs_metadata.mjd) 

392 

393 # test _observedFromAppGeo 

394 # test without obs_metadata 

395 self.assertRaises(RuntimeError, _observedFromAppGeo, ra, dec) 

396 

397 # test without site 

398 dummy = ObservationMetaData(pointingRA=obs_metadata.pointingRA, 

399 pointingDec=obs_metadata.pointingDec, 

400 mjd=obs_metadata.mjd, 

401 site=None) 

402 self.assertRaises(RuntimeError, _observedFromAppGeo, 

403 ra, dec, obs_metadata=dummy) 

404 

405 # test without mjd 

406 dummy = ObservationMetaData(pointingRA=obs_metadata.pointingRA, 

407 pointingDec=obs_metadata.pointingDec, 

408 site=Site(name='LSST')) 

409 self.assertRaises(RuntimeError, _observedFromAppGeo, 

410 ra, dec, obs_metadata=dummy) 

411 

412 # test mismatches 

413 dummy = ObservationMetaData(pointingRA=obs_metadata.pointingRA, 

414 pointingDec=obs_metadata.pointingDec, 

415 mjd=obs_metadata.mjd, 

416 site=Site(name='LSST')) 

417 

418 self.assertRaises(RuntimeError, _observedFromAppGeo, 

419 ra, decShort, obs_metadata=dummy) 

420 self.assertRaises(RuntimeError, _observedFromAppGeo, 

421 raShort, dec, obs_metadata=dummy) 

422 

423 # test that it actually runs 

424 _observedFromAppGeo(ra, dec, obs_metadata=dummy) 

425 

426 # test _observedFromICRS 

427 # test without epoch 

428 self.assertRaises(RuntimeError, _observedFromICRS, 

429 ra, dec, obs_metadata=obs_metadata) 

430 

431 # test without obs_metadata 

432 self.assertRaises(RuntimeError, _observedFromICRS, 

433 ra, dec, epoch=2000.0) 

434 

435 # test without mjd 

436 dummy = ObservationMetaData(pointingRA=obs_metadata.pointingRA, 

437 pointingDec=obs_metadata.pointingDec, 

438 site=obs_metadata.site) 

439 self.assertRaises(RuntimeError, _observedFromICRS, ra, 

440 dec, epoch=2000.0, obs_metadata=dummy) 

441 

442 # test that it actually runs 

443 dummy = ObservationMetaData(pointingRA=obs_metadata.pointingRA, 

444 pointingDec=obs_metadata.pointingDec, 

445 site=obs_metadata.site, 

446 mjd=obs_metadata.mjd) 

447 

448 # test mismatches 

449 self.assertRaises(RuntimeError, _observedFromICRS, ra, 

450 decShort, epoch=2000.0, obs_metadata=dummy) 

451 self.assertRaises(RuntimeError, _observedFromICRS, 

452 raShort, dec, epoch=2000.0, obs_metadata=dummy) 

453 

454 # test that it actually runs 

455 ra_arr, dec_arr = _observedFromICRS( 

456 ra, dec, obs_metadata=dummy, epoch=2000.0) 

457 self.assertIsInstance(ra_arr, np.ndarray) 

458 self.assertIsInstance(dec_arr, np.ndarray) 

459 

460 # test passing in floats 

461 for ix in range(len(ra_arr)): 

462 ra_f, dec_f = _observedFromICRS( 

463 ra[ix], dec[ix], obs_metadata=dummy, epoch=2000.0) 

464 self.assertIsInstance(ra_f, np.float) 

465 self.assertIsInstance(dec_f, np.float) 

466 self.assertAlmostEqual(ra_f, ra_arr[ix], 12) 

467 self.assertAlmostEqual(dec_f, dec_arr[ix], 12) 

468 

469 def test_applyPrecession(self): 

470 

471 ra = np.zeros((3), dtype=float) 

472 dec = np.zeros((3), dtype=float) 

473 

474 ra[0] = 2.549091039839124218e+00 

475 dec[0] = 5.198752733024248895e-01 

476 ra[1] = 8.693375673649429425e-01 

477 dec[1] = 1.038086165642298164e+00 

478 ra[2] = 7.740864769302191473e-01 

479 dec[2] = 2.758053025017753179e-01 

480 

481 self.assertRaises(RuntimeError, _applyPrecession, ra, dec) 

482 

483 # just make sure it runs 

484 mjd = ModifiedJulianDate(TAI=57388.0) 

485 ra_arr, dec_arr = _applyPrecession(ra, dec, mjd=mjd) 

486 self.assertIsInstance(ra_arr, np.ndarray) 

487 self.assertIsInstance(dec_arr, np.ndarray) 

488 

489 # test that passing in floats gie the same results 

490 for ix, (rr, dd) in enumerate(zip(ra, dec)): 

491 ra_f, dec_f = _applyPrecession(rr, dd, mjd=mjd) 

492 self.assertIsInstance(ra_f, np.float) 

493 self.assertIsInstance(dec_f, np.float) 

494 self.assertAlmostEqual(ra_f, ra_arr[ix], 12) 

495 self.assertAlmostEqual(dec_f, dec_arr[ix], 12) 

496 

497 def test_applyProperMotion(self): 

498 """ 

499 Compare the output of _applyProperMotion to control outputs 

500 generated by recreating the 'space motion' section of code 

501 from palMapqk.c in palpy/cextern/pal 

502 """ 

503 VF = 0.21094502 

504 pal_das2r = 4.8481368110953599358991410235794797595635330237270e-6 

505 

506 rng = np.random.RandomState(18) 

507 nSamples = 1000 

508 

509 mjdList = rng.random_sample(20) * 20000.0 + 45000.0 

510 

511 for mjd in mjdList: 

512 

513 raList_icrs = rng.random_sample(nSamples) * 2.0 * np.pi 

514 decList_icrs = (rng.random_sample(nSamples) - 0.5) * np.pi 

515 

516 # stars' original position in Cartesian space 

517 x_list_icrs = np.cos(decList_icrs) * np.cos(raList_icrs) 

518 y_list_icrs = np.cos(decList_icrs) * np.sin(raList_icrs) 

519 z_list_icrs = np.sin(decList_icrs) 

520 

521 pm_ra = (rng.random_sample(nSamples) - 0.5) * radiansFromArcsec(1.0) 

522 pm_dec = (rng.random_sample(nSamples) - 0.5) * radiansFromArcsec(1.0) 

523 px = rng.random_sample(nSamples) * radiansFromArcsec(1.0) 

524 v_rad = rng.random_sample(nSamples) * 200.0 

525 

526 ra_list_pm, dec_list_pm = _applyProperMotion(raList_icrs, decList_icrs, 

527 pm_ra * np.cos(decList_icrs), 

528 pm_dec, px, v_rad, mjd=ModifiedJulianDate(TAI=mjd)) 

529 

530 # stars' Cartesian position after proper motion is applied 

531 x_list_pm = np.cos(dec_list_pm) * np.cos(ra_list_pm) 

532 y_list_pm = np.cos(dec_list_pm) * np.sin(ra_list_pm) 

533 z_list_pm = np.sin(dec_list_pm) 

534 

535 ############################################################### 

536 # The code below is copied from palMapqk.c in palpy/cextern/pal 

537 params = pal.mappa(2000.0, mjd) 

538 pmt = params[0] 

539 eb = np.array([params[1], params[2], params[3]]) 

540 

541 pxr = px * pal_das2r 

542 

543 w = VF * v_rad * pxr 

544 

545 motion_per_year = np.array([-1.0 * pm_ra * y_list_icrs - 

546 pm_dec * 

547 np.cos(raList_icrs) * np.sin(decList_icrs) + w * x_list_icrs, 

548 pm_ra * x_list_icrs - 

549 pm_dec * 

550 np.sin(raList_icrs) * np.sin(decList_icrs) + w * y_list_icrs, 

551 pm_dec * np.cos(decList_icrs) + w * z_list_icrs]) 

552 

553 xyz_control = np.array([x_list_icrs + pmt * motion_per_year[0] - pxr * eb[0], 

554 y_list_icrs + pmt * 

555 motion_per_year[1] - pxr * eb[1], 

556 z_list_icrs + pmt * motion_per_year[2] - pxr * eb[2]]) 

557 

558 xyz_norm = np.sqrt(np.power(xyz_control[0], 2) + 

559 np.power(xyz_control[1], 2) + 

560 np.power(xyz_control[2], 2)) 

561 

562 # stars' Cartesian position after applying the control proper 

563 # motion method 

564 xyz_control[0] = xyz_control[0] / xyz_norm 

565 xyz_control[1] = xyz_control[1] / xyz_norm 

566 xyz_control[2] = xyz_control[2] / xyz_norm 

567 

568 # this is the Cartesian distance between the stars' positions as found by _applyProperMotion 

569 # and the distance as found by the control proper motion code above 

570 distance = np.sqrt(np.power(x_list_pm - xyz_control[0], 2) + 

571 np.power(y_list_pm - xyz_control[1], 2) + 

572 np.power(z_list_pm - xyz_control[2], 2)) 

573 

574 # this is the Cartesian distance between the stars' original positions on the celestial sphere 

575 # and their positions after the control proper motion was applied 

576 correction = np.sqrt(np.power(xyz_control[0] - x_list_icrs, 2) + 

577 np.power(xyz_control[1] - y_list_icrs, 2) + 

578 np.power(xyz_control[2] - z_list_icrs, 2)) 

579 

580 dex = np.argmax(distance) 

581 msg = 'pm %e %e vr %e px %e; time %e; err %e arcsec; corr %e' % \ 

582 (arcsecFromRadians(pm_ra[dex]), arcsecFromRadians(pm_dec[dex]), 

583 v_rad[dex], arcsecFromRadians(px[dex]), pmt, arcsecFromRadians(distance[dex]), 

584 arcsecFromRadians(correction[dex])) 

585 

586 # demand that the two methods agree on the stars' new positions to 

587 # within one part in 100 

588 testValue = (distance / correction).max() 

589 self.assertLess(testValue, 0.01, msg=msg) 

590 

591 def test_applyProperMotion_inputs(self): 

592 """ 

593 Verify that _applyProperMotion handles both floats and numpy arrays as inputs 

594 """ 

595 rng = np.random.RandomState(7134) 

596 nSamples = 100 

597 pm_ra = (rng.random_sample(nSamples) - 0.5) * radiansFromArcsec(1.0) 

598 pm_dec = (rng.random_sample(nSamples) - 0.5) * radiansFromArcsec(1.0) 

599 px = rng.random_sample(nSamples) * radiansFromArcsec(1.0) 

600 v_rad = rng.random_sample(nSamples) * 200.0 

601 mjd = ModifiedJulianDate(TAI=59580.0) 

602 

603 ra_icrs = rng.random_sample(nSamples) * 2.0 * np.pi 

604 dec_icrs = (rng.random_sample(nSamples) - 0.5) * np.pi 

605 

606 ra_arr, dec_arr = _applyProperMotion(ra_icrs, dec_icrs, 

607 pm_ra, pm_dec, px, v_rad, mjd=mjd) 

608 

609 self.assertIsInstance(ra_arr, np.ndarray) 

610 self.assertIsInstance(dec_arr, np.ndarray) 

611 

612 for ix, (rr, dd, mura, mudec, xx, vv) in \ 

613 enumerate(zip(ra_icrs, dec_icrs, pm_ra, pm_dec, px, v_rad)): 

614 

615 ra_f, dec_f = _applyProperMotion( 

616 rr, dd, mura, mudec, xx, vv, mjd=mjd) 

617 self.assertIsInstance(ra_f, np.float) 

618 self.assertIsInstance(dec_f, np.float) 

619 distance = arcsecFromRadians( 

620 haversine(ra_f, dec_f, ra_arr[ix], dec_arr[ix])) 

621 self.assertLess(distance, 0.000001) 

622 

623 def test_appGeoFromICRS(self): 

624 """ 

625 Test conversion between ICRS RA, Dec and apparent geocentric ICRS. 

626 

627 Apparent, geocentric RA, Dec of objects will be taken from this website 

628 

629 http://aa.usno.navy.mil/data/docs/geocentric.php 

630 

631 dates converted to JD using this website 

632 

633 http://aa.usno.navy.mil/data/docs/geocentric.php 

634 

635 """ 

636 

637 hours = np.radians(360.0 / 24.0) 

638 minutes = hours / 60.0 

639 seconds = minutes / 60.0 

640 

641 # test on Arcturus 

642 # data taken from 

643 # http://aa.usno.navy.mil/data/docs/geocentric.php 

644 ra_icrs = 14.0 * hours + 15.0 * minutes + 39.67207 * seconds 

645 dec_icrs = np.radians(19.0 + 10.0 / 60.0 + 56.673 / 3600.0) 

646 pm_ra = radiansFromArcsec(-1.0939) 

647 pm_dec = radiansFromArcsec(-2.00006) 

648 v_rad = -5.19 

649 px = radiansFromArcsec(0.08883) 

650 

651 mjd_list = [] 

652 ra_app_list = [] 

653 dec_app_list = [] 

654 

655 # jd (UT) 

656 jd = 2457000.375000 

657 mjd = jd - 2400000.5 

658 

659 mjd_list.append(mjd) 

660 ra_app_list.append(14.0 * hours + 16.0 * minutes + 19.59 * seconds) 

661 dec_app_list.append(np.radians(19.0 + 6.0 / 60.0 + 19.56 / 3600.0)) 

662 

663 jd = 2457187.208333 

664 mjd = jd - 2400000.5 

665 mjd_list.append(mjd) 

666 ra_app_list.append(14.0 * hours + 16.0 * minutes + 22.807 * seconds) 

667 dec_app_list.append(np.radians(19.0 + 6.0 / 60.0 + 18.12 / 3600.0)) 

668 

669 jd = 2457472.625000 

670 mjd = jd - 2400000.5 

671 mjd_list.append(mjd) 

672 ra_app_list.append(14.0 * hours + 16.0 * minutes + 24.946 * seconds) 

673 dec_app_list.append(np.radians(19.0 + 5.0 / 60.0 + 49.65 / 3600.0)) 

674 

675 for mjd, ra_app, dec_app in zip(mjd_list, ra_app_list, dec_app_list): 

676 obs = ObservationMetaData(mjd=mjd) 

677 

678 ra_test, dec_test = _appGeoFromICRS(ra_icrs, dec_icrs, 

679 mjd=obs.mjd, 

680 pm_ra=pm_ra, 

681 pm_dec=pm_dec, 

682 v_rad=v_rad, 

683 parallax=px, 

684 epoch=2000.0) 

685 

686 self.assertIsInstance(ra_test, np.float) 

687 self.assertIsInstance(dec_test, np.float) 

688 

689 distance = arcsecFromRadians( 

690 haversine(ra_app, dec_app, ra_test, dec_test)) 

691 self.assertLess(distance, 0.1) 

692 

693 # test on Sirius 

694 # data taken from 

695 # http://simbad.u-strasbg.fr/simbad/sim-id?Ident=Sirius 

696 ra_icrs = 6.0 * hours + 45.0 * minutes + 8.91728 * seconds 

697 dec_icrs = np.radians(-16.0 - 42.0 / 60.0 - 58.0171 / 3600.0) 

698 pm_ra = radiansFromArcsec(-0.54601) 

699 pm_dec = radiansFromArcsec(-1.22307) 

700 px = radiansFromArcsec(0.37921) 

701 v_rad = -5.5 

702 

703 mjd_list = [] 

704 ra_app_list = [] 

705 dec_app_list = [] 

706 

707 jd = 2457247.000000 

708 mjd_list.append(jd - 2400000.5) 

709 ra_app_list.append(6.0 * hours + 45.0 * minutes + 49.276 * seconds) 

710 dec_app_list.append(np.radians(-16.0 - 44.0 / 60.0 - 18.69 / 3600.0)) 

711 

712 jd = 2456983.958333 

713 mjd_list.append(jd - 2400000.5) 

714 ra_app_list.append(6.0 * hours + 45.0 * minutes + 49.635 * seconds) 

715 dec_app_list.append(np.radians(-16.0 - 44.0 / 60.0 - 17.04 / 3600.0)) 

716 

717 jd = 2457523.958333 

718 mjd_list.append(jd - 2400000.5) 

719 ra_app_list.append(6.0 * hours + 45.0 * minutes + 50.99 * seconds) 

720 dec_app_list.append(np.radians(-16.0 - 44.0 / 60.0 - 39.76 / 3600.0)) 

721 

722 for mjd, ra_app, dec_app in zip(mjd_list, ra_app_list, dec_app_list): 

723 obs = ObservationMetaData(mjd=mjd) 

724 

725 ra_test, dec_test = _appGeoFromICRS(ra_icrs, dec_icrs, 

726 mjd=obs.mjd, 

727 pm_ra=pm_ra, 

728 pm_dec=pm_dec, 

729 v_rad=v_rad, 

730 parallax=px, 

731 epoch=2000.0) 

732 

733 self.assertIsInstance(ra_test, np.float) 

734 self.assertIsInstance(dec_test, np.float) 

735 

736 distance = arcsecFromRadians( 

737 haversine(ra_app, dec_app, ra_test, dec_test)) 

738 self.assertLess(distance, 0.1) 

739 

740 def test_appGeoFromICRS_inputs(self): 

741 """ 

742 Test that appGeoFromICRS behaves as expected when given both numpy 

743 array and float inputs. 

744 """ 

745 

746 rng = np.random.RandomState(83) 

747 nSamples = 100 

748 ra_icrs = 2.0 * np.pi * rng.random_sample(nSamples) 

749 dec_icrs = (rng.random_sample(nSamples) - 0.5) * np.pi 

750 pm_ra = radiansFromArcsec( 

751 (rng.random_sample(nSamples) - 0.5) * 0.02) 

752 pm_dec = radiansFromArcsec( 

753 (rng.random_sample(nSamples) - 0.5) * 0.02) 

754 parallax = radiansFromArcsec(rng.random_sample(nSamples) * 0.01) 

755 v_rad = (rng.random_sample(nSamples) - 0.5) * 1200.0 

756 mjd = ModifiedJulianDate(TAI=59580.0) 

757 

758 ra_control, dec_control = _appGeoFromICRS(ra_icrs, dec_icrs, 

759 pm_ra=pm_ra, pm_dec=pm_dec, parallax=parallax, 

760 v_rad=v_rad, mjd=mjd) 

761 

762 self.assertIsInstance(ra_control, np.ndarray) 

763 self.assertIsInstance(dec_control, np.ndarray) 

764 

765 # test that passing in floats and numpy arrays gives the same result 

766 for ix in range(len(ra_control)): 

767 ra_test, dec_test = _appGeoFromICRS(ra_icrs[ix], dec_icrs[ix], 

768 pm_ra=pm_ra[ix], pm_dec=pm_dec[ix], 

769 parallax=parallax[ix], v_rad=v_rad[ix], 

770 mjd=mjd) 

771 

772 self.assertIsInstance(ra_test, np.float) 

773 self.assertIsInstance(dec_test, np.float) 

774 dd = arcsecFromRadians(haversine(ra_test, dec_test, ra_control[ix], dec_control[ix])) 

775 self.assertLess(dd, 1.0e-6) 

776 

777 # next test that inputs of inappropriate types raise RuntimeErrors 

778 with self.assertRaises(RuntimeError) as context: 

779 ra, dec = _appGeoFromICRS(ra_icrs, 5.0, pm_ra=pm_ra, pm_dec=pm_dec, 

780 parallax=parallax, v_rad=v_rad, mjd=mjd) 

781 self.assertIn("The input arguments:", context.exception.args[0]) 

782 self.assertIn("dec", context.exception.args[0]) 

783 

784 with self.assertRaises(RuntimeError) as context: 

785 ra, dec = _appGeoFromICRS(5.0, dec_icrs, pm_ra=pm_ra, pm_dec=pm_dec, 

786 parallax=parallax, v_rad=v_rad, mjd=mjd) 

787 

788 self.assertIn("The input arguments:", context.exception.args[0]) 

789 self.assertIn("dec", context.exception.args[0]) 

790 

791 with self.assertRaises(RuntimeError) as context: 

792 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=5.0, pm_dec=pm_dec, 

793 parallax=parallax, v_rad=v_rad, mjd=mjd) 

794 

795 self.assertIn("The input arguments:", context.exception.args[0]) 

796 self.assertIn("pm_ra", context.exception.args[0]) 

797 

798 with self.assertRaises(RuntimeError) as context: 

799 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=pm_ra, pm_dec=5.0, 

800 parallax=parallax, v_rad=v_rad, mjd=mjd) 

801 

802 self.assertIn("The input arguments:", context.exception.args[0]) 

803 self.assertIn("pm_dec", context.exception.args[0]) 

804 

805 with self.assertRaises(RuntimeError) as context: 

806 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=pm_ra, pm_dec=pm_dec, 

807 parallax=5.0, v_rad=v_rad, mjd=mjd) 

808 

809 self.assertIn("The input arguments:", context.exception.args[0]) 

810 self.assertIn("parallax", context.exception.args[0]) 

811 

812 with self.assertRaises(RuntimeError) as context: 

813 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=pm_ra, pm_dec=pm_dec, 

814 parallax=parallax, v_rad=5.0, mjd=mjd) 

815 

816 self.assertIn("The input arguments:", context.exception.args[0]) 

817 self.assertIn("v_rad", context.exception.args[0]) 

818 

819 with self.assertRaises(RuntimeError) as context: 

820 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs[:2], pm_ra=pm_ra, pm_dec=pm_dec, 

821 parallax=parallax, v_rad=v_rad, mjd=mjd) 

822 

823 self.assertEqual(context.exception.args[0], 

824 "The arrays input to appGeoFromICRS all need to " 

825 "have the same length") 

826 

827 with self.assertRaises(RuntimeError) as context: 

828 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=pm_ra[:2], pm_dec=pm_dec, 

829 parallax=parallax, v_rad=v_rad, mjd=mjd) 

830 

831 self.assertEqual(context.exception.args[0], 

832 "The arrays input to appGeoFromICRS all need to " 

833 "have the same length") 

834 

835 with self.assertRaises(RuntimeError) as context: 

836 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=pm_ra, pm_dec=pm_dec[:2], 

837 parallax=parallax, v_rad=v_rad, mjd=mjd) 

838 

839 self.assertEqual(context.exception.args[0], 

840 "The arrays input to appGeoFromICRS all need to " 

841 "have the same length") 

842 

843 with self.assertRaises(RuntimeError) as context: 

844 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=pm_ra, pm_dec=pm_dec, 

845 parallax=parallax[:2], v_rad=v_rad, mjd=mjd) 

846 

847 self.assertEqual(context.exception.args[0], 

848 "The arrays input to appGeoFromICRS all need to " 

849 "have the same length") 

850 

851 with self.assertRaises(RuntimeError) as context: 

852 ra, dec = _appGeoFromICRS(ra_icrs, dec_icrs, pm_ra=pm_ra, pm_dec=pm_dec, 

853 parallax=parallax, v_rad=v_rad[:2], mjd=mjd) 

854 

855 self.assertEqual(context.exception.args[0], 

856 "The arrays input to appGeoFromICRS all need to " 

857 "have the same length") 

858 

859 def test_appGeoFromICRS_noMotion(self): 

860 """ 

861 Test that appGeoFromICRS with parallax, proper motion, and radial velocity 

862 set to None behaves the same as appGeoFromICRs with parallax, proper motion 

863 and radial velocity set to zero. 

864 """ 

865 obs = ObservationMetaData(pointingRA=25.0, pointingDec=-11.0, 

866 mjd=59781.2) 

867 

868 rng = np.random.RandomState(88) 

869 n_obj = 100 

870 ra_list = rng.random_sample(n_obj)*2.0*np.pi 

871 dec_list = rng.random_sample(n_obj)*np.pi-0.5*np.pi 

872 px_list = np.zeros(n_obj) 

873 vrad_list = np.zeros(n_obj) 

874 pm_ra_list = np.zeros(n_obj) 

875 pm_dec_list = np.zeros(n_obj) 

876 

877 control_ra, control_dec = _appGeoFromICRS(ra_list, dec_list, mjd=obs.mjd, 

878 pm_ra=pm_ra_list, pm_dec=pm_dec_list, 

879 parallax=px_list, v_rad=vrad_list, 

880 epoch=2000.0) 

881 

882 test_ra, test_dec = _appGeoFromICRS(ra_list, dec_list, 

883 mjd=obs.mjd, epoch=2000.0) 

884 

885 dd_sun = _distanceToSun(ra_list, dec_list, obs.mjd) 

886 valid = np.where(dd_sun > np.radians(20.0)) 

887 self.assertGreater(len(valid[0]), n_obj/3) 

888 

889 dd = _angularSeparation(test_ra[valid], test_dec[valid], 

890 control_ra[valid], control_dec[valid]) 

891 self.assertLess(arcsecFromRadians(dd).max(), 0.005) 

892 

893 def test_icrsFromAppGeo(self): 

894 """ 

895 Test that _icrsFromAppGeo really inverts _appGeoFromICRS. 

896 

897 This test is a tricky because _appGeoFromICRS applies 

898 light deflection due to the sun. _icrsFromAppGeo does not 

899 account for that effect, which is fine, because it is only 

900 meant to map pointing RA, Decs to RA, Decs on fatboy. 

901 

902 _icrsFromAppGeo should invert _appGeoFromICRS to within 

903 0.01 arcsec at an angular distance greater than 45 degrees 

904 from the sun. 

905 """ 

906 

907 rng = np.random.RandomState(412) 

908 nSamples = 100 

909 

910 for tai in (53000.0, 53241.6, 58504.6): 

911 

912 mjd = ModifiedJulianDate(TAI=tai) 

913 

914 ra_in = rng.random_sample(nSamples) * 2.0 * np.pi 

915 dec_in = (rng.random_sample(nSamples) - 0.5) * np.pi 

916 

917 ra_app, dec_app = _appGeoFromICRS(ra_in, dec_in, mjd=mjd) 

918 

919 ra_icrs, dec_icrs = _icrsFromAppGeo(ra_app, dec_app, 

920 epoch=2000.0, mjd=mjd) 

921 

922 self.assertFalse(np.isnan(ra_icrs).any(), msg='There were NaNs in ra_icrs; should not be') 

923 self.assertFalse(np.isnan(dec_icrs).any(), msg='There were NaNs in dec_icrs; should not be') 

924 

925 valid_pts = np.where(_distanceToSun( 

926 ra_in, dec_in, mjd) > 0.25 * np.pi)[0] 

927 

928 self.assertGreater(len(valid_pts), 0) 

929 

930 distance = arcsecFromRadians(pal.dsepVector(ra_in[valid_pts], dec_in[valid_pts], 

931 ra_icrs[valid_pts], dec_icrs[valid_pts])) 

932 

933 self.assertLess(distance.max(), 0.01) 

934 

935 # test passing in floats 

936 for ix in valid_pts: 

937 ra_test, dec_test = _icrsFromAppGeo( 

938 ra_app[ix], dec_app[ix], mjd=mjd) 

939 self.assertIsInstance(ra_test, np.float) 

940 self.assertIsInstance(dec_test, np.float) 

941 distance_f = arcsecFromRadians(pal.dsep(ra_in[ix], dec_in[ix], 

942 ra_test, dec_test)) 

943 self.assertLess(distance_f, 0.01) 

944 

945 def test_icrsFromObserved(self): 

946 """ 

947 Test that _icrsFromObserved really inverts _observedFromICRS and that 

948 _appGeoFromObserved really does invert _observedFromAppGeo. 

949 

950 In this case, the method is only reliable at distances of more than 

951 45 degrees from the sun and at zenith distances less than 70 degrees. 

952 """ 

953 

954 rng = np.random.RandomState(412) 

955 nSamples = 100 

956 

957 site = Site(name='LSST') 

958 

959 for tai in (53000.0, 53241.6, 58504.6): 

960 for includeRefraction in (True, False): 

961 for raPointing in (23.5, 256.9, 100.0): 

962 for decPointing in (-12.0, 45.0, 66.8): 

963 

964 obs = ObservationMetaData(mjd=tai, site=site) 

965 

966 raZenith, decZenith = _raDecFromAltAz( 

967 0.5 * np.pi, 0.0, obs) 

968 

969 obs = ObservationMetaData(pointingRA=raPointing, pointingDec=decPointing, 

970 mjd=tai, site=site) 

971 

972 rr = rng.random_sample( 

973 nSamples) * np.radians(50.0) 

974 theta = rng.random_sample(nSamples) * 2.0 * np.pi 

975 

976 ra_in = raZenith + rr * np.cos(theta) 

977 dec_in = decZenith + rr * np.sin(theta) 

978 

979 # test a round-trip between observedFromICRS and 

980 # icrsFromObserved 

981 ra_obs, dec_obs = _observedFromICRS(ra_in, dec_in, obs_metadata=obs, 

982 includeRefraction=includeRefraction, 

983 epoch=2000.0) 

984 

985 ra_icrs, dec_icrs = _icrsFromObserved(ra_obs, dec_obs, obs_metadata=obs, 

986 includeRefraction=includeRefraction, 

987 epoch=2000.0) 

988 

989 valid_pts = np.where(_distanceToSun( 

990 ra_in, dec_in, obs.mjd) > 0.25 * np.pi)[0] 

991 

992 self.assertGreater(len(valid_pts), 0) 

993 

994 distance = arcsecFromRadians(pal.dsepVector(ra_in[valid_pts], dec_in[valid_pts], 

995 ra_icrs[valid_pts], dec_icrs[valid_pts])) 

996 

997 self.assertLess(distance.max(), 1.0e-6) 

998 

999 # test a round-trip between observedFromAppGeo and 

1000 # appGeoFromObserved 

1001 ra_obs_from_app_geo, \ 

1002 dec_obs_from_app_geo = _observedFromAppGeo(ra_in, dec_in, obs_metadata=obs, 

1003 includeRefraction=includeRefraction) 

1004 

1005 ra_app, dec_app = _appGeoFromObserved(ra_obs_from_app_geo, 

1006 dec_obs_from_app_geo, 

1007 obs_metadata=obs, 

1008 includeRefraction=includeRefraction) 

1009 

1010 distance = arcsecFromRadians(pal.dsepVector(ra_in[valid_pts], dec_in[valid_pts], 

1011 ra_app[valid_pts], dec_app[valid_pts])) 

1012 

1013 self.assertLess(distance.max(), 1.0e-6) 

1014 

1015 # test that passing arguments in as floats gives consistent 

1016 # results 

1017 for ix in valid_pts: 

1018 ra_f, dec_f = _icrsFromObserved(ra_obs[ix], dec_obs[ix], 

1019 obs_metadata=obs, 

1020 includeRefraction=includeRefraction, 

1021 epoch=2000.0) 

1022 self.assertIsInstance(ra_f, np.float) 

1023 self.assertIsInstance(dec_f, np.float) 

1024 dist_f = arcsecFromRadians(pal.dsep(ra_icrs[ix], dec_icrs[ix], ra_f, dec_f)) 

1025 self.assertLess(dist_f, 1.0e-9) 

1026 

1027 ra_f, dec_f = _observedFromAppGeo(ra_in[ix], dec_in[ix], 

1028 obs_metadata=obs, 

1029 includeRefraction=includeRefraction) 

1030 self.assertIsInstance(ra_f, np.float) 

1031 self.assertIsInstance(dec_f, np.float) 

1032 dist_f = arcsecFromRadians(pal.dsep(ra_obs_from_app_geo[ix], 

1033 dec_obs_from_app_geo[ix], 

1034 ra_f, dec_f)) 

1035 self.assertLess(dist_f, 1.0e-9) 

1036 

1037 ra_f, dec_f = _appGeoFromObserved(ra_obs_from_app_geo[ix], 

1038 dec_obs_from_app_geo[ix], 

1039 obs_metadata=obs, 

1040 includeRefraction=includeRefraction) 

1041 self.assertIsInstance(ra_f, np.float) 

1042 self.assertIsInstance(dec_f, np.float) 

1043 dist_f = arcsecFromRadians(pal.dsep(ra_app[ix], dec_app[ix], ra_f, dec_f)) 

1044 self.assertLess(dist_f, 1.0e-9) 

1045 

1046 def test_icrsFromObserved_noRefraction(self): 

1047 """ 

1048 Test that _icrsFromObserved really does invert _observedFromICRS 

1049 in the case of no refraction. 

1050 """ 

1051 rng = np.random.RandomState(85) 

1052 n_batches = 10 

1053 n_samples = 10 

1054 for i_batch in range(n_batches): 

1055 _d_sun = 0.0 

1056 while _d_sun < 0.25*np.pi: # because ICRS -> Observed conversion breaks close to the sun 

1057 mjd = rng.random_sample(1)[0]*10000.0 + 40000.0 

1058 obs = ObservationMetaData(mjd=mjd) 

1059 ra_in = rng.random_sample(n_samples)*np.pi*2.0 

1060 dec_in = rng.random_sample(n_samples)*np.pi - 0.5*np.pi 

1061 

1062 _d_sun = _distanceToSun(ra_in, dec_in, obs.mjd).min() 

1063 

1064 ra_obs, dec_obs = _observedFromICRS(ra_in, dec_in, obs_metadata=obs, 

1065 includeRefraction=False, 

1066 epoch=2000.0) 

1067 ra_icrs, dec_icrs = _icrsFromObserved(ra_obs, dec_obs, obs_metadata=obs, 

1068 includeRefraction=False, 

1069 epoch=2000.0) 

1070 distance = pal.dsepVector(ra_in, dec_in, ra_icrs, dec_icrs) 

1071 

1072 self.assertLess(arcsecFromRadians(distance).max(), 0.01) 

1073 

1074 def test_icrsFromObservedExceptions(self): 

1075 """ 

1076 Test that _icrsFromObserved raises exceptions when it is supposed to. 

1077 """ 

1078 rng = np.random.RandomState(33) 

1079 ra_in = rng.random_sample(10) 

1080 dec_in = rng.random_sample(10) 

1081 with self.assertRaises(RuntimeError) as context: 

1082 ra_out, dec_out = _icrsFromObserved(ra_in, dec_in, epoch=2000.0) 

1083 self.assertEqual(context.exception.args[0], 

1084 "Cannot call icrsFromObserved; obs_metadata is None") 

1085 

1086 obs = ObservationMetaData(pointingRA=23.0, pointingDec=-19.0) 

1087 with self.assertRaises(RuntimeError) as context: 

1088 ra_out, dec_out = _icrsFromObserved(ra_in, dec_in, epoch=2000.0, obs_metadata=obs) 

1089 self.assertEqual(context.exception.args[0], 

1090 "Cannot call icrsFromObserved; obs_metadata.mjd is None") 

1091 

1092 obs = ObservationMetaData(pointingRA=23.0, pointingDec=-19.0, 

1093 mjd=ModifiedJulianDate(TAI=52344.0)) 

1094 with self.assertRaises(RuntimeError) as context: 

1095 ra_out, dec_out = _icrsFromObserved(ra_in, dec_in, obs_metadata=obs) 

1096 self.assertEqual(context.exception.args[0], 

1097 "Cannot call icrsFromObserved; you have not specified an epoch") 

1098 

1099 with self.assertRaises(RuntimeError) as context: 

1100 ra_out, dec_out = _icrsFromObserved(ra_in[:3], dec_in, obs_metadata=obs, epoch=2000.0) 

1101 self.assertEqual(context.exception.args[0], 

1102 "The arrays input to icrsFromObserved all need to have the same length") 

1103 

1104 def test_appGeoFromObserved(self): 

1105 """ 

1106 Test that _appGeoFromObserved really does invert _observedFromAppGeo 

1107 """ 

1108 mjd = 58350.0 

1109 site = Site(longitude=np.degrees(0.235), 

1110 latitude=np.degrees(-1.2), name='LSST') 

1111 raCenter, decCenter = raDecFromAltAz(90.0, 0.0, 

1112 ObservationMetaData(mjd=mjd, site=site)) 

1113 

1114 obs = ObservationMetaData(pointingRA=raCenter, pointingDec=decCenter, 

1115 mjd=ModifiedJulianDate(TAI=58350.0), 

1116 site=site) 

1117 

1118 rng = np.random.RandomState(125543) 

1119 nSamples = 200 

1120 

1121 # Note: the PALPY routines in question start to become inaccurate at 

1122 # a zenith distance of about 75 degrees, so we restrict our test points 

1123 # to be within 50 degrees of the telescope pointing, which is at zenith 

1124 # in a flat sky approximation 

1125 rr = rng.random_sample(nSamples) * np.radians(50.0) 

1126 theta = rng.random_sample(nSamples) * 2.0 * np.pi 

1127 ra_in = np.radians(raCenter) + rr * np.cos(theta) 

1128 dec_in = np.radians(decCenter) + rr * np.sin(theta) 

1129 

1130 xx_in = np.cos(dec_in) * np.cos(ra_in) 

1131 yy_in = np.cos(dec_in) * np.sin(ra_in) 

1132 zz_in = np.sin(dec_in) 

1133 

1134 for includeRefraction in [True, False]: 

1135 for wavelength in (0.5, 0.3, 0.7): 

1136 ra_obs, dec_obs = _observedFromAppGeo(ra_in, dec_in, obs_metadata=obs, 

1137 wavelength=wavelength, 

1138 includeRefraction=includeRefraction) 

1139 

1140 ra_out, dec_out = _appGeoFromObserved(ra_obs, dec_obs, obs_metadata=obs, 

1141 wavelength=wavelength, 

1142 includeRefraction=includeRefraction) 

1143 

1144 xx_out = np.cos(dec_out) * np.cos(ra_out) 

1145 yy_out = np.cos(dec_out) * np.sin(ra_out) 

1146 zz_out = np.sin(dec_out) 

1147 

1148 distance = np.sqrt(np.power(xx_in - xx_out, 2) + 

1149 np.power(yy_in - yy_out, 2) + 

1150 np.power(zz_in - zz_out, 2)) 

1151 

1152 self.assertLess(distance.max(), 1.0e-12) 

1153 

1154 def test_appGeoFromObservedExceptions(self): 

1155 """ 

1156 Test that _appGeoFromObserved raises exceptions where expected 

1157 """ 

1158 rng = np.random.RandomState(12) 

1159 ra_in = rng.random_sample(10) * 2.0 * np.pi 

1160 dec_in = (rng.random_sample(10) - 0.5) * np.pi 

1161 

1162 with self.assertRaises(RuntimeError) as context: 

1163 ra_out, dec_out = _appGeoFromObserved(ra_in, dec_in) 

1164 self.assertEqual(context.exception.args[0], 

1165 "Cannot call appGeoFromObserved without an obs_metadata") 

1166 

1167 obs = ObservationMetaData(pointingRA=25.0, pointingDec=-12.0, 

1168 site=None, mjd=ModifiedJulianDate(TAI=52000.0)) 

1169 

1170 with self.assertRaises(RuntimeError) as context: 

1171 ra_out, dec_out = _appGeoFromObserved( 

1172 ra_in, dec_in, obs_metadata=obs) 

1173 self.assertEqual(context.exception.args[0], 

1174 "Cannot call appGeoFromObserved: obs_metadata has no site info") 

1175 

1176 obs = ObservationMetaData(pointingRA=25.0, pointingDec=-12.0) 

1177 with self.assertRaises(RuntimeError) as context: 

1178 ra_out, dec_out = _appGeoFromObserved( 

1179 ra_in, dec_in, obs_metadata=obs) 

1180 self.assertEqual(context.exception.args[0], 

1181 "Cannot call appGeoFromObserved: obs_metadata has no mjd") 

1182 

1183 obs = ObservationMetaData(pointingRA=25.0, pointingDec=-12.0, 

1184 mjd=ModifiedJulianDate(TAI=52000.0)) 

1185 with self.assertRaises(RuntimeError) as context: 

1186 ra_out, dec_out = _appGeoFromObserved( 

1187 ra_in[:2], dec_in, obs_metadata=obs) 

1188 self.assertEqual(context.exception.args[0], 

1189 "The arrays input to appGeoFromObserved all need to have the same length") 

1190 

1191 def testRefractionCoefficients(self): 

1192 output = refractionCoefficients( 

1193 wavelength=5000.0, site=self.obs_metadata.site) 

1194 

1195 self.assertAlmostEqual(output[0], 2.295817926320665320e-04, 6) 

1196 self.assertAlmostEqual(output[1], -2.385964632924575670e-07, 6) 

1197 

1198 def testApplyRefraction(self): 

1199 coeffs = refractionCoefficients( 

1200 wavelength=5000.0, site=self.obs_metadata.site) 

1201 

1202 output = applyRefraction(0.25 * np.pi, coeffs[0], coeffs[1]) 

1203 

1204 self.assertAlmostEqual(output, 7.851689251070859132e-01, 6) 

1205 

1206 # test that passing in a numpy array and passing in floats 

1207 # give the same results 

1208 rng = np.random.RandomState(712) 

1209 zd_arr = rng.random_sample(20) * np.pi * 0.2 

1210 control_refraction = applyRefraction(zd_arr, coeffs[0], coeffs[1]) 

1211 for ix, zz in enumerate(zd_arr): 

1212 test_refraction = applyRefraction(zz, coeffs[0], coeffs[1]) 

1213 self.assertAlmostEqual(test_refraction, control_refraction[ix], 12) 

1214 

1215 def test_applyProperMotion_vs_icrs(self): 

1216 """ 

1217 test that running: 

1218 applyProperMotion() -> observedFromICRS(pm=0) 

1219 gives the same results as running 

1220 observedFromICRS(pm!=0) 

1221 """ 

1222 rng = np.random.RandomState(18293) 

1223 n_obj = 500 

1224 ra = 46.2 

1225 dec = -14.2 

1226 

1227 # generate a set of points uniformly distributed on the 

1228 # unit sphere 

1229 xyz_list = rng.normal(loc=0.0, scale=1.0, size=(n_obj, 3)) 

1230 ra_list, dec_list = sphericalFromCartesian(xyz_list) 

1231 self.assertEqual(len(ra_list), n_obj) 

1232 ra_list = np.degrees(ra_list) 

1233 dec_list = np.degrees(dec_list) 

1234 

1235 px_list = np.array([0.2]*n_obj) 

1236 vrad_list = np.array([200.0]*n_obj) 

1237 pm_ra_list = np.array([30.0]*n_obj) 

1238 pm_dec_list = np.array([-30.0]*n_obj) 

1239 

1240 obs = ObservationMetaData(pointingRA=ra, pointingDec=dec, 

1241 mjd=60123.0) 

1242 

1243 for includeRefraction in (True, False): 

1244 ra_control, dec_control = observedFromICRS(ra_list, dec_list, 

1245 pm_ra=pm_ra_list, pm_dec=pm_dec_list, 

1246 v_rad=vrad_list, parallax=px_list, obs_metadata=obs, 

1247 epoch=2000.0, includeRefraction=includeRefraction) 

1248 

1249 ra_pm, dec_pm = applyProperMotion(ra_list, dec_list, pm_ra_list, pm_dec_list, 

1250 parallax=px_list, v_rad=vrad_list, mjd=obs.mjd, epoch=2000.0) 

1251 

1252 ra_test, dec_test = observedFromICRS(ra_pm, dec_pm, parallax=px_list, v_rad=vrad_list, 

1253 obs_metadata=obs, epoch=2000.0, 

1254 includeRefraction=includeRefraction) 

1255 

1256 # the distance between the test points and the control points 

1257 dd = arcsecFromRadians(haversine(np.radians(ra_test), np.radians(dec_test), 

1258 np.radians(ra_control), np.radians(dec_control))) 

1259 

1260 self.assertLess(dd.max(), 0.005) 

1261 

1262 # the distance between the origina points and the motion-propagated points 

1263 dd_bad = arcsecFromRadians(haversine(np.radians(ra_control), np.radians(dec_control), 

1264 np.radians(ra_list), np.radians(dec_list))) 

1265 

1266 self.assertGreater(dd_bad.min(), 10.0) 

1267 

1268 

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

1270 pass 

1271 

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

1273 lsst.utils.tests.init() 

1274 unittest.main()