コード例 #1
0
def display_bins_generators(xBin, yBin, velBin, x, y, angle=None, **kwargs):
    """
    Displays a Voronoi binned map starting from the original coordinates of the pixels
    and the coordinates of the *generators* (not the centroids!) of the Voronoi
    tessellation, as provided in output e.g. by my voronoi_2d_binning routine.

    NB: When possible, insted of this routine, one should use the more general display_bins
    routine which uses the binNumber of every spaxel insted of the Voronoi generators.

    :param xBin: coordinates of the *generators* of the Voronoi tessellation
    :param yBin:
    :param velBin:
    :param x: coordinates of the original spaxels
    :param y:
    :param angle:
    :param kwargs:
    :return: image
    """

    warnings.warn('When possible, usage of the routine display_bins is preferred to display_bins_generators')

    if not (xBin.size == yBin.size == velBin.size):
        raise ValueError('The vectors (XBIN, YBIN, VEL) must have the same size')
    if x.size != y.size:
        raise ValueError('The vectors (X, Y) must have the same size')
    if x.size < xBin.size:
        raise ValueError('The vectors (X, Y) cannot be smaller than (XBIN, YBIN)')
    
    # Perform a Voronoi tessellation starting from the coordinates
    # of the generators and the coordinates of the original pixels
    #
    binNum = np.argmin((x[:, np.newaxis] - xBin)**2 + (y[:, np.newaxis] - yBin)**2, axis=1)
    f = display_pixels(x, y, velBin[binNum], angle=angle, **kwargs)
    
    return f
コード例 #2
0
def sn_plot(galname, sndata):
	fig = plt.figure(figsize=(7, 5))
	fig.add_subplot(1, 1, 1)
	ax1 = display_pixels(sndata[0, :], sndata[1, :], sndata[2, :], colorbar=1, label='S/N')
	ax1.axes.set_xlabel(r'SPAXEL')
	ax1.axes.set_ylabel(r'SPAXEL')
	fig.savefig(galname + '_sn.pdf', facecolor='None')
コード例 #3
0
def dual_plot(grid, SX, SY, SVEL, GX, GY, GVEL, stellar_pa, halpha_pa):
    fig, ax = plt.subplots(1,2, figsize=(10,3))
    # display_pixels doesn't like obj-orientated matplotlib
    plt.sca(ax[0])
    display_pixels(SX, SY, SVEL, colorbar=True)
    ax[0].set_title('STAR-PA: '+str(stellar_pa))
    
    # Plotting 0 and 90 deg lines.
    rad = np.sqrt(np.max(grid[0]**2 + grid[1]**2))
    ang = [0, -np.pi] 
    ax[0].plot(rad*np.cos(ang), rad*np.sin(ang), linestyle='dashed', color='gray', linewidth=3, alpha=0.2)
    ax[1].plot(rad*np.cos(ang), rad*np.sin(ang), linestyle='dashed', color='gray', linewidth=3, alpha=0.2)
    
    ang =  [-np.pi/2, -np.pi*3/2]
    ax[0].plot(rad*np.cos(ang), rad*np.sin(ang), linestyle='dashed', color='gray', linewidth=3, alpha=0.2)
    ax[1].plot(rad*np.cos(ang), rad*np.sin(ang), linestyle='dashed', color='gray', linewidth=3, alpha=0.2)
    
    # Plotting pa lines.
    if stellar_pa != np.nan:
        rad = np.sqrt(np.max(grid[0]**2 + grid[1]**2))
        ang = [0,np.pi] + np.radians(stellar_pa)
        ax[0].plot(rad*np.cos(-ang), rad*np.sin(-ang), 'k--', linewidth=3) # Zero-velocity line
        ax[0].plot(-rad*np.sin(-ang), rad*np.cos(-ang), color="limegreen", linewidth=3) # Major axis PA
    
    # limits and labels.
    ax[0].set_xlim([np.min(grid[0]), np.max(grid[0])])
    ax[0].set_ylim([np.min(grid[1]), np.max(grid[1])])
    ax[0].set_xlabel('arcsec')
    ax[0].set_ylabel('arcsec')

    plt.sca(ax[1])
    display_pixels(GX, GY, GVEL, colorbar=True)
    ax[1].set_title('GAS-PA: '+str(halpha_pa))
    
    # Plotting pa lines.
    if halpha_pa != np.nan:
        rad = np.sqrt(np.max(grid[0]**2 + grid[1]**2))
        ang = [0,np.pi] + np.radians(halpha_pa)
        ax[1].plot(rad*np.cos(-ang), rad*np.sin(-ang), 'k--', linewidth=3) # Zero-velocity line
        ax[1].plot(-rad*np.sin(-ang), rad*np.cos(-ang), color="limegreen", linewidth=3) # Major axis PA
    
    # limits and labels.
    ax[1].set_xlim([np.min(grid[0]), np.max(grid[0])])
    ax[1].set_ylim([np.min(grid[1]), np.max(grid[1])])
    ax[1].set_xlabel('arcsec')
    ax[1].set_ylabel('arcsec')
    return
