[Astropy Basics]
[Astroquery]
[Photutils]
[Pyephem/Astroplan]
In [ ]:
%matplotlib inline
import numpy as np
import math
import matplotlib.pyplot as plt
import seaborn
plt.rcParams['figure.figsize'] = (12, 8)
plt.rcParams['font.size'] = 14
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['xtick.labelsize'] = 13
plt.rcParams['ytick.labelsize'] = 13
plt.rcParams['axes.titlesize'] = 14
plt.rcParams['legend.fontsize'] = 13
In [ ]:
from astropy import constants as const
from astropy import units as u
By default astropy constants uses S.I. units...
In [ ]:
print(const.c)
It can be transformed to any units...
In [ ]:
const.c.to('km/s')
In [ ]:
const.c.to('pc/yr')
You can also define your own constant using astropy Units
In [ ]:
my_emission_line_flux = 12.32 * u.erg / u.cm ** 2 / u.s
my_emission_line_flux
Here we can compute earth's orbit speed using astropy constants...
In [ ]:
speed_of_earth = const.au * 2 * math.pi / u.yr
speed_of_earth.to('km/s')
In [ ]:
# %load -r 2-3 solutions/10_Astronomy.py
The simplest coordinate we can define is a single point in the sky, by default in the ICRS frame.
In [ ]:
from astropy.coordinates import SkyCoord
c = SkyCoord(ra=10.625*u.degree, dec=41.2*u.degree, frame='icrs')
In [ ]:
c = SkyCoord(10.625, 41.2, frame='icrs', unit='deg')
c = SkyCoord('00h42m30s', '+41d12m00s', frame='icrs')
c = SkyCoord('00h42.5m', '+41d12m')
c = SkyCoord('00 42 30 +41 12 00', unit=(u.hourangle, u.deg))
c = SkyCoord('00:42.5 +41:12', unit=(u.hourangle, u.deg))
c
Astropy also has a significantly large list of sources than can be retrieved by its name:
In [ ]:
a_big_blue_star = SkyCoord.from_name('rigel')
print (a_big_blue_star.ra, a_big_blue_star.dec)
In [ ]:
c.galactic
Or even get what is the closest constellation to the object, very useful for astronomers as you know...
In [ ]:
c.get_constellation()
In [ ]:
c = SkyCoord(ra=10.68458*u.degree, dec=41.26917*u.degree, distance=770*u.kpc)
print (c.cartesian.x, c.cartesian.y, c.cartesian.z)
If we define one or more coordinates we can compute the distance between the two objects:
In [ ]:
c1 = SkyCoord(ra=10*u.degree, dec=9*u.degree, distance=10*u.pc, frame='icrs')
c2 = SkyCoord(ra=11*u.degree, dec=10*u.degree, distance=11.5*u.pc, frame='icrs')
print ("Angular Separation: %s" % c1.separation(c2))
print ("Distance between objects: %s" % c1.separation_3d(c2))
In [ ]:
ras = np.array([0-.7, 21.5, 120.9]) * u.deg
decs = np.array([4.5, -5.2, 6.3]) * u.deg
catalogue = SkyCoord(ras, decs, frame='icrs')
catalogue.galactic
In [ ]:
from astropy.time import Time
times = ['2017-09-13T00:00:00', '2017-09-15T11:20:15.123456789',]
t1 = Time(times)
t1
Default format is ISOT and scale UTC, but it can be set to others.
In [ ]:
times = [58009, 58011.47239726]
t2 = Time(times, format='mjd', scale='tai')
t2
In [ ]:
print ("To julian date: %s" % t1[0].jd)
print ("To modified julian date: %s" % t1[0].mjd)
print ("To FITS: %s" % t1[0].fits)
print ("To GPS: %s" % t1[0].gps)
print ("To Bessel Epoch Year: %s" % t1[0].byear_str)
print ("To Julian Epoch Year: %s" % t1[0].jyear_str)
When a Time object is constructed from a timezone-aware datetime, no timezone information is saved in the Time object. However, Time objects can be converted to timezone-aware datetime objects:
In [ ]:
from datetime import datetime
from astropy.time import Time, TimezoneInfo
import astropy.units as u
utc_plus_one_hour = TimezoneInfo(utc_offset=1*u.hour)
dt_aware = datetime(2000, 1, 1, 0, 0, 0, tzinfo=utc_plus_one_hour)
t = Time(dt_aware) # Loses timezone info, converts to UTC
print(t) # will return UTC
In [ ]:
print(t.to_datetime(timezone=utc_plus_one_hour)) # to timezone-aware datetime
In [ ]:
from astropy.io import fits
gaia_hdulist = fits.open('../resources/cosmohub_catalogue.fits')
gaia_hdulist.info()
To access the primary HDU, we can directly call it by its name, and preview the header like this:
In [ ]:
gaia_hdulist['PRIMARY'].header
As the second extension has no name, we can access to it from its index:
In [ ]:
gaia_hdulist[1].header
The data is contained in the Binary table and can be accessed very similarly to a numpy/pandas table:
In [ ]:
plt.scatter(gaia_hdulist[1].data['ra'], gaia_hdulist[1].data['dec'])
plt.xlabel('Right Ascension (deg)')
plt.ylabel('Declination (deg)')
Even FITS is widely used in astronomy, there are a several other widely used formats for storing table and catalogue data. The module astropy.io.ascii provides read capability for most of them. Find the list of supported formats in astropy's documentation: http://docs.astropy.org/en/stable/io/ascii/index.html#supported-formats
In [ ]:
from astropy.io import ascii
data = ascii.read("../resources/sources.dat")
print(data)
The read method tries to identify the file format automatically, but it can be specified in the format input parameter:
In [ ]:
data = ascii.read("../resources/sources.csv", format='csv')
print(data)
Any catalogue can then be exported (in this case to screen) to any format:
In [ ]:
import sys
ascii.write(data, sys.stdout, format='latex')
In [ ]:
sdss_qso_hdulist = fits.open('../resources/sdss_qso_spec-0501-52235-0313.fits')
sdss_qso_hdulist.info()
The coadd seems to have the coadded spectra from several observations.
Let's now inspect what columns we get in the spectra:
In [ ]:
sdss_qso_hdulist['COADD'].columns
We can now have a look at the spectra data itself, using a scatter plot.
In [ ]:
plt.scatter(10**sdss_qso_hdulist['COADD'].data['loglam'], sdss_qso_hdulist['COADD'].data['flux'], s=2)
plt.xlabel('Wavelengths (Angstroms)')
plt.ylabel(r'f$\lambda$ (erg/s/cm2/A)')
The previous spectra seems to have some bad measurements, but we can make use of the OR mask included to discard those measurements.
To better visualize the spectra file we will apply a gaussian filtering...
In [ ]:
from scipy.ndimage.filters import gaussian_filter
from scipy.interpolate import interp1d
y_values_masked = np.ma.masked_where(sdss_qso_hdulist['COADD'].data['or_mask'],
sdss_qso_hdulist['COADD'].data['flux'])
x_values_masked = np.ma.masked_where(sdss_qso_hdulist['COADD'].data['or_mask'],
sdss_qso_hdulist['COADD'].data['loglam'])
plt.scatter(10**x_values_masked, y_values_masked, s=2, label='masked')
plt.plot(10**sdss_qso_hdulist['COADD'].data['loglam'], gaussian_filter(y_values_masked, sigma=16),
color='orange', linewidth=3, label='masked and filtered')
plt.xlabel('Wavelengths (Angstroms)')
plt.ylabel(r'f$\lambda$ (erg/s/cm2/A)')
plt.legend()
Another information that is included in this spectra file is the emission lines measured by SDSS. We can inspect the columns of that extension:
In [ ]:
sdss_qso_hdulist['SPZLINE'].data.columns
In [ ]:
# %load -r 7-18 solutions/10_Astronomy.py
In [ ]:
hst_hdulist = fits.open('../resources/hst_656nmos.fits')
hst_hdulist.info()
In [ ]:
plt.imshow(hst_hdulist['PRIMARY'].data)
plt.xlabel('X pixels')
plt.ylabel('Y pixels')
plt.colorbar()
In [ ]:
from astropy.visualization import ZScaleInterval
norm = ZScaleInterval()
vmin, vmax = norm.get_limits(hst_hdulist['PRIMARY'].data)
plt.imshow(hst_hdulist[0].data, vmin=vmin, vmax=vmax, interpolation='none', origin='lower')
plt.xlabel('X pixels')
plt.ylabel('Y pixels')
plt.colorbar()
The image header contains the World Coordinate System (WCS) information, stored in a set of keywords (CD, CRVAL, CRPIX and optionally some distortion parameters). The WCS provides the projection of the image in the sky, allowing to work with pixels and sky coordinates.
In [ ]:
print ("WCS projection type:")
print (hst_hdulist['PRIMARY'].header['CTYPE1'])
print (hst_hdulist['PRIMARY'].header['CTYPE2'])
print ("WCS reference values:")
print (hst_hdulist['PRIMARY'].header['CRVAL1'])
print (hst_hdulist['PRIMARY'].header['CRVAL2'])
print ("WCS reference pixel:")
print (hst_hdulist['PRIMARY'].header['CRPIX1'])
print (hst_hdulist['PRIMARY'].header['CRPIX2'])
print ("WCS pixel to sky matrix:")
print (hst_hdulist['PRIMARY'].header['CD1_1'])
print (hst_hdulist['PRIMARY'].header['CD1_2'])
print (hst_hdulist['PRIMARY'].header['CD2_1'])
print (hst_hdulist['PRIMARY'].header['CD2_2'])
We can load the WCS of the image directly like this:
In [ ]:
from astropy import wcs
hst_image_wcs = wcs.WCS(hst_hdulist['PRIMARY'].header)
hst_image_wcs.printwcs()
Once loaded the WCS, we can retrieve the corners of the image footprint:
In [ ]:
hst_image_wcs.calc_footprint()
We can infer its pixel scale from the CD matrix:
In [ ]:
hst_pixelscale = np.mean(wcs.utils.proj_plane_pixel_scales(hst_image_wcs) * u.degree).to('arcsec')
hst_pixelscale
It is also useful to know the coordinates of a specific pixel in the image:
In [ ]:
# Origin of the pixel coordinates convention:
# Set 0 when first pixel is 0 (c/python-like)
# Set 1 when first pixel is 1 (fortran-like)
origin = 0
# convert the pixels
lon, lat = hst_image_wcs.all_pix2world(20, 30, origin)
print (lon, lat)
In the same way, sky coordinates can be transformed to pixel positions in the image.
In [ ]:
x, y = hst_image_wcs.all_world2pix(lon, lat, origin)
print (x, y)
Note that the function we used is called all_XXXXXX. This is the method to use all distortion information (such as SIP, TPV,...). To use only the WCS without the distortion, use the equivalent method wcs_XXXXXXX.
In [ ]:
# %load -r 22-38 solutions/10_Astronomy.py
X and Y in the plot correspond to the X and Y of the image. WCS axes allows you to plot sky coordinates without remapping the image pixels:
In [ ]:
ax = plt.subplot(projection=hst_image_wcs)
ax.imshow(hst_hdulist[0].data, vmin=vmin, vmax=vmax, origin='lower')
overlay = ax.get_coords_overlay('fk5')
overlay.grid(color='white', ls='dotted')
overlay[0].set_axislabel('Right Ascension (J2000)')
overlay[1].set_axislabel('Declination (J2000)')