In [1]:
import numpy as np
import matplotlib.pyplot as plt
from astropy.coordinates import SkyCoord
import astropy.units as u
from mocpy import MOC
from tqdm import tqdm
import pandas as pd
from astropy.io import fits
from astropy.wcs import WCS
import os
Speeding up Butter¶
Run times are shown below each code cell.
In [ ]:
skycell_csv = "./data/SkyCells/skycell_coordinates.csv"
skycell_df = pd.read_csv(skycell_csv)
In [3]:
def angular_distance(ra1, dec1, ra2, dec2):
"""
Angular distance between two points on the sky
"""
d1 = np.sin(np.radians(dec1)) * np.sin(np.radians(dec2))
d2 = np.cos(np.radians(dec1)) * np.cos(np.radians(dec2)) * np.cos(np.radians(ra1 - ra2))
return np.degrees(np.arccos(d1 + d2))
def max_radius(ra1, dec1, ra2, dec2):
"""
Maximum angular distance between two points on the sky
"""
return np.nanmax(angular_distance(ra1, dec1, ra2, dec2))
In [ ]:
# def _image1(self, im1_file):
# """
# Opens the TESS image and extracts the necessary information
# """
im1_file = "../data/tess/20_3_3/tess2020019135923-s0020-3-3-0165-s_ffic.fits"
hdul = fits.open(im1_file)
super_data = hdul[1].data
data_shape = np.shape(super_data)
super_wcs = WCS(hdul[1].header)
im1_poly = super_wcs.calc_footprint()
hdul.close()
ra_centre, dec_centre = super_wcs.all_pix2world(data_shape[1]/2, data_shape[0]/2, 0)
WARNING: FITSFixedWarning: 'datfix' made the change 'Set MJD-OBS to 58867.584712 from DATE-OBS. Set MJD-END to 58867.605545 from DATE-END'. [astropy.wcs.wcs]
In [ ]:
# def _ravelling(self):
# """
# Unravels the TESS image to get the pixel coordinates
# """
t_y, t_x = data_shape
_ty, _tx = np.mgrid[:t_y, :t_x]
ty_input = _ty.ravel()
tx_input = _tx.ravel()
tpix_coord_input = np.column_stack([ty_input, tx_input])
_x_im1 = tpix_coord_input[:,1]
_y_im1 = tpix_coord_input[:,0]
_ra_im1, _dec_im1 = super_wcs.all_pix2world(_x_im1, _y_im1, 0)
ravelled_indices = np.array([(i, j) for i in range(data_shape[0]) for j in range(data_shape[1])])
ravelled_index = np.arange(data_shape[0] * data_shape[1])
1.6s
In [ ]:
ra_corners = im1_poly[:, 0]
dec_corners = im1_poly[:, 1]
radius_var = max_radius(ra_centre, dec_centre, ra_corners, dec_corners) + np.sqrt(2) * 0.4
radius = angular_distance(ra_centre, dec_centre, skycell_df['RA'].to_numpy(), skycell_df['DEC'].to_numpy())
mask = radius < radius_var
skycells_list = skycell_df[mask].Name.to_numpy()
skycells_id = np.arange(len(skycells_list))
0.0s
In [ ]:
skycell_wcs_csv = "./data/SkyCells/skycell_wcs.csv"
skycell_wcs_df = pd.read_csv(skycell_wcs_csv)
In [8]:
# i = 22
i = 915
skycell = skycells_list[i]
skycell_index = skycells_id[i]
buffer = 120
In [9]:
skycell, skycell_index
Out[9]:
('skycell.2589.099', 915)
In [10]:
def ps1_wcs_function(skycell):
"""
Grabs and stores the WCS information for the PanSTARRS1 images
"""
other_temp = skycell_wcs_df[skycell_wcs_df['NAME'] == skycell].reset_index(drop=True)
records = other_temp.to_dict(orient='records')
header_dict = {k: v for d in records for k, v in d.items()}
temp_head = fits.Header(header_dict)
ps1_data_shape = (other_temp['NAXIS2'].iloc[0], other_temp['NAXIS1'].iloc[0])
temp_wcs = WCS(temp_head)
# header_dicts.append(temp_head)
# ps1_wcs_master.append(temp_wcs)
return temp_wcs, ps1_data_shape
In [ ]:
# def _ps1_image(skycell):
# """
# Grabs the PanSTARRS1 skycells information for extraction
# """
# other_temp = skycell_wcs_df[skycell_wcs_df['NAME'] == skycell].reset_index(drop=True)
# ps1_data_shape = (other_temp['NAXIS2'].iloc[0], other_temp['NAXIS1'].iloc[0])
# temp_ind = complete_skycells[complete_skycells['Name'] == skycell].index[0]
# ps1_wcs = ps1_wcs_master[temp_ind]
ps1_wcs, ps1_data_shape = ps1_wcs_function(skycell)
corns = np.array([[buffer, buffer], [ps1_data_shape[1] - buffer, buffer],
[ps1_data_shape[1] - buffer, ps1_data_shape[0] - buffer],
[buffer, ps1_data_shape[0] - buffer]])
ra_corners_ps1, dec_corners_ps1 = ps1_wcs.all_pix2world(corns[:, 0], corns[:, 1], 0)
min_ps1_ra = np.min(ra_corners_ps1)
max_ps1_ra = np.max(ra_corners_ps1)
min_ps1_dec = np.min(dec_corners_ps1)
max_ps1_dec = np.max(dec_corners_ps1)
ps1_poly = np.column_stack((ra_corners_ps1, dec_corners_ps1))
0.0s
In [ ]:
# def _pixel_vertices(self):
# """
# Grab the pixel vertices for the TESS image
# """
pixel_vertices = []
_ra, _dec = super_wcs.all_pix2world(tpix_coord_input[:, 1], tpix_coord_input[:, 0], 0)
mask = (_ra >= min_ps1_ra) & (_ra < max_ps1_ra) & (_dec >= min_ps1_dec) & (_dec < max_ps1_dec)
filtered_indices = np.where(mask)[0]
pix_centers = np.column_stack((_ra[filtered_indices], _dec[filtered_indices]))
pix_center_ra = _ra[filtered_indices]
pix_center_dec = _dec[filtered_indices]
im1_indices = ravelled_indices[filtered_indices]
im1_index = ravelled_index[filtered_indices]
for c in tpix_coord_input[filtered_indices]:
x = c[1]
y = c[0]
upper_left = (x - 0.5, y - 0.5)
upper_right = (x + 0.5, y - 0.5)
lower_right = (x + 0.5, y + 0.5)
lower_left = (x - 0.5, y + 0.5)
t_poly = super_wcs.all_pix2world([upper_left, upper_right, lower_right, lower_left], 0)
pixel_vertices.append(t_poly)
0.6s
In [13]:
coords = tpix_coord_input[filtered_indices]
x_coords = coords[:, 1]
y_coords = coords[:, 0]
corners = np.array([
np.column_stack([x_coords - 0.5, y_coords - 0.5]), # upper_left
np.column_stack([x_coords + 0.5, y_coords - 0.5]), # upper_right
np.column_stack([x_coords + 0.5, y_coords + 0.5]), # lower_right
np.column_stack([x_coords - 0.5, y_coords + 0.5]) # lower_left
])
corners_reshaped = corners.transpose(1, 0, 2).reshape(-1, 2)
world_coords = super_wcs.all_pix2world(corners_reshaped, 0)
pixel_vertices_vectorized = world_coords.reshape(len(filtered_indices), 4, 2)
In [14]:
np.sum(np.array(pixel_vertices) != pixel_vertices_vectorized)
Out[14]:
0
In [15]:
# def _ps1_ravelling(self):
# """
# PanSTARRS1 image unravelling
# """
p_y, p_x = ps1_data_shape
py, px = np.mgrid[:p_y, :p_x]
py_input = py.ravel()
px_input = px.ravel()
ppix_coord_input = np.column_stack((py_input, px_input))
x2 = ppix_coord_input[:,1]
y2 = ppix_coord_input[:,0]
_ra2, _dec2 = ps1_wcs.all_pix2world(x2, y2, 0)
In [16]:
from itertools import compress
In [17]:
# def moccy(self):
# """
# Grabbing enclosed PanSTARRS1 pixels
# """
ps1_skycoord = SkyCoord(ps1_poly, unit="deg", frame="icrs")
ps1_moc = MOC.from_polygon_skycoord(ps1_skycoord, complement=False, max_depth=21)
ps1_mask = ps1_moc.contains_lonlat(pix_center_ra * u.degree, pix_center_dec * u.degree)
enc_pix_vertices = list(compress(pixel_vertices, ps1_mask))
enc_pix_center_ra = np.asarray(pix_center_ra)[ps1_mask]
enc_pix_center_dec = np.asarray(pix_center_dec)[ps1_mask]
enc_pix_indices = np.asarray(im1_indices)[ps1_mask]
enc_pix_index = np.asarray(im1_index)[ps1_mask]
if len(enc_pix_vertices) > 0:
enc_pix = []
for t in enc_pix_vertices:
pix_skycoord = SkyCoord(t, unit="deg", frame="icrs")
tess_pix_moc = MOC.from_polygon_skycoord(pix_skycoord, complement=False, max_depth=21)
enc_pix.append(tess_pix_moc)
else:
skip = True
print('Skipping:', skycell, skycell_index)
4.9s
In [19]:
enc_pix_vertices_small = enc_pix_vertices
# enc_pix_vertices_small = enc_pix_vertices[:500]
original but simplified code¶
In [20]:
## OLD but simplified
pix_ras = _ra2
pix_decs = _dec2
# output_vec = []
fll = np.full((ps1_data_shape[0], ps1_data_shape[1], 2), np.nan)
for i, tp in enumerate(enc_pix_vertices_small):
pix_skycoord = SkyCoord(tp, unit="deg", frame="icrs")
tess_pix_moc = MOC.from_polygon_skycoord(pix_skycoord, complement=False, max_depth=21)
tess_ind = enc_pix_indices[i]
_min_ra, _max_ra = np.min(tp[:, 0]) - 0.05, np.max(tp[:, 0]) + 0.05
_min_dec, _max_dec = np.min(tp[:, 1]) - 0.05, np.max(tp[:, 1]) + 0.05
search_indices = np.where((pix_ras >= _min_ra) &
(pix_ras <= _max_ra) &
(pix_decs >= _min_dec) &
(pix_decs <= _max_dec))[0]
enc_ps1_pix_mask = tess_pix_moc.contains_lonlat(pix_ras[search_indices] * u.degree, pix_decs[search_indices] * u.degree)
ps1_ind = np.arange(len(pix_ras))
ps1_ind = ps1_ind[search_indices][enc_ps1_pix_mask]
temp_ra = np.asarray(pix_ras[search_indices])[enc_ps1_pix_mask]
temp_dec = np.asarray(pix_decs[search_indices])[enc_ps1_pix_mask]
# output_vec.append((tess_ind, temp_ra, temp_dec, ps1_ind))
unravel_index = np.unravel_index(ps1_ind, ps1_data_shape)
length = len(unravel_index[0])
zeros = np.zeros(length)
tup = (unravel_index[0], unravel_index[1], zeros)
fll[tup[0], tup[1], np.int16(tup[2])] = tess_ind[1]
fll[tup[0], tup[1], np.int16(tup[2]) + 1] = tess_ind[0]
15m52.3s
New code¶
In [21]:
from numba import jit
pix_ras = _ra2
pix_decs = _dec2
polygons = np.array(enc_pix_vertices_small)
rust_result = MOC.filter_points_in_polygons(polygons=polygons, pix_ras=pix_ras, pix_decs=pix_decs, buffer=0.05, max_depth=21)
fll_1d_vec_rust = np.full((ps1_data_shape[0] * ps1_data_shape[1]), -1, dtype=np.int32)
@jit(nopython=True)
def populate_array_numba(fll_1d_vec_rust, rust_result, enc_pix_indices):
for i in range(len(rust_result)):
ps1_pix_in_tess = rust_result[i]
tess_ind = enc_pix_index[i]
for j in range(len(ps1_pix_in_tess)):
fll_1d_vec_rust[ps1_pix_in_tess[j]] = tess_ind
populate_array_numba(fll_1d_vec_rust, rust_result, enc_pix_indices)
6m12.1s
61% reduction in run time
In [22]:
np.array_equiv(rust_result[-1], ps1_ind)
Out[22]:
True
Original code¶
In [23]:
pix_obj_ind = np.arange(len(enc_pix))
pix_obj_etp = enc_pix
pix_obj_pix = enc_pix_indices[pix_obj_ind]
pix_indexs, tess_pix_mocs, tess_inds = pix_obj_ind, pix_obj_etp, pix_obj_pix
In [24]:
# def initialize_moc_pixel(self, pix_obj):
# """
# Initializes the PanSTARRS1 pixels to TESS pixels matching
# """
pix_ras = _ra2
pix_decs = _dec2
poly = enc_pix_vertices
output = []
# pix_indexs, tess_pix_mocs, tess_inds = pix_obj
if len(pix_indexs) != 0:
for i in range(len(pix_indexs)):
pix_index = pix_indexs[i]
tess_ind = tess_inds[i]
tess_pix_moc = tess_pix_mocs[i]
tp = poly[pix_index]
_min_ra, _max_ra = np.min(tp[:, 0]) - 0.05, np.max(tp[:, 0]) + 0.05
_min_dec, _max_dec = np.min(tp[:, 1]) - 0.05, np.max(tp[:, 1]) + 0.05
search_indices = np.where((pix_ras >= _min_ra) &
(pix_ras <= _max_ra) &
(pix_decs >= _min_dec) &
(pix_decs <= _max_dec))[0]
enc_ps1_pix_mask = tess_pix_moc.contains_lonlat(pix_ras[search_indices] * u.degree, pix_decs[search_indices] * u.degree)
ps1_ind = np.arange(len(pix_ras))
ps1_ind = ps1_ind[search_indices][enc_ps1_pix_mask]
temp_ra = np.asarray(pix_ras[search_indices])[enc_ps1_pix_mask]
temp_dec = np.asarray(pix_decs[search_indices])[enc_ps1_pix_mask]
output.append((tess_ind, temp_ra, temp_dec, ps1_ind))
# fll = _arrayifying(output)
# _fitsify(fll)
else:
pass
pix_output = output
14m54.9s
In [25]:
# def _arrayifying(self, pix_output):
# """
# Converts the PanSTARRS1 pixels to TESS pixels output to an array
# """
fll = np.full((ps1_data_shape[0], ps1_data_shape[1], 2), np.nan)
for i in range(len(pix_output)):
unravel_index = np.unravel_index(pix_output[i][-1], ps1_data_shape)
length = len(unravel_index[0])
zeros = np.zeros(length)
tup = (unravel_index[0], unravel_index[1], zeros)
fll[tup[0], tup[1], np.int16(tup[2])] = pix_output[i][0][1]
fll[tup[0], tup[1], np.int16(tup[2]) + 1] = pix_output[i][0][0]
# return fll
0.7s
In [30]:
# checking if the output matches the Rust implementation
fll_1d = (fll[:, :, 1] * data_shape[1] + fll[:, :, 0]).flatten()
fll_1d[np.isnan(fll_1d)] = -1
np.array_equal(fll_1d_vec_rust, fll_1d, equal_nan=True)
Out[30]:
True
In [32]:
plt.imshow(fll_1d.reshape(ps1_data_shape[0], ps1_data_shape[1]), origin="lower", cmap="gray")
plt.colorbar()
plt.show()