コード例 #4
0
def display_bins(x, y, bin_num, vel_bin, **kwargs):

    assert bin_num.dtype.kind == 'i', "bin_num must be integer"
    assert x.size == y.size == bin_num.size, "The vectors (x, y, bin_num) must have the same size"
    assert np.unique(
        bin_num
    ).size == vel_bin.size, "vel_bin size does not match number of bins"

    img = display_pixels(x, y, vel_bin[bin_num], **kwargs)

    return img
コード例 #5
0
def display_bins(x, y, binNum, velBin):

    if not (x.size == y.size == binNum.size):
        raise ValueError("The vectors (x, y, binNum) must have the same size")

    if np.uniq(binNum).size != velBin.size:
        raise ValueError("velBin size does not match number of bins")

    img = display_pixels(x, y, velBin[binNum])

    return img
コード例 #6
0
def display_bins_generators(xBin, yBin, velBin, x, y, angle=None, **kwargs):
    """
    Displays a Voronoi binned map starting from the original coordinates of the pixels
    and the coordinates of the *generators* (not the centroids!) of the Voronoi
    tessellation, as provided in output e.g. by my voronoi_2d_binning routine.

    NB: When possible, instead of this routine, one should use the more general display_bins
    routine which uses the binNumber of every spaxel instead of the Voronoi generators.

    :param xBin: coordinates of the *generators* of the Voronoi tessellation
    :param yBin:
    :param velBin:
    :param x: coordinates of the original spaxels
    :param y:
    :param angle:
    :param kwargs:
    :return: image
    """

    warnings.warn(
        'When possible, usage of the routine display_bins is preferred to display_bins_generators'
    )

    assert xBin.size == yBin.size == velBin.size, 'The vectors (XBIN, YBIN, VEL) must have the same size'
    assert x.size == y.size, 'The vectors (X, Y) must have the same size'
    assert x.size >= xBin.size, 'The vectors (X, Y) cannot be smaller than (XBIN, YBIN)'

    # Perform a Voronoi tessellation starting from the coordinates
    # of the generators and the coordinates of the original pixels
    #
    if x.size < 1e4:
        binNum = np.argmin((x[:, None] - xBin)**2 + (y[:, None] - yBin)**2,
                           axis=1)
    else:  # use for loop to reduce memory usage
        binNum = np.zeros(x.size, dtype=int)
        for j, (xj, yj) in enumerate(zip(x, y)):
            binNum[j] = np.argmin((xj - xBin)**2 + (yj - yBin)**2)

    f = display_pixels(x, y, velBin[binNum], angle=angle, **kwargs)

    return f
