Coverage for tests/testAstrometry.py : 7%

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).
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)
14"""
16from __future__ import with_statement
17from __future__ import division
18from builtins import zip
19from builtins import range
21import numpy as np
23import unittest
24import palpy as pal
25import lsst.utils.tests
27from lsst.sims.utils import ObservationMetaData
28from lsst.sims.utils import _getRotTelPos, _raDecFromAltAz, \
29 radiansFromArcsec, arcsecFromRadians, Site, \
30 raDecFromAltAz, haversine, ModifiedJulianDate, \
31 _getRotSkyPos, _angularSeparation
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
41# Tell astropy not to download the IERS tables
42from astropy.utils import iers
43iers.conf.auto_download=False
45def setup_module(module):
46 lsst.utils.tests.init()
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)
61 radius = 0.1
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)
70 return obs_metadata
73def makeRandomSample(raCenter=None, decCenter=None, radius=None):
74 # create a random sample of object data
76 nsamples = 100
77 rng = np.random.RandomState(32)
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)
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
93 return ra, dec, pm_ra, pm_dec, parallax, v_rad
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 """
105 def setUp(self):
106 self.metadata = {}
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)
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)
128 self.tol = 1.0e-5
130 def tearDown(self):
131 del self.obs_metadata
132 del self.metadata
133 del self.tol
135 def testDistanceToSun(self):
136 """
137 Test _distanceToSun using solar RA, Dec calculated from
139 http://aa.usno.navy.mil/data/docs/JulianDate.php
140 http://aa.usno.navy.mil/data/docs/geocentric.php
141 """
143 hour = np.radians(360.0 / 24.0)
144 minute = hour / 60.0
145 second = minute / 60.0
147 mjd_list = [57026.0, 57543.625]
149 sun_ra_list = [18.0 * hour + 56.0 * minute + 51.022 * second,
150 4.0 * hour + 51.0 * minute + 22.776 * second]
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)]
155 for raS, decS, tai in zip(sun_ra_list, sun_dec_list, mjd_list):
157 mjd = ModifiedJulianDate(TAI=tai)
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)
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)
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):
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
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))
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)
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)
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)
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)
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)
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)
223 def testDistanceToSunDeg(self):
224 """
225 Test that distanceToSun is consistent with _distanceToSun
226 """
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)
234 def testSolarRaDecDeg(self):
235 """
236 Test that solarRaDec is consistent with _solarRaDec
237 """
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)
246 def testDistanceToSunArray(self):
247 """
248 Test _distanceToSun on numpy arrays of RA, Dec using solar RA, Dec calculated from
250 http://aa.usno.navy.mil/data/docs/JulianDate.php
251 http://aa.usno.navy.mil/data/docs/geocentric.php
252 """
254 rng = np.random.RandomState(77)
255 nStars = 100
257 hour = np.radians(360.0 / 24.0)
258 minute = hour / 60.0
259 second = minute / 60.0
261 mjd_list = [57026.0, 57543.625]
263 sun_ra_list = [18.0 * hour + 56.0 * minute + 51.022 * second,
264 4.0 * hour + 51.0 * minute + 22.776 * second]
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)]
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)
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()
286 raShort = np.array([1.0])
287 decShort = np.array([1.0])
289 # test refractionCoefficients
290 self.assertRaises(RuntimeError, refractionCoefficients)
291 site = obs_metadata.site
292 x, y = refractionCoefficients(site=site)
294 # test applyRefraction
295 zd = 0.1
296 applyRefraction(zd, x, y)
298 zd = [0.1, 0.2]
299 self.assertRaises(RuntimeError, applyRefraction, zd, x, y)
301 zd = np.array([0.1, 0.2])
302 applyRefraction(zd, x, y)
304 # test _applyPrecession
305 # test without mjd
306 self.assertRaises(RuntimeError, _applyPrecession, ra, dec)
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))
314 # test that it runs
315 _applyPrecession(ra, dec, mjd=ModifiedJulianDate(TAI=52000.0))
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)
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]])
330 # test without mjd
331 self.assertRaises(RuntimeError, _applyProperMotion,
332 ra, dec, pm_ra, pm_dec, parallax, v_rad)
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))
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))
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))
380 # test _appGeoFromICRS
381 # test without mjd
382 self.assertRaises(RuntimeError, _appGeoFromICRS, ra, dec)
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))
390 # test that it actually urns
391 _appGeoFromICRS(ra, dec, mjd=obs_metadata.mjd)
393 # test _observedFromAppGeo
394 # test without obs_metadata
395 self.assertRaises(RuntimeError, _observedFromAppGeo, ra, dec)
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)
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)
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'))
418 self.assertRaises(RuntimeError, _observedFromAppGeo,
419 ra, decShort, obs_metadata=dummy)
420 self.assertRaises(RuntimeError, _observedFromAppGeo,
421 raShort, dec, obs_metadata=dummy)
423 # test that it actually runs
424 _observedFromAppGeo(ra, dec, obs_metadata=dummy)
426 # test _observedFromICRS
427 # test without epoch
428 self.assertRaises(RuntimeError, _observedFromICRS,
429 ra, dec, obs_metadata=obs_metadata)
431 # test without obs_metadata
432 self.assertRaises(RuntimeError, _observedFromICRS,
433 ra, dec, epoch=2000.0)
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)
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)
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)
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)
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)
469 def test_applyPrecession(self):
471 ra = np.zeros((3), dtype=float)
472 dec = np.zeros((3), dtype=float)
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
481 self.assertRaises(RuntimeError, _applyPrecession, ra, dec)
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)
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)
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
506 rng = np.random.RandomState(18)
507 nSamples = 1000
509 mjdList = rng.random_sample(20) * 20000.0 + 45000.0
511 for mjd in mjdList:
513 raList_icrs = rng.random_sample(nSamples) * 2.0 * np.pi
514 decList_icrs = (rng.random_sample(nSamples) - 0.5) * np.pi
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)
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
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))
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)
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]])
541 pxr = px * pal_das2r
543 w = VF * v_rad * pxr
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])
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]])
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))
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
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))
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))
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]))
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)
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)
603 ra_icrs = rng.random_sample(nSamples) * 2.0 * np.pi
604 dec_icrs = (rng.random_sample(nSamples) - 0.5) * np.pi
606 ra_arr, dec_arr = _applyProperMotion(ra_icrs, dec_icrs,
607 pm_ra, pm_dec, px, v_rad, mjd=mjd)
609 self.assertIsInstance(ra_arr, np.ndarray)
610 self.assertIsInstance(dec_arr, np.ndarray)
612 for ix, (rr, dd, mura, mudec, xx, vv) in \
613 enumerate(zip(ra_icrs, dec_icrs, pm_ra, pm_dec, px, v_rad)):
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)
623 def test_appGeoFromICRS(self):
624 """
625 Test conversion between ICRS RA, Dec and apparent geocentric ICRS.
627 Apparent, geocentric RA, Dec of objects will be taken from this website
629 http://aa.usno.navy.mil/data/docs/geocentric.php
631 dates converted to JD using this website
633 http://aa.usno.navy.mil/data/docs/geocentric.php
635 """
637 hours = np.radians(360.0 / 24.0)
638 minutes = hours / 60.0
639 seconds = minutes / 60.0
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)
651 mjd_list = []
652 ra_app_list = []
653 dec_app_list = []
655 # jd (UT)
656 jd = 2457000.375000
657 mjd = jd - 2400000.5
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))
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))
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))
675 for mjd, ra_app, dec_app in zip(mjd_list, ra_app_list, dec_app_list):
676 obs = ObservationMetaData(mjd=mjd)
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)
686 self.assertIsInstance(ra_test, np.float)
687 self.assertIsInstance(dec_test, np.float)
689 distance = arcsecFromRadians(
690 haversine(ra_app, dec_app, ra_test, dec_test))
691 self.assertLess(distance, 0.1)
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
703 mjd_list = []
704 ra_app_list = []
705 dec_app_list = []
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))
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))
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))
722 for mjd, ra_app, dec_app in zip(mjd_list, ra_app_list, dec_app_list):
723 obs = ObservationMetaData(mjd=mjd)
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)
733 self.assertIsInstance(ra_test, np.float)
734 self.assertIsInstance(dec_test, np.float)
736 distance = arcsecFromRadians(
737 haversine(ra_app, dec_app, ra_test, dec_test))
738 self.assertLess(distance, 0.1)
740 def test_appGeoFromICRS_inputs(self):
741 """
742 Test that appGeoFromICRS behaves as expected when given both numpy
743 array and float inputs.
744 """
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)
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)
762 self.assertIsInstance(ra_control, np.ndarray)
763 self.assertIsInstance(dec_control, np.ndarray)
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)
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)
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])
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)
788 self.assertIn("The input arguments:", context.exception.args[0])
789 self.assertIn("dec", context.exception.args[0])
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)
795 self.assertIn("The input arguments:", context.exception.args[0])
796 self.assertIn("pm_ra", context.exception.args[0])
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)
802 self.assertIn("The input arguments:", context.exception.args[0])
803 self.assertIn("pm_dec", context.exception.args[0])
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)
809 self.assertIn("The input arguments:", context.exception.args[0])
810 self.assertIn("parallax", context.exception.args[0])
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)
816 self.assertIn("The input arguments:", context.exception.args[0])
817 self.assertIn("v_rad", context.exception.args[0])
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)
823 self.assertEqual(context.exception.args[0],
824 "The arrays input to appGeoFromICRS all need to "
825 "have the same length")
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)
831 self.assertEqual(context.exception.args[0],
832 "The arrays input to appGeoFromICRS all need to "
833 "have the same length")
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)
839 self.assertEqual(context.exception.args[0],
840 "The arrays input to appGeoFromICRS all need to "
841 "have the same length")
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)
847 self.assertEqual(context.exception.args[0],
848 "The arrays input to appGeoFromICRS all need to "
849 "have the same length")
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)
855 self.assertEqual(context.exception.args[0],
856 "The arrays input to appGeoFromICRS all need to "
857 "have the same length")
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)
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)
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)
882 test_ra, test_dec = _appGeoFromICRS(ra_list, dec_list,
883 mjd=obs.mjd, epoch=2000.0)
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)
889 dd = _angularSeparation(test_ra[valid], test_dec[valid],
890 control_ra[valid], control_dec[valid])
891 self.assertLess(arcsecFromRadians(dd).max(), 0.005)
893 def test_icrsFromAppGeo(self):
894 """
895 Test that _icrsFromAppGeo really inverts _appGeoFromICRS.
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.
902 _icrsFromAppGeo should invert _appGeoFromICRS to within
903 0.01 arcsec at an angular distance greater than 45 degrees
904 from the sun.
905 """
907 rng = np.random.RandomState(412)
908 nSamples = 100
910 for tai in (53000.0, 53241.6, 58504.6):
912 mjd = ModifiedJulianDate(TAI=tai)
914 ra_in = rng.random_sample(nSamples) * 2.0 * np.pi
915 dec_in = (rng.random_sample(nSamples) - 0.5) * np.pi
917 ra_app, dec_app = _appGeoFromICRS(ra_in, dec_in, mjd=mjd)
919 ra_icrs, dec_icrs = _icrsFromAppGeo(ra_app, dec_app,
920 epoch=2000.0, mjd=mjd)
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')
925 valid_pts = np.where(_distanceToSun(
926 ra_in, dec_in, mjd) > 0.25 * np.pi)[0]
928 self.assertGreater(len(valid_pts), 0)
930 distance = arcsecFromRadians(pal.dsepVector(ra_in[valid_pts], dec_in[valid_pts],
931 ra_icrs[valid_pts], dec_icrs[valid_pts]))
933 self.assertLess(distance.max(), 0.01)
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)
945 def test_icrsFromObserved(self):
946 """
947 Test that _icrsFromObserved really inverts _observedFromICRS and that
948 _appGeoFromObserved really does invert _observedFromAppGeo.
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 """
954 rng = np.random.RandomState(412)
955 nSamples = 100
957 site = Site(name='LSST')
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):
964 obs = ObservationMetaData(mjd=tai, site=site)
966 raZenith, decZenith = _raDecFromAltAz(
967 0.5 * np.pi, 0.0, obs)
969 obs = ObservationMetaData(pointingRA=raPointing, pointingDec=decPointing,
970 mjd=tai, site=site)
972 rr = rng.random_sample(
973 nSamples) * np.radians(50.0)
974 theta = rng.random_sample(nSamples) * 2.0 * np.pi
976 ra_in = raZenith + rr * np.cos(theta)
977 dec_in = decZenith + rr * np.sin(theta)
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)
985 ra_icrs, dec_icrs = _icrsFromObserved(ra_obs, dec_obs, obs_metadata=obs,
986 includeRefraction=includeRefraction,
987 epoch=2000.0)
989 valid_pts = np.where(_distanceToSun(
990 ra_in, dec_in, obs.mjd) > 0.25 * np.pi)[0]
992 self.assertGreater(len(valid_pts), 0)
994 distance = arcsecFromRadians(pal.dsepVector(ra_in[valid_pts], dec_in[valid_pts],
995 ra_icrs[valid_pts], dec_icrs[valid_pts]))
997 self.assertLess(distance.max(), 1.0e-6)
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)
1005 ra_app, dec_app = _appGeoFromObserved(ra_obs_from_app_geo,
1006 dec_obs_from_app_geo,
1007 obs_metadata=obs,
1008 includeRefraction=includeRefraction)
1010 distance = arcsecFromRadians(pal.dsepVector(ra_in[valid_pts], dec_in[valid_pts],
1011 ra_app[valid_pts], dec_app[valid_pts]))
1013 self.assertLess(distance.max(), 1.0e-6)
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)
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)
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)
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
1062 _d_sun = _distanceToSun(ra_in, dec_in, obs.mjd).min()
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)
1072 self.assertLess(arcsecFromRadians(distance).max(), 0.01)
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")
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")
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")
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")
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))
1114 obs = ObservationMetaData(pointingRA=raCenter, pointingDec=decCenter,
1115 mjd=ModifiedJulianDate(TAI=58350.0),
1116 site=site)
1118 rng = np.random.RandomState(125543)
1119 nSamples = 200
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)
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)
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)
1140 ra_out, dec_out = _appGeoFromObserved(ra_obs, dec_obs, obs_metadata=obs,
1141 wavelength=wavelength,
1142 includeRefraction=includeRefraction)
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)
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))
1152 self.assertLess(distance.max(), 1.0e-12)
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
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")
1167 obs = ObservationMetaData(pointingRA=25.0, pointingDec=-12.0,
1168 site=None, mjd=ModifiedJulianDate(TAI=52000.0))
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")
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")
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")
1191 def testRefractionCoefficients(self):
1192 output = refractionCoefficients(
1193 wavelength=5000.0, site=self.obs_metadata.site)
1195 self.assertAlmostEqual(output[0], 2.295817926320665320e-04, 6)
1196 self.assertAlmostEqual(output[1], -2.385964632924575670e-07, 6)
1198 def testApplyRefraction(self):
1199 coeffs = refractionCoefficients(
1200 wavelength=5000.0, site=self.obs_metadata.site)
1202 output = applyRefraction(0.25 * np.pi, coeffs[0], coeffs[1])
1204 self.assertAlmostEqual(output, 7.851689251070859132e-01, 6)
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)
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
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)
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)
1240 obs = ObservationMetaData(pointingRA=ra, pointingDec=dec,
1241 mjd=60123.0)
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)
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)
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)
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)))
1260 self.assertLess(dd.max(), 0.005)
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)))
1266 self.assertGreater(dd_bad.min(), 10.0)
1269class MemoryTestClass(lsst.utils.tests.MemoryTestCase):
1270 pass
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()