def test_percent(self): """Test percent keywords.""" norm = simple_norm(DATA2, stretch='linear', percent=99.) assert_allclose(norm(DATA2), DATA2SCL, atol=0, rtol=1.e-5) norm2 = simple_norm(DATA2, stretch='linear', min_percent=0.5, max_percent=99.5) assert_allclose(norm(DATA2), norm2(DATA2), atol=0, rtol=1.e-5)
def test_percent(self): """Test percent keywords.""" norm = simple_norm(DATA2, stretch='linear', percent=99., clip=True) assert_allclose(norm(DATA2), DATA2SCL, atol=0, rtol=1.e-5) norm2 = simple_norm(DATA2, stretch='linear', min_percent=0.5, max_percent=99.5, clip=True) assert_allclose(norm(DATA2), norm2(DATA2), atol=0, rtol=1.e-5)
def test_invalid_data(self): data = np.arange(25.).reshape((5, 5)) data[2, 2] = np.nan data[1, 2] = np.inf percent = 85.0 interval = PercentileInterval(percent) # initialized without data norm = ImageNormalize(interval=interval) norm(data) # sets vmin/vmax assert_equal((norm.vmin, norm.vmax), (1.65, 22.35)) # initialized with data norm2 = ImageNormalize(data, interval=interval) assert_equal((norm2.vmin, norm2.vmax), (norm.vmin, norm.vmax)) norm3 = simple_norm(data, 'linear', percent=percent) assert_equal((norm3.vmin, norm3.vmax), (norm.vmin, norm.vmax)) assert_allclose(norm(data), norm2(data)) assert_allclose(norm(data), norm3(data)) norm4 = ImageNormalize() norm4(data) # sets vmin/vmax assert_equal((norm4.vmin, norm4.vmax), (0, 24)) norm5 = ImageNormalize(data) assert_equal((norm5.vmin, norm5.vmax), (norm4.vmin, norm4.vmax))
def plot_image(image): fig = plt.figure(figsize=(10, 5)) ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], projection=image.wcs) ax.set_xlim(-0.5, image.data.shape[1] - 0.5) ax.set_ylim(-0.5, image.data.shape[0] - 0.5) norm = simple_norm( image.data, stretch='power', power=0.4, min_cut=0, max_cut=0.5, ) print('image data max ', image.data.max()) ax.imshow( image.data, origin='lower', norm=norm, cmap=plt.cm.gist_heat, interpolation='none', ) plt.axis('off')
def test_sqrt_invalid_kw(self, invalid): stretch = SqrtStretch() norm1 = simple_norm(DATA3, stretch='sqrt', min_cut=-1, max_cut=1, clip=False, invalid=invalid) norm2 = ImageNormalize(stretch=stretch, vmin=-1, vmax=1, clip=False, invalid=invalid) assert_equal(norm1(DATA3), norm2(DATA3))
def test_min(self): """Test linear scaling.""" norm = simple_norm(DATA2, stretch='linear', min_cut=1., clip=True) assert_allclose(norm(DATA2), [0., 0., 1.], atol=0, rtol=1.e-5)
def test_asinh(self): """Test asinh scaling.""" norm = simple_norm(DATA2, stretch='asinh') ref = np.arcsinh(10 * DATA2SCL) / np.arcsinh(10) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_log(self): """Test log10 scaling.""" norm = simple_norm(DATA2, stretch='log') ref = np.log10(1000 * DATA2SCL + 1.0) / np.log10(1001.0) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_sqrt(self): """Test sqrt scaling.""" norm1 = simple_norm(DATA2, stretch='sqrt') assert_allclose(norm1(DATA2), np.sqrt(DATA2SCL), atol=0, rtol=1.e-5)
#result.image # In[ ]: import matplotlib.pyplot as plt # In[ ]: from astropy.visualization.mpl_normalize import simple_norm # In[ ]: fig = plt.figure() ax = fig.add_subplot(1, 1, 1) norm = simple_norm(result.image, 'sqrt', min_percent=1, max_percent=99) im = ax.imshow(result.image, origin='lower', norm=norm, cmap='gray_r') fig.colorbar(im) # <hr> # # Visualization # ## Scatter Plot using Matplotlib # In[ ]: get_ipython().run_line_magic('matplotlib', 'notebook') from ipywidgets import * import numpy as np import matplotlib.pyplot as plt
import numpy as np import matplotlib matplotlib.use('agg') from hips import HipsTileMeta, HipsTileAllskyArray import matplotlib.pyplot as plt from astropy.visualization.mpl_normalize import simple_norm meta_fits = HipsTileMeta(order=3, ipix=497, file_format='fits') url = 'https://github.com/hipspy/hips-extra/raw/master/datasets/samples/IRAC4/Norder3/Allsky.fits' allsky = HipsTileAllskyArray.fetch(meta_fits, url) data = allsky.data.astype('float') norm = simple_norm(data, 'log', min_cut=0, max_cut=data.max()) plt.figure() plt.imshow(data, norm=norm, origin='lower') plt.tight_layout() plt.savefig('Allsky_from_FITS.jpg') tile = allsky.tile(271) data = tile.data norm = simple_norm(data, 'linear', min_cut=0, max_cut=data.max()) plt.figure() plt.imshow(data, norm=norm, origin='lower') plt.tight_layout() plt.savefig('Tile_from_FITS.jpg') # Print infos about the brightest pixel in the FITS image max_value = allsky.data.max() idx = np.where(allsky.data == max_value) print('Brightest pixel value:', max_value) print('Brightest pixel in allsky data:', idx) for tile in allsky.tiles:
def make_clean_comparison_fig(u, v, vis, weights, sol, clean_profile, bin_widths, stretch='power', gamma=1.0, asinh_a=0.02, mean_convolved=None, dist=None, force_style=True, save_prefix=None, figsize=(8, 10)): r""" Produce a figure comparing a frank fit to a CLEAN fit, in real space by convolving the frank fit with the CLEAN beam, and in visibility space by taking the discrete Hankel transform of the CLEAN profile Parameters ---------- u, v : array, unit = :math:`\lambda` u and v coordinates of observations vis : array, unit = Jy Observed visibilities (complex: real + imag * 1j) weights : array, unit = Jy^-2 Weights assigned to observed visibilities, of the form :math:`1 / \sigma^2` sol : _HankelRegressor object Reconstructed profile using Maximum a posteriori power spectrum (see frank.radial_fitters.FrankFitter) clean_profile : dict Dictionary with entries 'r' for the radial points [arcsec], 'I' for the brightness [Jy / sr], and optionally the negative and positive brightness uncertainties 'lo_err' and 'hi_err' [Jy / sr]. If only the negative uncertainty is provided, the positive uncertainty is assumed equal to it bin_widths : list, unit = \lambda Bin widths in which to bin the observed visibilities stretch : string, default = 'power' Transformation to apply to the colorscale. The default 'power' is a power law stretch. The other option is 'asinh', an arcsinh stretch, which requires astropy.visualization.mpl_normalize.simple_norm gamma : float, default = 1.0 Index of power law normalization to apply to swept profile image's colormap (see matplotlib.colors.PowerNorm). gamma=1.0 yields a linear colormap asinh_a : float, default = 0.02 Scale parameter for an asinh stretch mean_convolved : None (default) or array, unit = Jy / sr frank brightness profile convolved with a CLEAN beam (see utilities.convolve_profile). The assumed unit is for the x-label dist : float, optional, unit = AU, default = None Distance to source, used to show second x-axis in [AU] force_style: bool, default = True Whether to use preconfigured matplotlib rcParams in generated figure save_prefix : string, default = None Prefix for saved figure name. If None, the figure won't be saved figsize : tuple = (width, height) of figure, unit = inch Returns ------- fig : Matplotlib `.Figure` instance The produced figure, including the GridSpec axes : Matplotlib `~.axes.Axes` class The axes of the produced figure """ logging.info(' Making CLEAN comparison figure') with frank_plotting_style_context_manager(force_style): gs = GridSpec(3, 1) gs2 = GridSpec(3, 3) fig = plt.figure(figsize=figsize) ax0 = fig.add_subplot(gs[0]) ax1 = fig.add_subplot(gs[1]) ax2 = fig.add_subplot(gs2[6]) ax3 = fig.add_subplot(gs2[7]) ax4 = fig.add_subplot(gs2[8]) axes = [ax0, ax1, ax2, ax3, ax4] if 'lo_err' in clean_profile.keys(): low_uncer = clean_profile['lo_err'] else: low_uncer = None if 'hi_err' in clean_profile.keys(): high_uncer = clean_profile['hi_err'] else: high_uncer = None plot_brightness_profile(clean_profile['r'], clean_profile['I'] / 1e10, ax0, low_uncer=low_uncer, high_uncer=high_uncer, c='b', ls='--', label='CLEAN') plot_brightness_profile(sol.r, sol.mean / 1e10, ax0, c='r', ls=':', label='frank') if mean_convolved is not None: plot_brightness_profile(sol.r, mean_convolved / 1e10, ax0, c='k', ls='-', label='frank, convolved') if dist: ax0_5 = plot_brightness_profile(sol.r, sol.mean / 1e10, ax0, dist=dist, c='r', ls=':') u_deproj, v_deproj, vis_deproj = sol.geometry.apply_correction(u, v, vis) baselines = (u_deproj**2 + v_deproj**2)**.5 grid = np.logspace(np.log10(min(baselines.min(), sol.q[0])), np.log10(max(baselines.max(), sol.q[-1])), 10**4 ) for i in range(len(bin_widths)): binned_vis = UVDataBinner(baselines, vis_deproj, weights, bin_widths[i]) vis_re_kl = binned_vis.V.real * 1e3 vis_err_re_kl = binned_vis.error.real * 1e3 plot_vis_quantity(binned_vis.uv, vis_re_kl, ax1, c=cs[i], marker=ms[i], ls='None', label=r'Obs.>0, {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) plot_vis_quantity(binned_vis.uv, -vis_re_kl, ax1, c=cs2[i], marker=ms[i], ls='None', label=r'Obs.<0, {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) vis_fit_kl = sol.predict_deprojected(grid).real * 1e3 # Take the discrete Hankel transform of the CLEAN profile, using the same # collocation points for the DHT as those in the frank fit Inu_interp = np.interp( sol.r, clean_profile['r'], clean_profile['I'].real) * 1e3 clean_DHT_kl = sol.predict_deprojected(grid, I=Inu_interp) plot_vis_quantity(grid, vis_fit_kl, ax1, c='r', label='frank>0') plot_vis_quantity(grid, -vis_fit_kl, ax1, c='r', ls='--', label='frank<0') plot_vis_quantity(grid, clean_DHT_kl, ax1, c='b', label='DHT of CLEAN>0') plot_vis_quantity(grid, -clean_DHT_kl, ax1, c='b', ls='--', label='DHT of CLEAN<0') if mean_convolved is not None: vmax = max(sol.mean.max(), mean_convolved.max(), clean_profile['I'].max()) else: vmax = max(sol.mean.max(), clean_profile['I'].max()) if stretch == 'asinh': vmin = max(0, min(sol.mean)) from astropy.visualization.mpl_normalize import simple_norm norm = simple_norm(sol.mean, stretch='asinh', asinh_a=asinh_a, min_cut=vmin) elif stretch == 'power': vmin = 0 norm = PowerNorm(gamma, vmin, vmax) else: err = ValueError("Unknown 'stretch'. Should be one of 'power' or 'asinh'") raise err plot_2dsweep(sol.r, sol.mean, ax=ax2, cmap='inferno', norm=norm, vmin=0, vmax=vmax / 1e10, xmax=sol.Rmax, plot_colorbar=True) if mean_convolved is not None: plot_2dsweep(sol.r, mean_convolved, ax=ax3, cmap='inferno', norm=norm, vmin=0, vmax=vmax / 1e10, xmax=sol.Rmax, plot_colorbar=True) # Interpolate the CLEAN profile onto the frank grid to ensure the CLEAN # swept 'image' has the same pixel resolution as the frank swept 'images' from scipy.interpolate import interp1d interp = interp1d(clean_profile['r'], clean_profile['I']) regrid_I_clean = interp(sol.r) plot_2dsweep(sol.r, regrid_I_clean, ax=ax4, cmap='inferno', norm=norm, vmin=0, vmax=vmax / 1e10, xmax=sol.Rmax, plot_colorbar=True) ax0.legend(loc='best') ax1.legend(loc='best') ax0.set_xlabel('r ["]') ax0.set_ylabel(r'Brightness [$10^{10}$ Jy sr$^{-1}$]') ax1.set_xlabel(r'Baseline [$\lambda$]') ax1.set_ylabel(r'Re(V) [mJy]') ax2.set_xlabel('RA offset ["]') ax3.set_xlabel('RA offset ["]') ax4.set_xlabel('RA offset ["]') ax2.set_ylabel('Dec offset ["]') ax0.set_xlim(right=sol.Rmax) if dist: xlims = ax0.get_xlim() ax0_5.set_xlim(np.multiply(xlims, dist)) ax1.set_xlim(.9 * baselines.min(), 1.2 * baselines.max()) ax1.set_xscale('log') ax1.set_yscale('log') ax1.set_ylim(bottom=1e-3) ax2.set_title('Unconvolved frank profile swept') ax3.set_title('Convolved frank profile swept') ax4.set_title('CLEAN profile swept') ax0.text(.5, .9, 'a)', transform=ax0.transAxes) ax1.text(.5, .9, 'b)', transform=ax1.transAxes) ax2.text(.1, .9, 'c)', c='w', transform=ax2.transAxes) ax3.text(.1, .9, 'd)', c='w', transform=ax3.transAxes) ax4.text(.1, .9, 'e)', c='w', transform=ax4.transAxes) if save_prefix: plt.savefig(save_prefix + '_frank_clean_comparison.png', dpi=600) plt.close() return fig, axes
def make_full_fig(u, v, vis, weights, sol, bin_widths, alpha, wsmooth, dist=None, logx=True, force_style=True, save_prefix=None, norm_residuals=False, stretch='power', gamma=1.0, asinh_a=0.02, figsize=(8, 6)): r""" Produce a figure showing a Frankenstein fit and some useful diagnostics Parameters ---------- u, v : array, unit = :math:`\lambda` u and v coordinates of observations vis : array, unit = Jy Observed visibilities (complex: real + imag * 1j) weights : array, unit = Jy^-2 Weights assigned to observed visibilities, of the form :math:`1 / \sigma^2` sol : _HankelRegressor object Reconstructed profile using Maximum a posteriori power spectrum (see frank.radial_fitters.FrankFitter) bin_widths : list, unit = \lambda Bin widths in which to bin the observed visibilities alpha : float Value for the :math:`\alpha` hyperparameter. Used for the plot legends wsmooth : float Value for the :math:`w_{smooth}` hyperparameter. Used for the plot legends dist : float, optional, unit = AU, default = None Distance to source, used to show second x-axis in [AU] logx : bool, default = True Whether to plot the visibility distributions in log(baseline) force_style: bool, default = True Whether to use preconfigured matplotlib rcParams in generated figure save_prefix : string, default = None Prefix for saved figure name. If None, the figure won't be saved norm_residuals : bool, default = False Whether to normalize the residual visibilities by the data's visibility amplitudes stretch : string, default = 'power' Transformation to apply to the colorscale. The default 'power' is a power law stretch. The other option is 'asinh', an arcsinh stretch, which requires astropy.visualization.mpl_normalize.simple_norm gamma : float, default = 1.0 Index of power law normalization to apply to swept profile image's colormap (see matplotlib.colors.PowerNorm). gamma=1.0 yields a linear colormap asinh_a : float, default = 0.02 Scale parameter for an asinh stretch figsize : tuple = (width, height) of figure, unit = inch Returns ------- fig : Matplotlib `.Figure` instance The produced figure, including the GridSpec axes : Matplotlib `~.axes.Axes` class The axes of the produced figure """ logging.info(' Making full figure') with frank_plotting_style_context_manager(force_style): gs = GridSpec(3, 3, hspace=0) gs1 = GridSpec(4, 3, hspace=0, top=.88) gs2 = GridSpec(3, 3, hspace=.35, left=.04) fig = plt.figure(figsize=figsize) ax0 = fig.add_subplot(gs[0]) ax1 = fig.add_subplot(gs[3]) ax2 = fig.add_subplot(gs2[6]) ax3 = fig.add_subplot(gs[1]) ax4 = fig.add_subplot(gs[4]) ax5 = fig.add_subplot(gs[7]) ax6 = fig.add_subplot(gs[2]) ax7 = fig.add_subplot(gs1[5]) ax8 = fig.add_subplot(gs1[8]) ax9 = fig.add_subplot(gs1[11]) ax0.text(.9, .6, 'a)', transform=ax0.transAxes) ax1.text(.9, .6, 'b)', transform=ax1.transAxes) ax2.text(.1, .9, 'c)', c='w', transform=ax2.transAxes) ax3.text(.1, .5, 'd)', transform=ax3.transAxes) ax4.text(.1, .7, 'e)', transform=ax4.transAxes) ax5.text(.1, .7, 'f)', transform=ax5.transAxes) ax6.text(.9, .9, 'g)', transform=ax6.transAxes) ax7.text(.9, .9, 'h)', transform=ax7.transAxes) ax8.text(.9, .9, 'i)', transform=ax8.transAxes) ax9.text(.9, .9, 'j)', transform=ax9.transAxes) axes = [ax0, ax1, ax2, ax3, ax4, ax5, ax6, ax7, ax8, ax9] # Calculate the fit's total flux (2D, by sweeping the 1D profile over 2\pi) total_flux = trapz(sol.mean * 2 * np.pi * sol.r, sol.r) # Plot the fitted brightness profile in linear- and log-y plot_brightness_profile(sol.r, sol.mean / 1e10, ax0, c='r', label='frank, total flux {:.2e} Jy'.format(total_flux)) if dist: ax0_5 = plot_brightness_profile(sol.r, sol.mean / 1e10, ax0, dist=dist, c='r') xlims = ax0.get_xlim() ax0_5.set_xlim(np.multiply(xlims, dist)) plot_brightness_profile(sol.r, sol.mean / 1e10, ax1, c='r', label='frank') # Apply deprojection to the provided (u, v) coordinates # and visibility amplitudes u_deproj, v_deproj, vis_deproj = sol.geometry.apply_correction(u, v, vis) baselines = (u_deproj**2 + v_deproj**2)**.5 # Set a grid of baselines on which to plot the visibility domain frank fit grid = np.logspace(np.log10(min(baselines.min(), sol.q[0])), np.log10(max(baselines.max(), sol.q[-1])), 10**4 ) # Map the frank visibility fit to `grid`, considering only the real component # that frank fits vis_fit_kl = sol.predict_deprojected(grid).real * 1e3 # Make a guess of good y-bounds for zooming in on the visibility fit # in linear-y zoom_ylim_guess = abs(vis_fit_kl[np.int(.5 * len(vis_fit_kl)):]).max() zoom_bounds = [-1.1 * zoom_ylim_guess, 1.1 * zoom_ylim_guess] ax4.set_ylim(zoom_bounds) # Bin the observed (real and imaginary components of the) visibilities # for plotting for i in range(len(bin_widths)): binned_vis = UVDataBinner(baselines, vis_deproj, weights, bin_widths[i]) vis_re_kl = binned_vis.V.real * 1e3 vis_im_kl = binned_vis.V.imag * 1e3 vis_err_re_kl = binned_vis.error.real * 1e3 vis_err_im_kl = binned_vis.error.imag * 1e3 vis_fit = sol.predict_deprojected(binned_vis.uv).real * 1e3 # Determine the visiblity domain frank fit residuals (and RMS error) # for Real(V) resid = vis_re_kl - vis_fit if norm_residuals: resid /= vis_re_kl rmse = (np.mean(resid**2))**.5 # Plot the observed, binned visibilities (with errorbars) and the residuals plot_vis_quantity(binned_vis.uv, vis_re_kl, ax3, c=cs[i], marker=ms[i], ls='None', label=r'Obs., {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) plot_vis_quantity(binned_vis.uv, vis_re_kl, ax4, c=cs[i], marker=ms[i], ls='None', label=r'Obs., {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) plot_vis_quantity(binned_vis.uv, vis_re_kl, ax6, c=cs[i], marker=ms[i], ls='None', label=r'Obs.>0, {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) plot_vis_quantity(binned_vis.uv, -vis_re_kl, ax6, c=cs2[i], marker=ms[i], ls='None', label=r'Obs.<0, {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) plot_vis_quantity(binned_vis.uv, vis_im_kl, ax9, c=cs[i], marker=ms[i], ls='None', label=r'Obs., {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) plot_vis_quantity(binned_vis.uv, resid, ax5, c=cs[i], marker=ms[i], ls='None', label=r'{:.0f} k$\lambda$ bins, RMSE {:.3f} mJy'.format(bin_widths[i]/1e3, rmse)) # Plot a histogram of the observed visibilties to examine how the # visibility count varies with baseline plot_vis_hist(binned_vis, ax8, color=hist_cs[i], label=r'Obs., {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) # Plot the visibility domain frank fit in log-y plot_vis_quantity(grid, vis_fit_kl, ax3, c='r', label='frank') plot_vis_quantity(grid, vis_fit_kl, ax4, c='r', label='frank') plot_vis_quantity(grid, vis_fit_kl, ax6, c='r', label='frank>0') plot_vis_quantity(grid, -vis_fit_kl, ax6, c='#1EFEDC', label='frank<0') # Plot the frank inferred power spectrum plot_vis_quantity(sol.q, sol.power_spectrum, ax7, label=r'$\alpha$ {:.2f}'.format( alpha) + '\n' + '$w_{smooth}$' + ' {:.1e}'.format(wsmooth)) # Plot a sweep over 2\pi of the frank 1D fit # (analogous to a model image of the source) vmax = sol.mean.max() if stretch == 'asinh': vmin = max(0, min(sol.mean)) from astropy.visualization.mpl_normalize import simple_norm norm = simple_norm(sol.mean, stretch='asinh', asinh_a=asinh_a, min_cut=vmin) elif stretch == 'power': vmin = 0 norm = PowerNorm(gamma, vmin, vmax) else: err = ValueError("Unknown 'stretch'. Should be one of 'power' or 'asinh'") raise err plot_2dsweep(sol.r, sol.mean, ax=ax2, cmap='inferno', norm=norm, vmin=vmin, vmax=vmax / 1e10, project=True, geom=sol.geometry) ax1.set_xlabel('r ["]') ax0.set_ylabel(r'Brightness [$10^{10}$ Jy sr$^{-1}$]') ax1.set_ylabel(r'Brightness [$10^{10}$ Jy sr$^{-1}$]') ax1.set_yscale('log') ax1.set_ylim(bottom=1e-3) ax2.set_xlabel('RA offset ["]') ax2.set_ylabel('Dec offset ["]') ax3.set_ylabel('Re(V) [mJy]') ax4.set_ylabel('Re(V) [mJy]') if norm_residuals: ax5.set_ylabel('Norm. residual') else: ax5.set_ylabel('Residual [mJy]') ax5.set_xlabel(r'Baseline [$\lambda$]') ax6.set_ylabel('Re(V) [mJy]') ax7.set_ylabel(r'Power [Jy$^2$]') ax8.set_ylabel('Count') ax9.set_ylabel('Im(V) [mJy]') ax9.set_xlabel(r'Baseline [$\lambda$]') if logx: ax3.set_xscale('log') ax4.set_xscale('log') ax5.set_xscale('log') ax6.set_xscale('log') ax7.set_xscale('log') ax8.set_xscale('log') ax9.set_xscale('log') xlims = ax5.get_xlim() ax3.set_xlim(xlims) ax4.set_xlim(xlims) ax6.set_xlim(xlims) ax7.set_xlim(xlims) ax8.set_xlim(xlims) ax9.set_xlim(xlims) ax6.set_yscale('log') ax7.set_yscale('log') ax8.set_yscale('log') ax6.set_ylim(bottom=1e-4) plt.setp(ax0.get_xticklabels(), visible=False) plt.setp(ax3.get_xticklabels(), visible=False) plt.setp(ax4.get_xticklabels(), visible=False) plt.setp(ax6.get_xticklabels(), visible=False) plt.setp(ax7.get_xticklabels(), visible=False) plt.setp(ax8.get_xticklabels(), visible=False) plt.tight_layout() if save_prefix: plt.savefig(save_prefix + '_frank_fit_full.png', dpi=600) plt.close() return fig, axes
def make_quick_fig(u, v, vis, weights, sol, bin_widths, dist=None, logx=True, force_style=True, save_prefix=None, stretch='power', gamma=1.0, asinh_a=0.02, figsize=(8,6)): r""" Produce a simple figure showing just a Frankenstein fit, not any diagnostics Parameters ---------- u, v : array, unit = :math:`\lambda` u and v coordinates of observations vis : array, unit = Jy Observed visibilities (complex: real + imag * 1j) weights : array, unit = Jy^-2 Weights assigned to observed visibilities, of the form :math:`1 / \sigma^2` sol : _HankelRegressor object Reconstructed profile using Maximum a posteriori power spectrum (see frank.radial_fitters.FrankFitter) bin_widths : list, unit = \lambda Bin widths in which to bin the observed visibilities dist : float, optional, unit = AU, default = None Distance to source, used to show second x-axis in [AU] logx : bool, default = True Whether to plot the visibility distributions in log(baseline) gamma : float, default = 1.0 Index of power law normalization to apply to swept profile image's colormap (see matplotlib.colors.PowerNorm). gamma=1.0 yields a linear colormap force_style: bool, default = True Whether to use preconfigured matplotlib rcParams in generated figure save_prefix : string, default = None Prefix for saved figure name. If None, the figure won't be saved stretch : string, default = 'power' Transformation to apply to the colorscale. The default 'power' is a power law stretch. The other option is 'asinh', an arcsinh stretch, which requires astropy.visualization.mpl_normalize.simple_norm asinh_a : float, default = 0.02 Scale parameter for an asinh stretch figsize : tuple = (width, height) of figure, unit = inch Returns ------- fig : Matplotlib `.Figure` instance The produced figure, including the GridSpec axes : Matplotlib `~.axes.Axes` class The axes of the produced figure """ logging.info(' Making quick figure') with frank_plotting_style_context_manager(force_style): gs = GridSpec(3, 2, hspace=0, bottom=.12) gs2 = GridSpec(3, 2, hspace=.2) fig = plt.figure(figsize=figsize) ax0 = fig.add_subplot(gs[0]) ax1 = fig.add_subplot(gs[2]) ax2 = fig.add_subplot(gs[1]) ax3 = fig.add_subplot(gs[3]) ax4 = fig.add_subplot(gs2[4]) ax5 = fig.add_subplot(gs2[5]) ax0.text(.5, .9, 'a)', transform=ax0.transAxes) ax1.text(.5, .9, 'b)', transform=ax1.transAxes) ax2.text(.5, .9, 'c)', transform=ax2.transAxes) ax3.text(.5, .9, 'd)', transform=ax3.transAxes) ax4.text(.5, .9, 'e)', c='w', transform=ax4.transAxes) ax5.text(.5, .9, 'f)', c='w', transform=ax5.transAxes) axes = [ax0, ax1, ax2, ax3, ax4, ax5] total_flux = trapz(sol.mean * 2 * np.pi * sol.r, sol.r) plot_brightness_profile(sol.r, sol.mean / 1e10, ax0, c='r', label='frank, total flux {:.2e} Jy'.format(total_flux)) if dist: ax0_5 = plot_brightness_profile(sol.r, sol.mean / 1e10, ax0, dist=dist, c='r') xlims = ax0.get_xlim() ax0_5.set_xlim(np.multiply(xlims, dist)) plot_brightness_profile(sol.r, sol.mean / 1e10, ax1, c='r', label='frank') u_deproj, v_deproj, vis_deproj = sol.geometry.apply_correction(u, v, vis) baselines = (u_deproj**2 + v_deproj**2)**.5 grid = np.logspace(np.log10(min(baselines.min(), sol.q[0])), np.log10(max(baselines.max(), sol.q[-1])), 10**4) for i in range(len(bin_widths)): binned_vis = UVDataBinner( baselines, vis_deproj, weights, bin_widths[i]) vis_re_kl = binned_vis.V.real * 1e3 vis_err_re_kl = binned_vis.error.real * 1e3 vis_fit = sol.predict_deprojected(binned_vis.uv).real * 1e3 for ax in [ax2, ax3]: plot_vis_quantity(binned_vis.uv / 1e6, vis_re_kl, ax, vis_err_re_kl, c=cs[i], marker=ms[i], ls='None', label=r'Obs., {:.0f} k$\lambda$ bins'.format(bin_widths[i]/1e3)) vis_fit_kl = sol.predict_deprojected(grid).real * 1e3 plot_vis_quantity(grid / 1e6, vis_fit_kl, ax2, c='r', label='frank', zorder=10) # Make a guess of good y-bounds for zooming in on the visibility fit # in linear-y zoom_ylim_guess = abs(vis_fit_kl[np.int(.5 * len(vis_fit_kl)):]).max() zoom_bounds = [-1.1 * zoom_ylim_guess, 1.1 * zoom_ylim_guess] ax3.set_ylim(zoom_bounds) plot_vis_quantity(grid / 1e6, vis_fit_kl, ax3, c='r', label='frank', zorder=10) vmax = sol.mean.max() if stretch == 'asinh': vmin = max(0, min(sol.mean)) from astropy.visualization.mpl_normalize import simple_norm norm = simple_norm(sol.mean, stretch='asinh', asinh_a=asinh_a, min_cut=vmin) elif stretch == 'power': vmin = 0 norm = PowerNorm(gamma, vmin, vmax) else: err = ValueError("Unknown 'stretch'. Should be one of 'power' or 'asinh'") raise err plot_2dsweep(sol.r, sol.mean, ax=ax4, cmap='inferno', norm=norm, vmin=vmin, vmax=vmax / 1e10, project=False) plot_2dsweep(sol.r, sol.mean, ax=ax5, cmap='inferno', norm=norm, vmin=vmin, vmax=vmax / 1e10, project=True, geom=sol.geometry) ax1.set_xlabel('r ["]') ax0.set_ylabel(r'Brightness [$10^{10}$ Jy sr$^{-1}$]') ax1.set_ylabel(r'Brightness [$10^{10}$ Jy sr$^{-1}$]') ax1.set_yscale('log') ax1.set_ylim(bottom=1e-3) ax3.set_xlabel(r'Baseline [M$\lambda$]') ax2.set_ylabel('Re(V) [mJy]') ax3.set_ylabel('Re(V) [mJy]') if logx: ax2.set_xscale('log') ax3.set_xscale('log') else: ax2.set_xlim(0., max(binned_vis.uv) / 1e6 * 1.1) xlims = ax2.get_xlim() ax3.set_xlim(xlims) ax4.set_xlabel('RA offset ["]') ax4.set_ylabel('Dec offset ["]') ax5.set_xlabel('RA offset ["]') ax5.set_ylabel('Dec offset ["]') plt.setp(ax0.get_xticklabels(), visible=False) plt.setp(ax2.get_xticklabels(), visible=False) plt.tight_layout() if save_prefix: plt.savefig(save_prefix + '_frank_fit_quick.png', dpi=600) plt.close() return fig, axes
def fits2bitmap(filename, ext=0, out_fn=None, stretch='linear', power=1.0, asinh_a=0.1, min_cut=None, max_cut=None, min_percent=None, max_percent=None, percent=None, cmap='Greys_r'): """ Create a bitmap file from a FITS image, applying a stretching transform between minimum and maximum cut levels and a matplotlib colormap. Parameters ---------- filename : str The filename of the FITS file. ext : int FITS extension name or number of the image to convert. The default is 0. out_fn : str The filename of the output bitmap image. The type of bitmap is determined by the filename extension (e.g. '.jpg', '.png'). The default is a PNG file with the same name as the FITS file. stretch : {{'linear', 'sqrt', 'power', log', 'asinh'}} The stretching function to apply to the image. The default is 'linear'. power : float, optional The power index for ``stretch='power'``. The default is 1.0. asinh_a : float, optional For ``stretch='asinh'``, the value where the asinh curve transitions from linear to logarithmic behavior, expressed as a fraction of the normalized image. Must be in the range between 0 and 1. The default is 0.1. min_cut : float, optional The pixel value of the minimum cut level. Data values less than ``min_cut`` will set to ``min_cut`` before stretching the image. The default is the image minimum. ``min_cut`` overrides ``min_percent``. max_cut : float, optional The pixel value of the maximum cut level. Data values greater than ``min_cut`` will set to ``min_cut`` before stretching the image. The default is the image maximum. ``max_cut`` overrides ``max_percent``. min_percent : float, optional The percentile value used to determine the pixel value of minimum cut level. The default is 0.0. ``min_percent`` overrides ``percent``. max_percent : float, optional The percentile value used to determine the pixel value of maximum cut level. The default is 100.0. ``max_percent`` overrides ``percent``. percent : float, optional The percentage of the image values used to determine the pixel values of the minimum and maximum cut levels. The lower cut level will set at the ``(100 - percent) / 2`` percentile, while the upper cut level will be set at the ``(100 + percent) / 2`` percentile. The default is 100.0. ``percent`` is ignored if either ``min_percent`` or ``max_percent`` is input. cmap : str The matplotlib color map name. The default is 'Greys_r'. """ import matplotlib import matplotlib.cm as cm import matplotlib.image as mimg # __main__ gives ext as a string try: ext = int(ext) except ValueError: pass try: image = getdata(filename, ext) except Exception as e: log.critical(e) return 1 if image.ndim != 2: log.critical('data in FITS extension {0} is not a 2D array' .format(ext)) if out_fn is None: out_fn = os.path.splitext(filename)[0] if out_fn.endswith('.fits'): out_fn = os.path.splitext(out_fn)[0] out_fn += '.png' # need to explicitly define the output format due to a bug in # matplotlib (<= 2.1), otherwise the format will always be PNG out_format = os.path.splitext(out_fn)[1][1:] # workaround for matplotlib 2.0.0 bug where png images are inverted # (mpl-#7656) if (out_format.lower() == 'png' and LooseVersion(matplotlib.__version__) == LooseVersion('2.0.0')): image = image[::-1] if cmap not in cm.datad: log.critical('{0} is not a valid matplotlib colormap name.' .format(cmap)) return 1 norm = simple_norm(image, stretch=stretch, power=power, asinh_a=asinh_a, min_cut=min_cut, max_cut=max_cut, min_percent=min_percent, max_percent=max_percent, percent=percent) mimg.imsave(out_fn, norm(image), cmap=cmap, origin='lower', format=out_format) log.info('Saved file to {0}.'.format(out_fn))
def test_asinh(self): """Test asinh scaling.""" a = 0.1 norm = simple_norm(DATA2, stretch='asinh', asinh_a=a) ref = np.arcsinh(DATA2SCL / a) / np.arcsinh(1. / a) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_min(self): """Test linear scaling.""" norm = simple_norm(DATA2, stretch='linear', min_cut=1.) assert_allclose(norm(DATA2), [0., 0., 1.], atol=0, rtol=1.e-5)
def test_asinh(self): """Test asinh scaling.""" a = 0.1 norm = simple_norm(DATA2, stretch='asinh', asinh_a=a) ref = np.arcsinh(DATA2SCL / a) / np.arcsinh(1. / a) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_invalid_stretch(self): """Test invalid stretch keyword.""" with pytest.raises(ValueError): simple_norm(DATA2, stretch='invalid')
def test_linear(self): """Test linear scaling.""" norm = simple_norm(DATA2, stretch='linear') assert_allclose(norm(DATA2), DATA2SCL, atol=0, rtol=1.e-5)
def fits2bitmap(filename, ext=0, out_fn=None, stretch='linear', power=1.0, asinh_a=0.1, min_cut=None, max_cut=None, min_percent=None, max_percent=None, percent=None, cmap='Greys_r'): """ Create a bitmap file from a FITS image, applying a stretching transform between minimum and maximum cut levels and a matplotlib colormap. Parameters ---------- filename : str The filename of the FITS file. ext : int FITS extension name or number of the image to convert. The default is 0. out_fn : str The filename of the output bitmap image. The type of bitmap is determined by the filename extension (e.g. '.jpg', '.png'). The default is a PNG file with the same name as the FITS file. stretch : {{'linear', 'sqrt', 'power', log', 'asinh'}} The stretching function to apply to the image. The default is 'linear'. power : float, optional The power index for ``stretch='power'``. The default is 1.0. asinh_a : float, optional For ``stretch='asinh'``, the value where the asinh curve transitions from linear to logarithmic behavior, expressed as a fraction of the normalized image. Must be in the range between 0 and 1. The default is 0.1. min_cut : float, optional The pixel value of the minimum cut level. Data values less than ``min_cut`` will set to ``min_cut`` before stretching the image. The default is the image minimum. ``min_cut`` overrides ``min_percent``. max_cut : float, optional The pixel value of the maximum cut level. Data values greater than ``min_cut`` will set to ``min_cut`` before stretching the image. The default is the image maximum. ``max_cut`` overrides ``max_percent``. min_percent : float, optional The percentile value used to determine the pixel value of minimum cut level. The default is 0.0. ``min_percent`` overrides ``percent``. max_percent : float, optional The percentile value used to determine the pixel value of maximum cut level. The default is 100.0. ``max_percent`` overrides ``percent``. percent : float, optional The percentage of the image values used to determine the pixel values of the minimum and maximum cut levels. The lower cut level will set at the ``(100 - percent) / 2`` percentile, while the upper cut level will be set at the ``(100 + percent) / 2`` percentile. The default is 100.0. ``percent`` is ignored if either ``min_percent`` or ``max_percent`` is input. cmap : str The matplotlib color map name. The default is 'Greys_r'. """ import matplotlib import matplotlib.cm as cm import matplotlib.image as mimg # __main__ gives ext as a string try: ext = int(ext) except ValueError: pass try: image = getdata(filename, ext) except Exception as e: log.critical(e) return 1 if image.ndim != 2: log.critical('data in FITS extension {} is not a 2D array' .format(ext)) if out_fn is None: out_fn = os.path.splitext(filename)[0] if out_fn.endswith('.fits'): out_fn = os.path.splitext(out_fn)[0] out_fn += '.png' # need to explicitly define the output format due to a bug in # matplotlib (<= 2.1), otherwise the format will always be PNG out_format = os.path.splitext(out_fn)[1][1:] # workaround for matplotlib 2.0.0 bug where png images are inverted # (mpl-#7656) if (out_format.lower() == 'png' and LooseVersion(matplotlib.__version__) == LooseVersion('2.0.0')): image = image[::-1] try: cm.get_cmap(cmap) except ValueError: log.critical('{} is not a valid matplotlib colormap name.' .format(cmap)) return 1 norm = simple_norm(image, stretch=stretch, power=power, asinh_a=asinh_a, min_cut=min_cut, max_cut=max_cut, min_percent=min_percent, max_percent=max_percent, percent=percent) mimg.imsave(out_fn, norm(image), cmap=cmap, origin='lower', format=out_format) log.info(f'Saved file to {out_fn}.')
def test_sqrt(self): """Test sqrt scaling.""" norm = simple_norm(DATA2, stretch='sqrt') assert_allclose(norm(DATA2), np.sqrt(DATA2SCL), atol=0, rtol=1.e-5)
def test_linear(self): """Test linear scaling.""" norm = simple_norm(DATA2, stretch='linear') assert_allclose(norm(DATA2), DATA2SCL, atol=0, rtol=1.e-5)
def test_power(self): """Test power scaling.""" power = 3.0 norm = simple_norm(DATA2, stretch='power', power=power) assert_allclose(norm(DATA2), DATA2SCL ** power, atol=0, rtol=1.e-5)
def test_power(self): """Test power scaling.""" power = 3.0 norm = simple_norm(DATA2, stretch='power', power=power) assert_allclose(norm(DATA2), DATA2SCL**power, atol=0, rtol=1.e-5)
def test_log(self): """Test log10 scaling.""" norm = simple_norm(DATA2, stretch='log') ref = np.log10(1000 * DATA2SCL + 1.0) / np.log10(1001.0) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_log_with_log_a(self): """Test log10 scaling with a custom log_a.""" log_a = 100 norm = simple_norm(DATA2, stretch='log', log_a=log_a) ref = np.log10(log_a * DATA2SCL + 1.0) / np.log10(log_a + 1) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_log_with_log_a(self): """Test log10 scaling with a custom log_a.""" log_a = 100 norm = simple_norm(DATA2, stretch='log', log_a=log_a) ref = np.log10(log_a * DATA2SCL + 1.0) / np.log10(log_a + 1) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_asinh_with_asinh_a(self): """Test asinh scaling with a custom asinh_a.""" asinh_a = 0.5 norm = simple_norm(DATA2, stretch='asinh', asinh_a=asinh_a) ref = np.arcsinh(DATA2SCL / asinh_a) / np.arcsinh(1. / asinh_a) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_asinh(self): """Test asinh scaling.""" norm = simple_norm(DATA2, stretch='asinh') ref = np.arcsinh(10 * DATA2SCL) / np.arcsinh(10) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)
def test_invalid_stretch(self): """Test invalid stretch keyword.""" with pytest.raises(ValueError): simple_norm(DATA2, stretch='invalid')
def test_asinh_with_asinh_a(self): """Test asinh scaling with a custom asinh_a.""" asinh_a = 0.5 norm = simple_norm(DATA2, stretch='asinh', asinh_a=asinh_a) ref = np.arcsinh(DATA2SCL / asinh_a) / np.arcsinh(1. / asinh_a) assert_allclose(norm(DATA2), ref, atol=0, rtol=1.e-5)