コード例 #7
0
def display_bins_generators(xBin, yBin, velBin, x, y, angle=None, **kwargs):
    """
    Displays a Voronoi binned map starting from the original coordinates of the pixels
    and the coordinates of the *generators* (not the centroids!) of the Voronoi
    tessellation, as provided in output e.g. by my voronoi_2d_binning routine.

    NB: When possible, insted of this routine, one should use the more general display_bins
    routine which uses the binNumber of every spaxel insted of the Voronoi generators.

    :param xBin: coordinates of the *generators* of the Voronoi tessellation
    :param yBin:
    :param velBin:
    :param x: coordinates of the original spaxels
    :param y:
    :param angle:
    :param kwargs:
    :return: image
    """

    warnings.warn(
        'When possible, usage of the routine display_bins is preferred to display_bins_generators'
    )

    if not (xBin.size == yBin.size == velBin.size):
        raise ValueError(
            'The vectors (XBIN, YBIN, VEL) must have the same size')
    if x.size != y.size:
        raise ValueError('The vectors (X, Y) must have the same size')
    if x.size < xBin.size:
        raise ValueError(
            'The vectors (X, Y) cannot be smaller than (XBIN, YBIN)')

    # Perform a Voronoi tessellation starting from the coordinates
    # of the generators and the coordinates of the original pixels
    #
    binNum = np.argmin(
        (x[:, np.newaxis] - xBin)**2 + (y[:, np.newaxis] - yBin)**2, axis=1)
    f = display_pixels(x, y, velBin[binNum], angle=angle, **kwargs)

    return f
