Flux Calibration Example Using HST image¶
From an HST image contained in our field of view, we compute the calibration bias of our SITELLE data cube.
We integrate our sitelle data in the HST filter bandpass. The obtained ‘flux map’ is compared pixel by pixel to the HST flux map to compute the flux bias
[1]:
#Should be removed in a regular python script
%matplotlib notebook
from astropy import wcs
from orcs.process import SpectralCube
import numpy as np
from astropy.io import fits
import matplotlib.pyplot as plt
from scipy.interpolate import RectBivariateSpline
from orcs.core import Filter
from orb.utils.image import nanbin_image
/home/blaunet/sitelle_env/lib/python2.7/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
from ._conv import register_converters as _register_converters
We load the SITELLE data and adjust the WCS calibration (done beforehand)
[2]:
SN2 = SpectralCube('/data/blaunet/fits/orig/M31_SN2.merged.cm1.1.0.hdf5')
SN2.set_wcs('./calibrations_maps/direct.wcs.deep_frame.fits')
SN2.set_dxdymaps('./calibrations_maps/direct.wcs.dxmap.fits', './calibrations_maps/direct.wcs.dymap.fits')
INFO| Data shape : (2048, 2064, 556)
WARNING| /home/blaunet/orcs/orcs/core.py:1586: UserWarning: Malformed spectral cube. The number of steps in the header (900) does not correspond to the real size of the data cube (556)
warnings.warn('Malformed spectral cube. The number of steps in the header ({}) does not correspond to the real size of the data cube ({})'.format(step_nb, self.dimz))
INFO| Cube is in WAVENUMBER (cm-1)
INFO| Cube is CALIBRATED in wavenumber
WARNING| /home/blaunet/orb/orb/core.py:416: UserWarning: Parameter already defined
warnings.warn('Parameter already defined')
INFO| Cube is in WAVENUMBER (cm-1)
INFO| Cube is CALIBRATED in wavenumber
WARNING| /home/blaunet/orcs/orcs/core.py:1441: UserWarning: dxmap reshaped from (200, 200) to (2048, 2064)
format(dxmap.shape, self.dimx, self.dimy))
WARNING| /home/blaunet/orcs/orcs/core.py:1448: UserWarning: dymap reshaped from (200, 200) to (2048, 2064)
format(dymap.shape, self.dimx, self.dimy))
We load our HST data. We are interested in the extension n°1 of the fits file
[3]:
hst_file_path = '/data/blaunet/fits/HLA/hst_11714_96_wfc3_uvis_f502n/hst_11714_96_wfc3_uvis_f502n_drz.fits'
xmin, xmax = 980, 1170
ymin, ymax = 940, 1130
PHOTFLAM = fits.getheader(hst_file_path, 1)['PHOTFLAM']
ext_num = 1
[4]:
hst_data = fits.getdata(hst_file_path, ext_num).T #Transpose to match ORCS convention on indexing [x,y]
hst_header = fits.getheader(hst_file_path, ext_num)
hst_data = hst_data*PHOTFLAM # We want our data to be in erg/cm2/A/s
hst_wcs = wcs.WCS(hst_header) #We need the WCS to make the two data set correspond
[5]:
f,ax = plt.subplots()
ax.imshow(hst_data.T, origin='lower', cmap='gray_r',
vmin = np.nanpercentile(hst_data, 5),
vmax = np.nanpercentile(hst_data, 95))
[5]:
<matplotlib.image.AxesImage at 0x7f724e261390>
Instanciation of a Filter¶
We need to get the filter corresponding to our HST image
[6]:
hst_filter_path = '/data/blaunet/fits/HST/HST_WFC3_UVIS1.F502N.dat.txt'
[7]:
a = []
T = []
with open(hst_filter_path, 'r') as f:
for line in f:
tmp = map(float,line.strip().split(' ')) # We convert the line elemtnts into floats
a.append(tmp[0]/10.) # to have nm
T.append(tmp[1])
filter_axis = np.array(a) #the list are converted to numpy array for convenience
filter_transmission = np.array(T)
hst_filter = Filter(filter_axis, filter_transmission) # a Filter object is istanciated
Integration of SITELLE data in HST filter bandpass¶
For speed concerns, we don’t need to perform this integration over the whole SITELLE field of view, as HST images are usually much smaller. From image study, we determined boundaries of HST image in our field of view
[8]:
#Outside this region, the flux is set to NaN
sitelle_flux_map = SN2.integrate(hst_filter, xmin,xmax,ymin,ymax)
sitelle_flux_map[sitelle_flux_map == 0.] = np.nan
[9]:
f,ax = plt.subplots()
im = ax.imshow(sitelle_flux_map.T, origin='lower', cmap='RdBu_r')
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin,ymax)
f.colorbar(im)
ax.set_title('Integrated SITELLE flux in HST F502N filter')
[9]:
Text(0.5,1,u'Integrated SITELLE flux in HST F502N filter')
Reprojection of HST data in SITELLE coordinates¶
We are going to interpolate HST data on our SITELLE grid. Because the pixel size are obviously not the same, we need to be careful in how we do that.
astropy WCS objects provide useful methods to do this (see http://docs.astropy.org/en/stable/api/astropy.wcs.utils.proj_plane_pixel_area.html#astropy.wcs.utils.proj_plane_pixel_area)
[10]:
print 'SITELLE pixel area in the "projection plane" : ', wcs.utils.proj_plane_pixel_area(SN2.get_wcs())
print 'HST pixel area in the "projection plane" : ', wcs.utils.proj_plane_pixel_area(hst_wcs)
pixel_scale_factor = wcs.utils.proj_plane_pixel_area(SN2.get_wcs())/wcs.utils.proj_plane_pixel_area(hst_wcs)
print 'Pixel scale factor (SITELLE/HST) : ', pixel_scale_factor
SITELLE pixel area in the "projection plane" : 8.092914404881915e-09
HST pixel area in the "projection plane" : 1.1926811840401893e-10
Pixel scale factor (SITELLE/HST) : 67.85480070597988
Therefore, we are going to intentionally oversample our HST image, bin it and correct by the pixel sacle factor
[11]:
scale_factor = np.ceil(np.sqrt(pixel_scale_factor))
X,Y = np.mgrid[xmin:xmax:1/scale_factor,ymin:ymax:1/scale_factor] #This is an ovsersampled grid
XYp = hst_wcs.all_world2pix(SN2.pix2world(np.array([X.flatten(),Y.flatten()]).T), 0)
Xp, Yp = XYp.T
Xp, Yp are now the coordinates of an oversampled SITELLE grid in HST image coordinates.
We perform a linear interpolation of HST image on this grid
[12]:
# If we have NaNs it's going to mess with the interpolation
hst_data = fits.getdata(hst_file_path, ext_num).T*PHOTFLAM
nan_pos = np.isnan(hst_data)
hst_data[nan_pos] = 0.
[13]:
projection_function = RectBivariateSpline(np.arange(hst_data.shape[0]),
np.arange(hst_data.shape[1]),
hst_data, kx=1, ky=1, s=0)
reprojected_HST = projection_function.ev(Xp.reshape(*X.shape),Yp.reshape(*Y.shape))
reprojected_HST
is now HST image evaluated on the oversampled SITELLE grid.
We now bin this to SITELLE’s resolution. As nanbin_image computes the binned mean, we have to correct for the pixel area
[14]:
hst_flux_map = np.full_like(sitelle_flux_map, np.nan)
hst_flux_map[xmin:xmax, ymin:ymax] = nanbin_image(reprojected_HST, int(scale_factor))*pixel_scale_factor
hst_flux_map[hst_flux_map == 0.] = np.nan
We plot the resulting map
[15]:
f,ax = plt.subplots()
im = ax.imshow(hst_flux_map.T, origin='bottom-left', cmap='jet')
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin,ymax)
f.colorbar(im)
ax.set_title('HST Flux map, in SITELLE grid')
[15]:
Text(0.5,1,u'HST Flux map, in SITELLE grid')
Flux Ratio¶
From this two maps, we can get the flux ratio (i.e. bias) between our datacube and HST data
[16]:
ratio = hst_flux_map/sitelle_flux_map
f,ax = plt.subplots()
im = ax.imshow(ratio.T, origin='bottom-left', cmap='jet',
vmax = np.nanpercentile(ratio, 99),
vmin=np.nanpercentile(ratio,1))
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin,ymax)
f.colorbar(im)
ax.set_title('Ratio of HST flux map over SITELLE flux map')
[16]:
Text(0.5,1,u'Ratio of HST flux map over SITELLE flux map')
This can also be plotted as an histogram
[17]:
f,ax = plt.subplots()
h = ax.hist(ratio[~np.isnan(ratio)], bins=300) # we need to remove the NaNs
ax.set_title('Median = %.5f, Std = %.5f'%(np.median(ratio[~np.isnan(ratio)]), np.std(ratio[~np.isnan(ratio)])))
[17]:
Text(0.5,1,u'Median = 0.87846, Std = 0.12119')
[ ]:
The End¶
From now on, it’s up to the user to choose what to do with this : use it as is or try to clean this map/histogram from obvious outliers (edge effects, center of the frame).
What follows may thus be applicable only to this specific data, and be conceptually arguable
[18]:
#We consider extrema at 1% and 99% as outliers
r = np.copy(ratio)
r[r > np.nanpercentile(ratio,99)] = np.nan
r[r < np.nanpercentile(ratio,1)] = np.nan
WARNING| /home/blaunet/sitelle_env/lib/python2.7/site-packages/ipykernel_launcher.py:3: RuntimeWarning: invalid value encountered in greater
This is separate from the ipykernel package so we can avoid doing imports until
WARNING| /home/blaunet/sitelle_env/lib/python2.7/site-packages/ipykernel_launcher.py:4: RuntimeWarning: invalid value encountered in less
after removing the cwd from sys.path.
[19]:
f,ax = plt.subplots()
ax.hist(r[~np.isnan(r)], bins=300) # we need to remove the NaNs
ax.set_title('Median = %.5f, Std = %.5f'%(np.median(r[~np.isnan(r)]), np.std(r[~np.isnan(r)])))
f,ax = plt.subplots()
im = ax.imshow(r.T, origin='lower', cmap='jet')
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin,ymax)
plt.colorbar(im)
[19]:
<matplotlib.colorbar.Colorbar at 0x7f7247736d50>
[ ]:
[ ]: