555 lines
27 KiB
Python
Executable File
555 lines
27 KiB
Python
Executable File
"""
|
|
Library functions for displaying informations using matplotlib
|
|
|
|
prototypes :
|
|
- plot_obs(data_array, headers, shape, vmin, vmax, savename, plots_folder)
|
|
Plots whole observation raw data in given display shape
|
|
|
|
- polarization_map(Stokes_hdul, SNRp_cut, SNRi_cut, step_vec, savename, plots_folder, display)
|
|
Plots polarization map of polarimetric parameters saved in an HDUList
|
|
"""
|
|
|
|
import copy
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.patches import Rectangle
|
|
from matplotlib.widgets import RectangleSelector
|
|
import matplotlib.font_manager as fm
|
|
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar, AnchoredDirectionArrows
|
|
from astropy.wcs import WCS
|
|
|
|
|
|
def princ_angle(ang):
|
|
"""
|
|
Return the principal angle in the 0-180° quadrant.
|
|
"""
|
|
while ang < 0.:
|
|
ang += 180.
|
|
while ang > 180.:
|
|
ang -= 180.
|
|
return ang
|
|
|
|
|
|
def sci_not(v,err,rnd=1):
|
|
"""
|
|
Return the scientifque error notation as a string.
|
|
"""
|
|
power = - int(('%E' % v)[-3:])+1
|
|
output = r"({0}".format(round(v*10**power,rnd))
|
|
if type(err) == list:
|
|
for error in err:
|
|
output += r" $\pm$ {0}".format(round(error*10**power,rnd))
|
|
else:
|
|
output += r" $\pm$ {0}".format(round(err*10**power,rnd))
|
|
return output+r")e{0}".format(-power)
|
|
|
|
|
|
def plot_obs(data_array, headers, shape=None, vmin=0., vmax=6., rectangle=None,
|
|
savename=None, plots_folder=""):
|
|
"""
|
|
Plots raw observation imagery with some information on the instrument and
|
|
filters.
|
|
----------
|
|
Inputs:
|
|
data_array : numpy.ndarray
|
|
Array of images (2D floats, aligned and of the same shape) of a
|
|
single observation with multiple polarizers of an instrument
|
|
headers : header list
|
|
List of headers corresponding to the images in data_array
|
|
shape : array-like of length 2, optional
|
|
Shape of the display, with shape = [#row, #columns]. If None, defaults
|
|
to the optimal square.
|
|
Defaults to None.
|
|
vmin : float, optional
|
|
Min pixel value that should be displayed.
|
|
Defaults to 0.
|
|
vmax : float, optional
|
|
Max pixel value that should be displayed.
|
|
Defaults to 6.
|
|
rectangle : numpy.ndarray, optional
|
|
Array of parameters for matplotlib.patches.Rectangle objects that will
|
|
be displayed on each output image. If None, no rectangle displayed.
|
|
Defaults to None.
|
|
savename : str, optional
|
|
Name of the figure the map should be saved to. If None, the map won't
|
|
be saved (only displayed).
|
|
Defaults to None.
|
|
plots_folder : str, optional
|
|
Relative (or absolute) filepath to the folder in wich the map will
|
|
be saved. Not used if savename is None.
|
|
Defaults to current folder.
|
|
"""
|
|
if shape is None:
|
|
shape = np.array([np.ceil(np.sqrt(data_array.shape[0])).astype(int),]*2)
|
|
fig, ax = plt.subplots(shape[0], shape[1], figsize=(10,10), dpi=200,
|
|
sharex=True, sharey=True)
|
|
|
|
for i, enum in enumerate(list(zip(ax.flatten(),data_array))):
|
|
ax = enum[0]
|
|
data = enum[1]
|
|
instr = headers[i]['instrume']
|
|
rootname = headers[i]['rootname']
|
|
exptime = headers[i]['exptime']
|
|
filt = headers[i]['filtnam1']
|
|
#plots
|
|
im = ax.imshow(data, vmin=vmin, vmax=vmax, origin='lower')
|
|
if not(rectangle is None):
|
|
x, y, width, height, angle, color = rectangle[i]
|
|
ax.add_patch(Rectangle((x, y), width, height, angle=angle,
|
|
edgecolor=color, fill=False))
|
|
#position of centroid
|
|
ax.plot([data.shape[1]/2, data.shape[1]/2], [0,data.shape[0]-1], lw=1,
|
|
color='black')
|
|
ax.plot([0,data.shape[1]-1], [data.shape[1]/2, data.shape[1]/2], lw=1,
|
|
color='black')
|
|
ax.annotate(instr+":"+rootname,color='white',fontsize=5,xy=(0.02, 0.95),
|
|
xycoords='axes fraction')
|
|
ax.annotate(filt,color='white',fontsize=10,xy=(0.02, 0.02),
|
|
xycoords='axes fraction')
|
|
ax.annotate(exptime,color='white',fontsize=5,xy=(0.80, 0.02),
|
|
xycoords='axes fraction')
|
|
|
|
fig.subplots_adjust(hspace=0, wspace=0, right=0.85)
|
|
cbar_ax = fig.add_axes([0.9, 0.12, 0.02, 0.75])
|
|
fig.colorbar(im, cax=cbar_ax)
|
|
|
|
if not (savename is None):
|
|
fig.suptitle(savename)
|
|
fig.savefig(plots_folder+savename+".png",bbox_inches='tight')
|
|
plt.show()
|
|
return 0
|
|
|
|
|
|
def plot_Stokes(Stokes, savename=None, plots_folder=""):
|
|
"""
|
|
Plots I/Q/U maps.
|
|
----------
|
|
Inputs:
|
|
Stokes : astropy.io.fits.hdu.hdulist.HDUList
|
|
HDUList containing I, Q, U, P, s_P, PA, s_PA (in this particular order)
|
|
for one observation.
|
|
savename : str, optional
|
|
Name of the figure the map should be saved to. If None, the map won't
|
|
be saved (only displayed).
|
|
Defaults to None.
|
|
plots_folder : str, optional
|
|
Relative (or absolute) filepath to the folder in wich the map will
|
|
be saved. Not used if savename is None.
|
|
Defaults to current folder.
|
|
"""
|
|
# Get data
|
|
stkI = Stokes[np.argmax([Stokes[i].header['datatype']=='I_stokes' for i in range(len(Stokes))])].data
|
|
stkQ = Stokes[np.argmax([Stokes[i].header['datatype']=='Q_stokes' for i in range(len(Stokes))])].data
|
|
stkU = Stokes[np.argmax([Stokes[i].header['datatype']=='U_stokes' for i in range(len(Stokes))])].data
|
|
|
|
wcs = WCS(Stokes[0]).deepcopy()
|
|
|
|
# Plot figure
|
|
fig = plt.figure(figsize=(30,10))
|
|
|
|
ax = fig.add_subplot(131, projection=wcs)
|
|
im = ax.imshow(stkI, origin='lower')
|
|
plt.colorbar(im)
|
|
ax.set(xlabel="RA", ylabel="DEC", title=r"$I_{stokes}$")
|
|
|
|
ax = fig.add_subplot(132, projection=wcs)
|
|
im = ax.imshow(stkQ, origin='lower')
|
|
plt.colorbar(im)
|
|
ax.set(xlabel="RA", ylabel="DEC", title=r"$Q_{stokes}$")
|
|
|
|
ax = fig.add_subplot(133, projection=wcs)
|
|
im = ax.imshow(stkU, origin='lower')
|
|
plt.colorbar(im)
|
|
ax.set(xlabel="RA", ylabel="DEC", title=r"$U_{stokes}$")
|
|
|
|
if not (savename is None):
|
|
fig.suptitle(savename+"_IQU")
|
|
fig.savefig(plots_folder+savename+"_IQU.png",bbox_inches='tight')
|
|
plt.show()
|
|
return 0
|
|
|
|
|
|
class crop_map(object):
|
|
"""
|
|
Class to interactively crop I_stokes map to desired Region of Interest
|
|
"""
|
|
def __init__(self, Stokes, data_mask, SNRp_cut=3., SNRi_cut=30.):
|
|
#Get data
|
|
stkI = Stokes[np.argmax([Stokes[i].header['datatype']=='I_stokes' for i in range(len(Stokes))])]
|
|
stk_cov = Stokes[np.argmax([Stokes[i].header['datatype']=='IQU_cov_matrix' for i in range(len(Stokes))])]
|
|
pol = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg_debiased' for i in range(len(Stokes))])]
|
|
pol_err = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg_err' for i in range(len(Stokes))])]
|
|
self.Stokes = Stokes
|
|
self.data_mask = data_mask
|
|
|
|
wcs = WCS(Stokes[0]).deepcopy()
|
|
|
|
#Compute SNR and apply cuts
|
|
pol.data[pol.data == 0.] = np.nan
|
|
SNRp = pol.data/pol_err.data
|
|
SNRp[np.isnan(SNRp)] = 0.
|
|
pol.data[SNRp < SNRp_cut] = np.nan
|
|
SNRi = stkI.data/np.sqrt(stk_cov.data[0,0])
|
|
SNRi[np.isnan(SNRi)] = 0.
|
|
pol.data[SNRi < SNRi_cut] = np.nan
|
|
|
|
convert_flux = Stokes[0].header['photflam']
|
|
|
|
#Plot the map
|
|
plt.rcParams.update({'font.size': 16})
|
|
self.fig = plt.figure(figsize=(15,15))
|
|
self.ax = self.fig.add_subplot(111, projection=wcs)
|
|
self.ax.set_facecolor('k')
|
|
self.fig.subplots_adjust(hspace=0, wspace=0, right=0.9)
|
|
cbar_ax = self.fig.add_axes([0.95, 0.12, 0.01, 0.75])
|
|
|
|
self.extent = [-stkI.data.shape[1]/2.,stkI.data.shape[1]/2.,-stkI.data.shape[0]/2.,stkI.data.shape[0]/2.]
|
|
self.center = [stkI.data.shape[1]/2.,stkI.data.shape[0]/2.]
|
|
|
|
vmin, vmax = 0., np.max(stkI.data[stkI.data > 0.]*convert_flux)
|
|
im = self.ax.imshow(stkI.data*convert_flux,extent=self.extent, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$F_{\lambda}$ [$ergs \cdot cm^{-2} \cdot s^{-1} \cdot \AA^{-1}$]")
|
|
levelsI = np.linspace(SNRi_cut, np.max(SNRi[SNRi > 0.]), 10)
|
|
cont = self.ax.contour(SNRi, extent=self.extent, levels=levelsI, colors='grey', linewidths=0.5)
|
|
|
|
fontprops = fm.FontProperties(size=16)
|
|
px_size = wcs.wcs.get_cdelt()[0]
|
|
px_sc = AnchoredSizeBar(self.ax.transData, 1./px_size, '1 arcsec', 3, pad=0.5, sep=5, borderpad=0.5, frameon=False, size_vertical=0.005, color='w', fontproperties=fontprops)
|
|
self.ax.add_artist(px_sc)
|
|
|
|
self.ax.set_title(
|
|
"Click and drag to crop to desired Region of Interest.\n"
|
|
"Press 'c' to toggle the selector on and off")
|
|
|
|
def onselect_crop(self, eclick, erelease) -> None:
|
|
# Obtain (xmin, xmax, ymin, ymax) values
|
|
self.RSextent = self.rect_selector.extents
|
|
self.RScenter = [self.center[i]+self.rect_selector.center[i] for i in range(2)]
|
|
|
|
# Zoom to selection
|
|
print("CROP TO : ",self.RSextent)
|
|
print("CENTER : ",self.RScenter)
|
|
#self.ax.set_xlim(self.extent[0], self.extent[1])
|
|
#self.ax.set_ylim(self.extent[2], self.extent[3])
|
|
|
|
def run(self) -> None:
|
|
self.rect_selector = RectangleSelector(self.ax, self.onselect_crop,
|
|
drawtype='box', button=[1], interactive=True)
|
|
#self.fig.canvas.mpl_connect('key_press_event', self.toggle_selector)
|
|
plt.show()
|
|
|
|
def crop(self):
|
|
Stokes_crop = copy.deepcopy(self.Stokes)
|
|
# Data sets to crop
|
|
stkI = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='I_stokes' for i in range(len(Stokes_crop))])]
|
|
stkQ = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='Q_stokes' for i in range(len(Stokes_crop))])]
|
|
stkU = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='U_stokes' for i in range(len(Stokes_crop))])]
|
|
stk_cov = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='IQU_cov_matrix' for i in range(len(Stokes_crop))])]
|
|
pol = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='Pol_deg_debiased' for i in range(len(Stokes_crop))])]
|
|
pol_err = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='Pol_deg_err' for i in range(len(Stokes_crop))])]
|
|
pang = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='Pol_ang' for i in range(len(Stokes_crop))])]
|
|
pang_err = Stokes_crop[np.argmax([Stokes_crop[i].header['datatype']=='Pol_ang_err' for i in range(len(Stokes_crop))])]
|
|
# Crop datasets
|
|
vertex = [int(self.RScenter[0]+self.RSextent[0]), int(self.RScenter[0]+self.RSextent[1]), int(self.RScenter[1]+self.RSextent[2]), int(self.RScenter[1]+self.RSextent[3])]
|
|
for dataset in [stkI, stkQ, stkU, pol, pol_err, pang, pang_err]:
|
|
dataset.data = dataset.data[vertex[2]:vertex[3], vertex[0]:vertex[1]]
|
|
data_mask = self.data_mask[vertex[2]:vertex[3], vertex[0]:vertex[1]]
|
|
new_stk_cov = np.zeros((3,3,stkI.data.shape[0],stkI.data.shape[1]))
|
|
for i in range(3):
|
|
for j in range(3):
|
|
new_stk_cov[i,j] = stk_cov.data[i,j][vertex[2]:vertex[3], vertex[0]:vertex[1]]
|
|
stk_cov.data = new_stk_cov
|
|
|
|
return Stokes_crop, data_mask
|
|
|
|
|
|
def polarization_map(Stokes, data_mask=None, rectangle=None, SNRp_cut=3., SNRi_cut=30.,
|
|
step_vec=1, savename=None, plots_folder="", display=None):
|
|
"""
|
|
Plots polarization map from Stokes HDUList.
|
|
----------
|
|
Inputs:
|
|
Stokes : astropy.io.fits.hdu.hdulist.HDUList
|
|
HDUList containing I, Q, U, P, s_P, PA, s_PA (in this particular order)
|
|
for one observation.
|
|
rectangle : numpy.ndarray, optional
|
|
Array of parameters for matplotlib.patches.Rectangle objects that will
|
|
be displayed on each output image. If None, no rectangle displayed.
|
|
Defaults to None.
|
|
SNRp_cut : float, optional
|
|
Cut that should be applied to the signal-to-noise ratio on P.
|
|
Any SNR < SNRp_cut won't be displayed.
|
|
Defaults to 3.
|
|
SNRi_cut : float, optional
|
|
Cut that should be applied to the signal-to-noise ratio on I.
|
|
Any SNR < SNRi_cut won't be displayed.
|
|
Defaults to 30. This value implies an uncertainty in P of 4.7%
|
|
step_vec : int, optional
|
|
Number of steps between each displayed polarization vector.
|
|
If step_vec = 2, every other vector will be displayed.
|
|
Defaults to 1
|
|
savename : str, optional
|
|
Name of the figure the map should be saved to. If None, the map won't
|
|
be saved (only displayed).
|
|
Defaults to None.
|
|
plots_folder : str, optional
|
|
Relative (or absolute) filepath to the folder in wich the map will
|
|
be saved. Not used if savename is None.
|
|
Defaults to current folder.
|
|
display : str, optional
|
|
Choose the map to display between intensity (default), polarization
|
|
degree ('p','pol','pol_deg') or polarization degree error ('s_p',
|
|
'pol_err','pol_deg_err').
|
|
Defaults to None (intensity).
|
|
----------
|
|
Returns:
|
|
fig, ax : matplotlib.pyplot object
|
|
The figure and ax created for interactive contour maps.
|
|
"""
|
|
#Get data
|
|
stkI = Stokes[np.argmax([Stokes[i].header['datatype']=='I_stokes' for i in range(len(Stokes))])]
|
|
stkQ = Stokes[np.argmax([Stokes[i].header['datatype']=='Q_stokes' for i in range(len(Stokes))])]
|
|
stkU = Stokes[np.argmax([Stokes[i].header['datatype']=='U_stokes' for i in range(len(Stokes))])]
|
|
stk_cov = Stokes[np.argmax([Stokes[i].header['datatype']=='IQU_cov_matrix' for i in range(len(Stokes))])]
|
|
#pol = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg' for i in range(len(Stokes))])]
|
|
pol = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg_debiased' for i in range(len(Stokes))])]
|
|
pol_err = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg_err' for i in range(len(Stokes))])]
|
|
#pol_err_Poisson = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg_err_Poisson_noise' for i in range(len(Stokes))])]
|
|
pang = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_ang' for i in range(len(Stokes))])]
|
|
pang_err = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_ang_err' for i in range(len(Stokes))])]
|
|
|
|
pivot_wav = Stokes[0].header['photplam']
|
|
convert_flux = Stokes[0].header['photflam']
|
|
wcs = WCS(Stokes[0]).deepcopy()
|
|
|
|
#Get image mask
|
|
if data_mask is None:
|
|
data_mask = np.ones(stkI.shape).astype(bool)
|
|
|
|
#Plot Stokes parameters map
|
|
if display is None:
|
|
plot_Stokes(Stokes, savename=savename, plots_folder=plots_folder)
|
|
|
|
#Compute SNR and apply cuts
|
|
pol.data[pol.data == 0.] = np.nan
|
|
SNRp = pol.data/pol_err.data
|
|
SNRp[np.isnan(SNRp)] = 0.
|
|
pol.data[SNRp < SNRp_cut] = np.nan
|
|
|
|
maskI = stk_cov.data[0,0] > 0
|
|
SNRi = np.zeros(stkI.data.shape)
|
|
SNRi[maskI] = stkI.data[maskI]/np.sqrt(stk_cov.data[0,0][maskI])
|
|
pol.data[SNRi < SNRi_cut] = np.nan
|
|
|
|
data_mask = (1.-data_mask).astype(bool)
|
|
mask = (SNRp > SNRp_cut) * (SNRi > SNRi_cut)
|
|
|
|
# Look for pixel of max polarization
|
|
if np.isfinite(pol.data).any():
|
|
p_max = np.max(pol.data[np.isfinite(pol.data)])
|
|
x_max, y_max = np.unravel_index(np.argmax(pol.data==p_max),pol.data.shape)
|
|
else:
|
|
print("No pixel with polarization information above requested SNR.")
|
|
|
|
#Plot the map
|
|
plt.rcParams.update({'font.size': 16})
|
|
fig = plt.figure(figsize=(15,15))
|
|
ax = fig.add_subplot(111, projection=wcs)
|
|
ax.set_facecolor('k')
|
|
fig.subplots_adjust(hspace=0, wspace=0, right=0.9)
|
|
cbar_ax = fig.add_axes([0.95, 0.12, 0.01, 0.75])
|
|
|
|
if display is None:
|
|
# If no display selected, show intensity map
|
|
vmin, vmax = 0., np.max(stkI.data[stkI.data > 0.]*convert_flux)
|
|
im = ax.imshow(stkI.data*convert_flux, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$F_{\lambda}$ [$ergs \cdot cm^{-2} \cdot s^{-1} \cdot \AA^{-1}$]")
|
|
levelsI = np.linspace(SNRi_cut, np.max(SNRi[SNRi > 0.]), 10)
|
|
cont = ax.contour(SNRi, levels=levelsI, colors='grey', linewidths=0.5)
|
|
elif display.lower() in ['pol_flux']:
|
|
# Display polarisation flux
|
|
pf_mask = (stkI.data > 0.) * (pol.data > 0.)
|
|
vmin, vmax = 0., np.max(stkI.data[pf_mask]*convert_flux*pol.data[pf_mask])
|
|
im = ax.imshow(stkI.data*convert_flux*pol.data, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$F_{\lambda} \cdot P$ [$ergs \cdot cm^{-2} \cdot s^{-1} \cdot \AA^{-1}$]")
|
|
elif display.lower() in ['p','pol','pol_deg']:
|
|
# Display polarization degree map
|
|
vmin, vmax = 0., 100.
|
|
im = ax.imshow(pol.data*100., vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$P$ [%]")
|
|
elif display.lower() in ['s_p','pol_err','pol_deg_err']:
|
|
# Display polarization degree error map
|
|
vmin, vmax = 0., 10.
|
|
p_err = pol_err.data.copy()
|
|
p_err[p_err > vmax/100.] = np.nan
|
|
im = ax.imshow(p_err*100., vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$\sigma_P$ [%]")
|
|
elif display.lower() in ['s_i','i_err']:
|
|
# Display intensity error map
|
|
vmin, vmax = 0., np.max(np.sqrt(stk_cov.data[0,0][stk_cov.data[0,0] > 0.])*convert_flux)
|
|
im = ax.imshow(np.sqrt(stk_cov.data[0,0])*convert_flux, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$\sigma_I$ [$ergs \cdot cm^{-2} \cdot s^{-1} \cdot \AA^{-1}$]")
|
|
elif display.lower() in ['snr','snri']:
|
|
# Display I_stokes signal-to-noise map
|
|
vmin, vmax = 0., np.max(SNRi[SNRi > 0.])
|
|
im = ax.imshow(SNRi, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$I_{Stokes}/\sigma_{I}$")
|
|
levelsI = np.linspace(SNRi_cut, np.max(SNRi[SNRi > 0.]), 10)
|
|
cont = ax.contour(SNRi, levels=levelsI, colors='grey', linewidths=0.5)
|
|
elif display.lower() in ['snrp']:
|
|
# Display polarization degree signal-to-noise map
|
|
vmin, vmax = SNRp_cut, np.max(SNRp[SNRp > 0.])
|
|
im = ax.imshow(SNRp, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$P/\sigma_{P}$")
|
|
levelsP = np.linspace(SNRp_cut, np.max(SNRp[SNRp > 0.]), 10)
|
|
cont = ax.contour(SNRp, levels=levelsP, colors='grey', linewidths=0.5)
|
|
else:
|
|
# Defaults to intensity map
|
|
vmin, vmax = 0., np.max(stkI.data[stkI.data > 0.]*convert_flux)
|
|
im = ax.imshow(stkI.data*convert_flux, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
cbar = plt.colorbar(im, cax=cbar_ax, label=r"$F_{\lambda}$ [$ergs \cdot cm^{-2} \cdot s^{-1} \cdot \AA$]")
|
|
levelsI = np.linspace(SNRi_cut, SNRi.max(), 10)
|
|
cont = ax.contour(SNRi, levels=levelsI, colors='grey', linewidths=0.5)
|
|
|
|
fontprops = fm.FontProperties(size=16)
|
|
px_size = wcs.wcs.get_cdelt()[0]*3600.
|
|
px_sc = AnchoredSizeBar(ax.transData, 1./px_size, '1 arcsec', 3, pad=0.5, sep=5, borderpad=0.5, frameon=False, size_vertical=0.005, color='w', fontproperties=fontprops)
|
|
ax.add_artist(px_sc)
|
|
|
|
#pol.data[np.isfinite(pol.data)] = 1./2.
|
|
X, Y = np.meshgrid(np.linspace(0,stkI.data.shape[0],stkI.data.shape[0]), np.linspace(0,stkI.data.shape[1],stkI.data.shape[1]))
|
|
U, V = pol.data*np.cos(np.pi/2.+pang.data*np.pi/180.), pol.data*np.sin(np.pi/2.+pang.data*np.pi/180.)
|
|
Q = ax.quiver(X[::step_vec,::step_vec],Y[::step_vec,::step_vec],U[::step_vec,::step_vec],V[::step_vec,::step_vec],units='xy',angles='uv',scale=0.5,scale_units='xy',pivot='mid',headwidth=0.,headlength=0.,headaxislength=0.,width=0.1,color='w')
|
|
pol_sc = AnchoredSizeBar(ax.transData, 2., r"$P$= 100 %", 4, pad=0.5, sep=5, borderpad=0.5, frameon=False, size_vertical=0.005, color='w', fontproperties=fontprops)
|
|
ax.add_artist(pol_sc)
|
|
|
|
north_dir = AnchoredDirectionArrows(ax.transAxes, "E", "N", length=-0.08, fontsize=0.03, loc=1, aspect_ratio=-1, sep_y=0.01, sep_x=0.01, angle=-Stokes[0].header['orientat'], color='w', arrow_props={'ec': 'w', 'fc': 'w', 'alpha': 1,'lw': 2})
|
|
ax.add_artist(north_dir)
|
|
|
|
# Display instrument FOV
|
|
if not(rectangle is None):
|
|
x, y, width, height, angle, color = rectangle
|
|
x, y = np.array([x, y])- np.array(stkI.data.shape)/2.
|
|
ax.add_patch(Rectangle((x, y), width, height, angle=angle,
|
|
edgecolor=color, fill=False))
|
|
|
|
# Compute integrated parameters and associated errors for pixels in the cut
|
|
n_pix = mask.size
|
|
I_int = stkI.data[mask].sum()
|
|
Q_int = stkQ.data[mask].sum()
|
|
U_int = stkU.data[mask].sum()
|
|
I_int_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[0,0][mask]))
|
|
Q_int_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[1,1][mask]))
|
|
U_int_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[2,2][mask]))
|
|
IQ_int_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[0,1][mask]**2))
|
|
IU_int_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[0,2][mask]**2))
|
|
QU_int_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[1,2][mask]**2))
|
|
|
|
P_int = np.sqrt(Q_int**2+U_int**2)/I_int
|
|
P_int_err = (1./I_int)*np.sqrt((Q_int**2*Q_int_err**2 + U_int**2*U_int_err**2 + 2.*Q_int*U_int*QU_int_err)/(Q_int**2 + U_int**2) + ((Q_int/I_int)**2 + (U_int/I_int)**2)*I_int_err**2 - 2.*(Q_int/I_int)*IQ_int_err - 2.*(U_int/I_int)*IU_int_err)
|
|
|
|
PA_int = princ_angle((90./np.pi)*np.arctan2(U_int,Q_int))
|
|
PA_int_err = (90./(np.pi*(Q_int**2 + U_int**2)))*np.sqrt(U_int**2*Q_int_err**2 + Q_int**2*U_int_err**2 - 2.*Q_int*U_int*QU_int_err)
|
|
|
|
# Compute integrated parameters and associated errors for all pixels
|
|
n_pix = stkI.data[data_mask].size
|
|
I_diluted = stkI.data[data_mask].sum()
|
|
Q_diluted = stkQ.data[data_mask].sum()
|
|
U_diluted = stkU.data[data_mask].sum()
|
|
I_diluted_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[0,0][data_mask]))
|
|
Q_diluted_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[1,1][data_mask]))
|
|
U_diluted_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[2,2][data_mask]))
|
|
IQ_diluted_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[0,1][data_mask]**2))
|
|
IU_diluted_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[0,2][data_mask]**2))
|
|
QU_diluted_err = np.sqrt(n_pix)*np.sqrt(np.sum(stk_cov.data[1,2][data_mask]**2))
|
|
|
|
P_diluted = np.sqrt(Q_diluted**2+U_diluted**2)/I_diluted
|
|
P_diluted_err = (1./I_diluted)*np.sqrt((Q_diluted**2*Q_diluted_err**2 + U_diluted**2*U_diluted_err**2 + 2.*Q_diluted*U_diluted*QU_diluted_err)/(Q_diluted**2 + U_diluted**2) + ((Q_diluted/I_diluted)**2 + (U_diluted/I_diluted)**2)*I_diluted_err**2 - 2.*(Q_diluted/I_diluted)*IQ_diluted_err - 2.*(U_diluted/I_diluted)*IU_diluted_err)
|
|
#P_diluted_err = np.sqrt(2/n_pix)*100.
|
|
|
|
PA_diluted = princ_angle((90./np.pi)*np.arctan2(U_diluted,Q_diluted))
|
|
PA_diluted_err = (90./(np.pi*(Q_diluted**2 + U_diluted**2)))*np.sqrt(U_diluted**2*Q_diluted_err**2 + Q_diluted**2*U_diluted_err**2 - 2.*Q_diluted*U_diluted*QU_diluted_err)
|
|
#PA_diluted_err = P_diluted_err/(2.*P_diluted)*180./np.pi
|
|
|
|
ax.annotate(r"$F_{{\lambda}}^{{int}}$({0:.0f} $\AA$) = {1} $ergs \cdot cm^{{-2}} \cdot s^{{-1}} \cdot \AA^{{-1}}$".format(pivot_wav,sci_not(I_diluted*convert_flux,I_diluted_err*convert_flux,2))+"\n"+r"$P^{{int}}$ = {0:.1f} $\pm$ {1:.1f} %".format(P_diluted*100.,P_diluted_err*100.)+"\n"+r"$\theta_{{P}}^{{int}}$ = {0:.1f} $\pm$ {1:.1f} °".format(PA_diluted,PA_diluted_err), color='white', fontsize=16, xy=(0.01, 0.92), xycoords='axes fraction')
|
|
|
|
ax.coords.grid(True, color='white', ls='dotted', alpha=0.5)
|
|
ax.coords[0].set_axislabel('Right Ascension (J2000)')
|
|
ax.coords[0].set_axislabel_position('t')
|
|
ax.coords[0].set_ticklabel_position('t')
|
|
ax.coords[1].set_axislabel('Declination (J2000)')
|
|
ax.coords[1].set_axislabel_position('l')
|
|
ax.coords[1].set_ticklabel_position('l')
|
|
ax.axis('equal')
|
|
|
|
if not savename is None:
|
|
#fig.suptitle(savename)
|
|
fig.savefig(plots_folder+savename+".png",bbox_inches='tight',dpi=200)
|
|
|
|
plt.show()
|
|
return fig, ax
|
|
|
|
class align_maps(object):
|
|
"""
|
|
Class to interactively align maps with different WCS.
|
|
"""
|
|
def __init__(self, Stokes, other_map, SNRp_cut=3., SNRi_cut=30.):
|
|
#Get data
|
|
stkI = Stokes[np.argmax([Stokes[i].header['datatype']=='I_stokes' for i in range(len(Stokes))])]
|
|
stk_cov = Stokes[np.argmax([Stokes[i].header['datatype']=='IQU_cov_matrix' for i in range(len(Stokes))])]
|
|
pol = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg_debiased' for i in range(len(Stokes))])]
|
|
pol_err = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_deg_err' for i in range(len(Stokes))])]
|
|
pang = Stokes[np.argmax([Stokes[i].header['datatype']=='Pol_ang' for i in range(len(Stokes))])]
|
|
|
|
wcs1 = WCS(Stokes[0]).deepcopy()
|
|
convert_flux = Stokes[0].header['photflam']
|
|
wcs2 = WCS(other_map).deepcopy()
|
|
|
|
#Compute SNR and apply cuts
|
|
pol.data[pol.data == 0.] = np.nan
|
|
SNRp = pol.data/pol_err.data
|
|
SNRp[np.isnan(SNRp)] = 0.
|
|
pol.data[SNRp < SNRp_cut] = np.nan
|
|
SNRi = stkI.data/np.sqrt(stk_cov.data[0,0])
|
|
SNRi[np.isnan(SNRi)] = 0.
|
|
pol.data[SNRi < SNRi_cut] = np.nan
|
|
|
|
plt.rcParams.update({'font.size': 16})
|
|
self.fig = plt.figure(figsize=(25,15))
|
|
#Plot the UV map
|
|
self.ax1 = self.fig.add_subplot(121, projection=wcs1)
|
|
self.ax1.set_facecolor('k')
|
|
|
|
vmin, vmax = 0., np.max(stkI.data[stkI.data > 0.]*convert_flux)
|
|
im1 = self.ax1.imshow(stkI.data*convert_flux, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
|
|
fontprops = fm.FontProperties(size=16)
|
|
px_size = wcs1.wcs.get_cdelt()[0]*3600.
|
|
px_sc = AnchoredSizeBar(self.ax1.transData, 1./px_size, '1 arcsec', 3, pad=0.5, sep=5, borderpad=0.5, frameon=False, size_vertical=0.005, color='w', fontproperties=fontprops)
|
|
self.ax1.add_artist(px_sc)
|
|
|
|
north_dir1 = AnchoredDirectionArrows(self.ax1.transAxes, "E", "N", length=-0.08, fontsize=0.03, loc=1, aspect_ratio=-1, sep_y=0.01, sep_x=0.01, angle=-Stokes[0].header['orientat'], color='w', arrow_props={'ec': 'w', 'fc': 'w', 'alpha': 1,'lw': 2})
|
|
self.ax1.add_artist(north_dir1)
|
|
|
|
pol.data[np.isfinite(pol.data)] = 1./2.
|
|
step_vec = 1
|
|
X, Y = np.meshgrid(np.linspace(0,stkI.data.shape[0],stkI.data.shape[0]), np.linspace(0,stkI.data.shape[1],stkI.data.shape[1]))
|
|
U, V = pol.data*np.cos(np.pi/2.+pang.data*np.pi/180.), pol.data*np.sin(np.pi/2.+pang.data*np.pi/180.)
|
|
Q = self.ax1.quiver(X[::step_vec,::step_vec],Y[::step_vec,::step_vec],U[::step_vec,::step_vec],V[::step_vec,::step_vec],units='xy',angles='uv',scale=0.5,scale_units='xy',pivot='mid',headwidth=0.,headlength=0.,headaxislength=0.,width=0.1,color='w')
|
|
|
|
self.ax1.set_title("Click on selected point of reference.")
|
|
|
|
#Plot the other map
|
|
self.ax2 = self.fig.add_subplot(122, projection=wcs2)
|
|
self.ax2.set_facecolor('k')
|
|
|
|
vmin, vmax = 0., np.max(other_map.data[other_map.data > 0.])
|
|
im2 = self.ax2.imshow(other_map.data, vmin=vmin, vmax=vmax, aspect='auto', cmap='inferno', alpha=1.)
|
|
|
|
fontprops = fm.FontProperties(size=16)
|
|
px_size = wcs2.wcs.get_cdelt()[0]*3600.
|
|
px_sc = AnchoredSizeBar(self.ax2.transData, 1./px_size, '1 arcsec', 3, pad=0.5, sep=5, borderpad=0.5, frameon=False, size_vertical=0.005, color='w', fontproperties=fontprops)
|
|
self.ax2.add_artist(px_sc)
|
|
|
|
self.ax2.set_title("Click on selected point of reference.")
|