コード例 #8
0
def lambdaR_e_calc(xpix, ypix, flux, vel_pix, disp_pix, eff_rad, ellip, phot_ang,
                  sig_psf=0., n=2., sigma_e=False, plot=False, vmin=None, vmax=None, dmin=None, dmax=None):
    '''
    Given 2D kinematic data, this routine calculates a value for the luminosity-weighted
    stellar angular momentum parameter lambdaR within one effective radius
    (see Equation (2) from Graham et al. 2018 = G18). Plotting requires the routine display_pixels available
    from Michele Cappellari's webpage: http://purl.org/cappellari/software

    The required quanitites are:
    *   xpix:       Array of pixel x-coordinates, spatial scale should be arcsec.
    *   ypix:       Array of pixel y-coordinates.
    *   flux:       Array of pixel flux.
    *   vel_pix:    Array of pixel velocity (should be corrected to the systemic velocity).
    *   disp_pix:   Array of pixel velocity dispersion (must be corrected for the instrumental dispersion).
    *   eff_rad:    Circular effective radius in arcsec.
    *   ellip:      Ellipticity of the half-light ellipse = 1 - axis ratio.
    *   phot_ang:   Photometric major axis East of North (E = 90 degrees = 9 o'clock on the sky).

    Optional:
    *   sigma_e:    If True, will calculate the effective velocity dispersion within the half-light ellipse
                    (see Equation (3) from G18).

    #################################################################################################

    Optional PSF Correction

    lambdaR_e is affected by smearing of the velocity field due to the finite PSF. In Graham et al. 2018,
    we presented an analytic correction to account for this effect (see Subsection 3.8 and Appendix C).
    The correction should be applied for regular rotators where the semi major axis is larger than the
    dispersion of the PSF (sig_PSF) and the Sersic index falls within the range 0.5 < n < 6.5.

    *   sig_psf:    Dispersion of the PSF (= FWHM/2.355, assumed to be a Gaussian).
                    Default = 0 (i.e. no correction)
    *   n:          Sersic index.
                    Default = 2

    #################################################################################################

    Plotting commands:
    *   plot:       If true, the routine will plot maps of the velocity, velocity dispersion and flux
                    indicating which pixels were included in the calculation.
                    Default = False
    *   vmin:       Minimum velocity for plotting.
    *   vmax:       Maximum velocity for plotting.
    *   dmin:       Minimum velocity dispersion for plotting.
    *   dmax:       Maximum velocity dispersion for plotting.

    #################################################################################################

    Returns:
    *   lambdaR:    Returns lambdaR_e within the half-light ellipse.
    *   error:      Returns the error calculated using the equations given in Figure C4 of G18.
    *   frac:       Returns the fraction of pixels within the half-light ellipse with disp_pix = 0
                    (See Subsection 3.6 and Appendix B of G18).
    *   sigma_e:    If sigma_e option is True, then sigma_e is returned.

    '''

    xpix, ypix, flux, vel_pix, disp_pix = np.ravel(xpix), np.ravel(ypix), np.ravel(flux), np.ravel(vel_pix), np.ravel(disp_pix)

    # Rotate kinematic data to lie along the major axis
    theta = np.radians(90-phot_ang)
    xpix_rot = xpix * np.cos(theta) - ypix * np.sin(theta)
    ypix_rot = xpix * np.sin(theta) + ypix * np.cos(theta)

    # Select pixels contained wth half-light ellipse
    w = (xpix_rot ** 2 * (1-ellip) + ypix_rot ** 2 / (1-ellip) < eff_rad ** 2)

    # Calculate fraction of half-light ellipse covered by pixels
    try:
        frac = (vel_pix.size - sum(np.isnan(vel_pix)))/sum(w)

    except ZeroDivisionError:
        frac = 1

    if frac < 0.85:
        print('Warning: data covers less than 85% of the half-light ellipse')

    r = np.sqrt(xpix ** 2 + ypix ** 2)                                      # radius vector

    # Calculate lambdaR_e, Equation 2, G18
    try:
        num = flux[w] * r[w] * abs(vel_pix[w])                                  # numerator of lambdaR
        denom = flux[w] * r[w] * np.sqrt(vel_pix[w] ** 2 + disp_pix[w] ** 2)    # denominator of lambdaR
    except IndexError:
        print('xpix:', xpix.shape, 'ypix:', ypix.shape, 'flux:', flux.shape, 'vel_pix:', vel_pix.shape, 'disp_pix:', disp_pix.shape)
        print('Check that input sizes match: all quantities should have sizes equal to the number of pixels')

    try:
        lambdaR = np.around(sum(num)/sum(denom), 3)
    except ZeroDivisionError:
        print('Denominator == 0')
        lambdaR = -999.

    # Measure fraction of pixels with sigma=0
    try:
        w1 = disp_pix[w] == 0
        frac = round(sum(w1) / len(w1), 3)
    except ZeroDivisionError:
        frac = 0

    # Calculate beam correction
    semi_maj_axis = eff_rad/np.sqrt(1-ellip)            # Semi-major axis
    sig_psf_re_ratio = sig_psf/semi_maj_axis            # Ratio between sig_PSF and semi-major axis

    if sig_psf_re_ratio > 1:
        print('Semi-major axis is smaller than sig_PSF: not correcting')
    if (n < 0.5) | (n > 6.5):
        print('Sersic index is outside the range 0.5 < n < 6.5: not correcting')

    # Equation 5, G18
    lambdaR_true = np.around(lambdaR * (1 + (n - 2) * (0.26 * sig_psf_re_ratio)) * (1 + (sig_psf_re_ratio / 0.47) ** 1.76) ** 0.84, 3)

    if lambdaR_true > 1:
        print('Warning: corrected value of lambdaR_e is greater than 1')

    error = np.around([-0.08*n*sig_psf_re_ratio, 0.03*sig_psf_re_ratio], 3)           # Figure C4, G18

    if lambdaR_true + error[1] < lambdaR:
        error[1] = lambdaR - lambdaR_true

    lambdaR = "{0:.4f}".format(lambdaR_true)

    if sigma_e:
        try:
            num = flux[w] * (vel_pix[w] ** 2 + disp_pix[w] ** 2)  # numerator of sigma_e
            denom = flux[w]    # denominator of sigma_e
        except IndexError:
            print('xpix:', xpix.shape, 'ypix:', ypix.shape, 'flux:', flux.shape, 'vel_pix:', vel_pix.shape, 'disp_pix:', disp_pix.shape)
            print('Check that input sizes match: all quantities should have sizes equal to the number of pixels')

        try:
            sigma_e = "{0:.1f}".format(np.sqrt(sum(num)/sum(denom)))
        except ZeroDivisionError:
            print('Denominator == 0')
            sigma_e = -999

    if plot:
        # Plot velocity, velocity dispersion and flux
        plt.figure(figsize=(15, 6))
        plt.subplot(1, 3, 1)
        ax = plt.gca()

        if not vmax:
            vmax = max(vel_pix)
        if not vmin:
            vmin = min(vel_pix)

        display_pixels(xpix, ypix, vel_pix, vmin=vmin, vmax=vmax, alpha=0.2)
        im = display_pixels(xpix[w], ypix[w], vel_pix[w], vmin=vmin, vmax=vmax)

        ellipse1 = patches.Ellipse(xy=(0, 0), width=2 * eff_rad * np.sqrt(1 - ellip), fill=False,
                                   height=2 * eff_rad / np.sqrt(1 - ellip), angle=phot_ang,
                                   color='red', linewidth=3)
        ax.add_patch(ellipse1)

        ellipse2 = patches.Ellipse(xy=(0, 0), width=2 * sig_psf, fill=False,
                                   height=2 * sig_psf, angle=0,
                                   color='grey', linewidth=3)
        ax.add_patch(ellipse2)

        ax.set_xlim(-1.1 * eff_rad / np.sqrt(1 - ellip), 1.1 * eff_rad / np.sqrt(1 - ellip))
        ax.set_ylim(-1.1 * eff_rad / np.sqrt(1 - ellip), 1.1 * eff_rad / np.sqrt(1 - ellip))
        cbar = plt.colorbar(im[0], ax=ax, fraction=0.046, pad=0.04)
        cbar.ax.set_ylabel('km/s', rotation=270, fontsize=18)
        ax.set_title('Velocity')

        plt.subplot(1, 3, 2)
        ax = plt.gca()

        if not dmax:
            dmax = max(disp_pix)
        if not dmin:
            dmin = min(disp_pix)

        display_pixels(xpix, ypix, disp_pix, vmin=dmin, vmax=dmax, alpha=0.2)
        im = display_pixels(xpix[w], ypix[w], disp_pix[w], vmin=dmin, vmax=dmax)

        ellipse1 = patches.Ellipse(xy=(0, 0), width=2 * eff_rad * np.sqrt(1 - ellip), fill=False,
                                   height=2 * eff_rad / np.sqrt(1 - ellip), angle=phot_ang,
                                   color='red', linewidth=3)
        ax.add_patch(ellipse1)

        ellipse2 = patches.Ellipse(xy=(0, 0), width=2 * sig_psf, fill=False,
                                   height=2 * sig_psf, angle=0,
                                   color='grey', linewidth=3)
        ax.add_patch(ellipse2)

        ax.set_xlim(-1.1 * eff_rad / np.sqrt(1 - ellip), 1.1 * eff_rad / np.sqrt(1 - ellip))
        ax.set_ylim(-1.1 * eff_rad / np.sqrt(1 - ellip), 1.1 * eff_rad / np.sqrt(1 - ellip))
        cbar = plt.colorbar(im[0], ax=ax, fraction=0.046, pad=0.04)
        cbar.ax.set_ylabel('km/s', rotation=270, fontsize=18)
        ax.set_title('Velocity Dispersion')

        plt.subplot(1, 3, 3)
        ax = plt.gca()

        display_pixels(xpix, ypix, flux, alpha=0.2)
        im = display_pixels(xpix[w], ypix[w], flux[w])

        ellipse1 = patches.Ellipse(xy=(0, 0), width=2 * eff_rad * np.sqrt(1 - ellip), fill=False,
                                   height=2 * eff_rad / np.sqrt(1 - ellip), angle=phot_ang,
                                   color='red', linewidth=3)
        ax.add_patch(ellipse1)

        ellipse2 = patches.Ellipse(xy=(0, 0), width=2 * sig_psf, fill=False,
                                   height=2 * sig_psf, angle=0,
                                   color='grey', linewidth=3)
        ax.add_patch(ellipse2)

        ax.set_xlim(-1.1 * eff_rad / np.sqrt(1 - ellip), 1.1 * eff_rad / np.sqrt(1 - ellip))
        ax.set_ylim(-1.1 * eff_rad / np.sqrt(1 - ellip), 1.1 * eff_rad / np.sqrt(1 - ellip))
        plt.colorbar(im[0], ax=ax, fraction=0.046, pad=0.04)
        plt.title('Flux')
        plt.suptitle('$\lambda_{R_e}$ = ' + str(lambdaR), fontsize=24)
        plt.tight_layout(h_pad=1.0)
        plt.show()

    if sigma_e:
        return lambdaR, error, frac, sigma_e
    else:
        return lambdaR, error, frac