def _set_aperture_elements(self):
        """ Set internal aperture elements.
        ``self._ap_rect``, ``self.ap_el_1``, ``self.ap_el_2`` and their
        ``_in`` counterparts are always made by
        ``np.atleast_2d(self.position)``, so their results are always in
        the ``N x 2`` shape.
        """
        if hasattr(self, 'a'):
            w = self.w
            a = self.a
            b = self.b
            h = self.h
            theta = self.theta
        elif hasattr(self, 'a_in'):  # annulus
            w = self.w
            a = self.a_out
            b = self.b_out
            h = self.h_out
            theta = self.theta
        else:
            raise ValueError('Cannot determine the aperture shape.')

        # positions only accepted in the shape of (N, 2), so shape[0]
        # gives the number of positions:
        pos = np.atleast_2d(self.positions)
        self.offset = np.array([w * np.cos(theta) / 2, w * np.sin(theta) / 2])
        offsets = np.repeat(np.array([
            self.offset,
        ]), pos.shape[0], 0)

        # aperture elements for aperture,
        # OUTER aperture elements for annulus:
        self._ap_rect = RectangularAperture(positions=pos,
                                            w=w,
                                            h=h,
                                            theta=theta)
        self._ap_el_1 = EllipticalAperture(positions=pos - offsets,
                                           a=a,
                                           b=b,
                                           theta=theta)
        self._ap_el_2 = EllipticalAperture(positions=pos + offsets,
                                           a=a,
                                           b=b,
                                           theta=theta)

        if hasattr(self, 'a_in'):  # inner components of annulus
            self._ap_rect_in = RectangularAperture(positions=pos,
                                                   w=self.w,
                                                   h=self.h_in,
                                                   theta=self.theta)
            self._ap_el_1_in = EllipticalAperture(positions=pos - offsets,
                                                  a=self.a_in,
                                                  b=self.b_in,
                                                  theta=self.theta)
            self._ap_el_2_in = EllipticalAperture(positions=pos + offsets,
                                                  a=self.a_in,
                                                  b=self.b_in,
                                                  theta=self.theta)
Exemple #2
0
def aperture_photometry_scan(data,
                             x_pos,
                             y_pos,
                             ap_width,
                             ap_length,
                             theta=0.0,
                             show=False,
                             plt_title=None):
    """Aperture photometry on source located on x_pos, y_pos with
    rectangular aperture of dimensions specified by ap_length, ap_width
    is used. Aperture sums are NOT sky subtracted.

    Parameters
    ----------
    data : `np.array`
        2D array of floats
    x_pos : float
        X position of source.
    y_pos : float
        Y position of source
    ap_width : int
        Width (along x axis) of photometric aperture.
    ap_length : int
        Length (along y axis) of photometric aperture.
    theta : float
        Angle of orientation (from x-axis) for aperture, in radians.
        Increases counter-clockwise.
    show : bool, optional
        If true, plot showing aperture(s) on source will pop up. Defaults to F.
    plt_title : str or None, optional
        Only used if `show` is True. Title for plot. Defaults to None.
    Returns
    -------
    phot_tab : `astropy.table`
        Table containing
    """

    copy_data = copy.copy(data)

    rect_ap = RectangularAperture((x_pos, y_pos),
                                  w=ap_width,
                                  h=ap_length,
                                  theta=theta)

    phot_table = aperture_photometry(copy_data, rect_ap, method='exact')
    if show:
        mask = rect_ap.to_mask(method='center')
        data_cutout = mask.cutout(data)
        plt.title(plt_title)
        z1, z2 = (-12.10630989074707, 32.53888328838081)
        plt.imshow(data, origin='lower', vmin=z1, vmax=z2)
        rect_ap.plot(color='white', lw=2)
        plt.show()
        plt.close()

    return phot_table
Exemple #3
0
    def buildApertures(self):
        """Builds the apertures from the centers"""

        if np.size(self.aperCens) < 1:
            return

        self.apersObj = RectangularAperture(positions=self.aperCens, \
                                                w=self.rectWidth, \
                                                h=self.rectHeight, \
                                                theta=self.rectTheta)

        self.apersSky = RectangularAperture(positions=self.skyCens, \
                                                w=self.rectWidth, \
                                                h=self.rectHeight, \
                                                theta=self.rectTheta)
Exemple #4
0
    def custom_aperture(self,
                        shape=None,
                        r=0.0,
                        l=0.0,
                        w=0.0,
                        theta=0.0,
                        pos=None,
                        method='exact'):
        """
        Creates a custom circular or rectangular aperture of arbitrary size. 
        
        Parameters
        ----------       
        shape: str, optional
            The shape of the aperture to be used. Must be either `circle` or `rectangle.`
        r: float, optional
            If shape is `circle` the radius of the circular aperture to be used.
        l: float, optional
            If shape is `rectangle` the length of the rectangular aperture to be used.
        w: float, optional
            If shape is `rectangle` the width of the rectangular aperture to be used.
        theta: float, optional
            If shape is `rectangle` the rotation of the rectangle relative to detector coordinate. 
            Uses units of radians.   
        pos: tuple, optional
            The center of the aperture, in TPF coordinates. If not set, defaults to the center of the TPF.
        method: str, optional
            The method of producing a light curve to be used, either `exact`, `center`, or `subpixel`.
            Passed through to photutils and used as intended by that package.
        """
        if shape is None:
            print("Please select a shape: circle or rectangle")

        shape = shape.lower()
        if pos is None:
            pos = (self.tpf.shape[1] / 2, self.tpf.shape[2] / 2)
        else:
            pos = pos

        if shape == 'circle':
            if r == 0.0:
                print("Please set a radius (in pixels) for your aperture")
            else:
                aperture = CircularAperture(pos, r=r)
                self.aperture = aperture.to_mask(method=method)[0].to_image(
                    shape=((np.shape(self.tpf[0]))))

        elif shape == 'rectangle':
            if l == 0.0 or w == 0.0:
                print(
                    "For a rectangular aperture, please set both length and width: custom_aperture(shape='rectangle', l=#, w=#)"
                )
            else:
                aperture = RectangularAperture(pos, l=l, w=w, t=theta)
                self.aperture = aperture.to_mask(method=method)[0].to_image(
                    shape=((np.shape(self.tpf[0]))))
        else:
            print(
                "Aperture shape not recognized. Please set shape == 'circle' or 'rectangle'"
            )
def photometry(image2d,
               cen_x,
               cen_y,
               index=0,
               shape='Circ',
               rad=None,
               r_in=None,
               r_out=None,
               ht=None,
               wid=None,
               w_in=None,
               w_out=None,
               h_out=None,
               ang=0.0):
    """
    PARAMETERS
    ----------
    image2d = 2 dimensional image array. Type = ndarray
    cen_x, cen_y = x & y center position. Type = ndarray/list
    index = if cen_x and cen_Y is a list of more than 1 element, specify the desired index. Type = Integer
    shape = 'Circ':CircularAperture, 'Rect':RectangularAperture, 'CircAnn':CircularAnnulus, 'RectAnn':RectangularAnnulus
    rad, r_in, r_out, ht, wid, w_in, w_out, h_out, ang = Astropy's aperture parameters 
    
    RETURNS
    -------
    flux = flux of the image extracted by the aperture described in the "shape" parameter. Type = Float
    aperture = aperture object created by astropy
    """

    mask = np.isnan(image2d) == True

    if shape == 'Circ':
        aperture = CircularAperture((cen_x[index], cen_y[index]), r=rad)
    elif shape == 'Rect':
        aperture = RectangularAperture((cen_x[index], cen_y[index]),
                                       w=wid,
                                       h=ht,
                                       theta=ang)
    elif shape == 'CircAnn':
        aperture = CircularAnnulus((cen_x[index], cen_y[index]),
                                   r_in=r_in,
                                   r_out=r_out)
    elif shape == 'RectAnn':
        aperture = RectangularAnnulus((cen_x[index], cen_y[index]),
                                      w_in=w_in,
                                      w_out=w_out,
                                      h_out=h_out,
                                      theta=ang)

    phot_table = aperture_photometry(image2d, aperture, mask=mask)
    flux = phot_table['aperture_sum']
    return flux, aperture
Exemple #6
0
def place_overlay(x_pixel, y_pixel, mousebutton, wcs, ax, FOV=1.5, Size='750'):
    """Places either the main CCD, or the guide CCD apeture overlay 
    centered on the pixel coordinates depending on the boolean case "guide"
    specified in the input arguments. The overlay for the guide camera is placed
    with respect to the CCD center. Function also replots image to reset apertures
    (Easiest way I could find to do it for now)
    """
    fov = FOV * 60
    size = int(Size)
    scale = fov / size
    #Set plate scale of image 60 is number of arcmin across

    position_Target = (size / 2, size / 2)
    aperture_Target = CircularAperture(position_Target, r=(PlateScale / scale))
    aperture_Target.plot(color='blue', lw=1.5, alpha=0.5)

    if x_pixel == None or y_pixel == None:
        position_CCD = (size / 2, size / 2)
        position_Guide = (size / 2 - GuideOffRA / scale,
                          ((size / 2) + (GuideOffDEC / scale)))
    else:
        if mousebutton:
            position_Guide = (x_pixel, y_pixel)
            position_CCD = ((x_pixel) + GuideOffRA / scale,
                            (y_pixel - (GuideOffDEC / scale)))
        else:
            position_Guide = (x_pixel - GuideOffRA / scale,
                              y_pixel + (GuideOffDEC / scale))
            position_CCD = ((x_pixel), (y_pixel))

    aperture_GuideStar = CircularAperture(position_Guide,
                                          r=(PlateScale / scale))
    aperture_Guide = RectangularAperture(position_Guide, (GuideX / scale),
                                         (GuideY / scale))
    aperture_GuideStar.plot(color='blue', lw=1.5, alpha=0.5)
    aperture_Guide.plot(color='red', lw=1.5, alpha=0.5)

    aperture_CCD = RectangularAperture(position_CCD, (SciX / scale),
                                       (SciY / scale))
    aperture_CCD.plot(color='green', lw=1.5, alpha=0.5)

    ax.figure.canvas.draw()

    coords = SkyCoord.from_pixel(position_CCD[0], position_CCD[1], wcs)
    RA_str = coords.ra.to_string(units.hour)
    DEC_str = coords.dec.to_string(units.degree)

    #print coords of main CCD center
    print(RA_str, DEC_str)
Exemple #7
0
def get_slit_loss(fwhm, sw):
    '''
    '''

    sig = fwhm * gaussian_fwhm_to_sigma

    ny = nx = np.int(10 * sig)
    Y, X = np.mgrid[-ny:ny + 1, -nx - 10:nx + 1 + 10]

    g = Gaussian2D(amplitude=1 / (2 * np.pi * sig**2),
                   x_mean=0,
                   y_mean=0,
                   x_stddev=sig,
                   y_stddev=sig,
                   theta=0)
    data = g(X, Y)

    (y, x) = np.unravel_index(np.argmax(data, axis=None), data.shape)

    aper = RectangularAperture((x, y), w=sw, h=data.shape[0])

    # HM = data[ind] / 2
    # FW = np.where( HM <= data[ind[0]] )[0][-1] - np.where( HM <= data[ind[0]] )[0][ 0]
    # print( FW * gaussian_fwhm_to_sigma / sig )

    # Aperture photometry
    phot_table = aperture_photometry(data,
                                     aper,
                                     method='subpixel',
                                     subpixels=100)

    slit_loss = data.sum() - phot_table['aperture_sum'][0]

    #     fig, ax = plt.subplots( 1, 1, figsize = ( 10, 10 ) )
    #     ax.imshow( data )
    #     aper.plot( axes = ax, color = 'red', lw = 2 )
    #     plt.show()

    return slit_loss
def transform_xy_rectangle(centerX, centerY,width, height, cubeObj):
    """ Update rectangle data widgets and image object attributes
    :param float centerX: x value of the center coordinate
    :param float centerY: y value of the center coordinate
    :param float width: width value of the rectangle
    :param float height: height value of the rectangle
    :param object cubeObj: current data from the cube
    :return list fValues: flux values list for each wavelength
    :return list wValues: wavelenght list for each slice
    :return Aperture_Photometry aperture: aperture of the rectangle
    """
    fValues = []

    #Because it gets all the flux on a pixel, it needs to get the area of it rather than one value.
    pixelArea = (cubeObj.cubeRAValue * 3600.) * (cubeObj.cubeDValue * 3600.)
    aperture = RectangularAperture([centerX, centerY], width, height)
    for i in range(cubeObj.maxSlice):
        phot_table = aperture_photometry(cubeObj.data_cube[i], aperture, method='subpixel')
        fValues.append(phot_table['aperture_sum'][0]*pixelArea)

    wValues = [((w+1) - cubeObj.cubeZCPix)*cubeObj.cubeWValue + cubeObj.cubeZCRVal for w in range(len(fValues))]

    return fValues, wValues, aperture
def A_photometry(image_data,
                 bg_err,
                 factor=1,
                 ape_sum=[],
                 ape_sum_err=[],
                 cx=15,
                 cy=15,
                 r=2.5,
                 a=5,
                 b=5,
                 w_r=5,
                 h_r=5,
                 theta=0,
                 shape='Circular',
                 method='center'):
    '''
    Performs aperture photometry, first by creating the aperture (Circular,
    Rectangular or Elliptical), then it sums up the flux that falls into the 
    aperture.

    Parameters
    ----------

    image_data: 3D array 
        Data cube of images (2D arrays of pixel values).

    bg_err   : 1D array
        Array of uncertainties on pixel value.

    factor   : float (optional)
        Electron count to photon count factor. Default is 1 if none given.

    ape_sum  : 1D array (optional)
        Array of flux to append new flux values to. If 'None', the new values
        will be appended to an empty array

    ape_sum_err: 1D array (optional)
        Array of flux uncertainty to append new flux uncertainty values to. If 
        'None', the new values will be appended to an empty array.

    cx       : int (optional)
        x-coordinate of the center of the aperture. Default is 15.

    cy       : int (optional)
        y-coordinate of the center of the aperture. Default is 15.

    r        : int (optional)
        If phot_meth is 'Aperture' and ap_shape is 'Circular', c_radius is 
        the radius for the circular aperture. Default is 2.5.

    a        : int (optional)
        If phot_meth is 'Aperture' and ap_shape is 'Elliptical', e_semix is
        the semi-major axis for elliptical aperture (x-axis). Default is 5.

    b        : int (optional)
        If phot_meth is 'Aperture' and ap_shape is 'Elliptical', e_semiy is
        the semi-major axis for elliptical aperture (y-axis). Default is 5.

    w_r      : int (optional)
        If phot_meth is 'Aperture' and ap_shape is 'Rectangular', r_widthx is
        the full width for rectangular aperture (x-axis). Default is 5.

    h_r      : int (optional)
        If phot_meth is 'Aperture' and ap_shape is 'Rectangular', r_widthy is
        the full height for rectangular aperture (y-axis). Default is 5.

    theta    : int (optional)
        If phot_meth is 'Aperture' and ap_shape is 'Elliptical' or
        'Rectangular', theta is the angle of the rotation angle in radians 
        of the semimajor axis from the positive x axis. The rotation angle 
        increases counterclockwise. Default is 0.

    shape    : string object (optional)
        If phot_meth is 'Aperture', ap_shape is the shape of the aperture. 
        Possible aperture shapes are 'Circular', 'Elliptical', 'Rectangular'. 
        Default is 'Circular'.

    method   : string object (optional)
        If phot_meth is 'Aperture', apemethod is the method used to 
        determine the overlap of the aperture on the pixel grid. Possible 
        methods are 'exact', 'subpixel', 'center'. Default is 'center'.

    Returns
    -------
    ape_sum  : 1D array
        Array of flux with new flux appended.

    ape_sum_err: 1D array
        Array of flux uncertainties with new flux uncertainties appended.

    '''
    l, h, w = image_data.shape
    position = [cx, cy]
    if (shape == 'Circular'):
        aperture = CircularAperture(position, r=r)
    elif (shape == 'Elliptical'):
        aperture = EllipticalAperture(position, a=a, b=b, theta=theta)
    elif (shape == 'Rectangular'):
        aperture = RectangularAperture(position, w=w_r, h=h_r, theta=theta)
    for i in range(l):
        data_error = calc_total_error(image_data[i, :, :],
                                      bg_err[i],
                                      effective_gain=1)
        phot_table = aperture_photometry(image_data[i, :, :],
                                         aperture,
                                         error=data_error,
                                         pixelwise_error=False)
        if (phot_table['aperture_sum_err'] > 0.000001):
            ape_sum.extend(phot_table['aperture_sum'] * factor)
            ape_sum_err.extend(phot_table['aperture_sum_err'] * factor)
        else:
            ape_sum.extend([np.nan])
            ape_sum_err.extend([np.nan])
    return ape_sum, ape_sum_err
Exemple #10
0
class ApertureSet(object):
    """Set of apertures for the photometry"""

    # (I'm not sure if I want to stick with photutils, since that
    # currently enforces the same aperture size for all
    # apertures. Either way, we separate the apertures list out from
    # the individual image.

    def __init__(self, perpendic=False):

        # control variable
        self.perpendic = perpendic

        # Some parameters for aperture construction
        self.rectWidth = 220.
        self.rectHeight = 90.
        self.rectTheta = np.radians(325.)

        # sky rectangles
        self.skyWidth = np.copy(self.rectWidth)
        self.skyHeight = np.copy(self.rectHeight)

        # sky nudge
        self.skyNudge = np.array([0., 30.])

        if self.perpendic:
            self.rectTheta = np.radians(55.)
            self.skyNudge = np.array([120., 130.])

        # some default positions for apertures
        self.aperCens = np.array([])
        self.skyCens = np.array([])

        # INCLUDE ENTRIES FOR THE SKY REGIONS!!!

    def setDefaultCenters(self):
        """Sets up default aperture centers for testing on the
        2017-04-14 Jupiter data"""

        # the third one is a dummy to help me get the array dimensions
        # the right way round...
        vX = np.array([944., 892., 1105., 693., 1297.])
        vY = np.array([520., 446., 365., 592., 250.5])

        if self.perpendic:
            vX = np.array([960., 885., 1100., 772., 1285.])
            vY = np.array([456., 509., 381., 588., 264.0])

        self.aperCens = np.vstack((vX, vY))

        # set up the sky apertures
        self.skyCens = np.copy(self.aperCens)

        # DO THE OFFSET HERE.
        self.skyCens[1] -= (self.skyNudge[1] + self.skyHeight)
        self.skyCens[0] -= self.skyNudge[0]

        if not self.perpendic:
            self.skyCens[1, 1] += 2.0 * (self.skyNudge[1] + self.skyHeight)
        # self.skyCens[1,0] += 2.0 * (self.skyNudge[1] + self.skyHeight)

    def buildApertures(self):
        """Builds the apertures from the centers"""

        if np.size(self.aperCens) < 1:
            return

        self.apersObj = RectangularAperture(positions=self.aperCens, \
                                                w=self.rectWidth, \
                                                h=self.rectHeight, \
                                                theta=self.rectTheta)

        self.apersSky = RectangularAperture(positions=self.skyCens, \
                                                w=self.rectWidth, \
                                                h=self.rectHeight, \
                                                theta=self.rectTheta)

    def showApertures(self, figNum=1, inAx=None):
        """Uses photutils apertures built-in to show the apertures"""

        # This is a really stupid cargo-cult way to proceed... Come
        # back to this later!!

        if not inAx:
            fig = plt.figure(figNum)
            fig.clf()
            ax = fig.add_subplot(111)
            ax.plot([0., 1024], [0., 1024], 'w.', alpha=0.)
        else:
            ax = inAx

        self.apersObj.plot(ax=ax, color='g', alpha=0.7)
        self.apersSky.plot(ax=ax, color='b', ls='--', alpha=0.7)
Exemple #11
0
    coeff = chebfit(x_sky[~clip_mask], sky_val[~clip_mask], deg=ORDER_APSKY)

    data_skysub.append(cut_i - chebval(np.arange(cut_i.shape[0]), coeff))

data_skysub = np.array(data_skysub).T
hdr = objhdu[0].header
hdr.add_history(f"Sky subtracted using sky offset = {ap_sky_offset}, " +
                f"{SIGMA_APSKY}-sigma {ITERS_APSKY}-iter clipping " +
                f"to fit order {ORDER_APSKY} Chebyshev")
_ = fits.PrimaryHDU(data=data_skysub, header=hdr)
_.data = _.data.astype('float32')
_.writeto(SKYSBPATH / (OBJIMAGE.stem + ".skysub.fits"), overwrite=True)

pos = np.array([x_ap, y_ap]).T
aps = RectangularAperture(positions=pos, w=1, h=apheight, theta=0)
phot = aperture_photometry(data_skysub, aps, method='subpixel', subpixels=30)
ap_summed = phot['aperture_sum'] / EXPTIME

fig, axs = plt.subplots(2,
                        1,
                        figsize=(10, 6),
                        sharex=False,
                        sharey=False,
                        gridspec_kw=None)

axs[0].imshow(objimage, vmin=0, vmax=4900, origin='lower')
axs[1].imshow(data_skysub, vmin=0, vmax=200, origin='lower')
axs[1].plot(pos[:, 0], pos[:, 1], 'r-')
axs[0].set(title="Before sky subtraction")
axs[1].set(title="Sky subtracted")
Exemple #12
0
def radial_profile(data, lim):

    ############################################################
    #
    # Get radial profiles - model
    #
    ############################################################

    ############################################################
    # Fetching information
    imfile = open("Image_jband.out").readlines()
    for line in imfile:
        if line.split('=')[0] == 'MCobs:fov':
            fov = float(line.split('=')[1].split('!')[0])
        elif line.split('=')[0] == 'MCobs:npix':
            npix = float(line.split('=')[1].split('!')[0])
        elif line.split('=')[0] == 'MCobs:phi':
            phi_image = float(line.split('=')[1].split('!')[0])
        elif line.split('=')[0] == 'MCobs:theta':
            theta = float(line.split('=')[1].split('!')[0])
        else:
            continue

    infile = open("input.dat").readlines()
    for line in infile:
        if line.split('=')[0] == 'Distance':
            d = float(line.split('=')[1])

    ############################################################
    # Derived quantities
    pxsize = fov / npix  # pixel scale (arcsec/px)
    phi = (phi_image * units.deg).to(
        units.rad).value  # PA from north to east (rad)
    e = np.sin(theta)  # eccentricity of the annulus

    angle_annulus = ((0.0) * units.deg).to(units.rad).value
    e = np.sin(
        (theta * units.deg).to(units.rad).value)  # eccentricity of the annulus

    # Determining limit for radial profile
    linear_lim = 2 * lim  # AU
    angular_lim = linear_lim / ((d * units.pc).to(units.au).value)  # rad
    angular_lim = (angular_lim * units.rad).to(units.arcsec).value  # arcsec
    pixel_lim = int(round(angular_lim / pxsize))

    xc = 0.5 * data.shape[0]  # Image center in data coordinates
    yc = 0.5 * data.shape[1]  # Image center in data coordinates
    dr = 1.0  # Width of the annulus
    w = 1.0
    h = 1.0
    lim = 120
    lim = toPX(lim, pxsize, d)
    InRad = np.pi / 180.0
    xc_array = []
    yc_array = []

    x0 = xc
    y0 = yc
    xc_array.append(x0)
    yc_array.append(y0)

    for l in np.arange(-lim, lim, 1.0):
        xval = x0 + l * np.cos(angle_annulus)
        yval = y0 + l * np.sin(angle_annulus)
        xc_array.append(xval)
        yc_array.append(yval)

    positions = [(i, j) for (i, j) in zip(xc_array, yc_array)]
    InRad = np.pi / 180.0
    apertures = RectangularAperture(positions, w, h, angle_annulus)
    """
    ############################################################
    # Do a check
    a=0.4
    vmin=np.percentile(data,a)
    vmax=np.percentile(data,100-a)
    plt.imshow(data,clim=(vmin,vmax))
    plt.title("Image observation")
    apertures.plot(color='red',lw=1)
    #plt.xlim(400,600)
    #plt.ylim(600,400)
    plt.show()
    """
    phot_table = aperture_photometry(data, apertures)
    r_au = [
        toAU(phot_table['xcenter'][i].value, phot_table['ycenter'][i].value,
             xc, yc, pxsize, d) for i in range(0, len(phot_table))
    ]
    brightness = [
        phot_table['aperture_sum'][i] for i in range(0, len(phot_table))
    ]
    brightness = np.array(brightness)

    ############################################################
    # Creating brightness profile normalized
    rcmin = 35.0
    rcmax = 100.0
    bmaxc = []
    for i in range(0, len(r_au)):
        if rcmin <= r_au[i] <= rcmax:
            bmaxc.append(brightness[i])
    bmaxc = np.array(bmaxc)
    fac = 1 / max(bmaxc)
    brightness = brightness * fac
    """
    fig=plt.figure()
    ax=plt.axes()
    ax.plot(r_au,brightness,'.')
    ax.set_xlabel(r"Projected radial distance (AU)")
    ax.set_ylabel("Density flux (a.u.)")
    ax.set_title("Radial profile observation")
    #ax.set_ylim(-0.1,5)
    plt.show()
    sys.exit()
    """
    ############################################################
    # Creating file
    file = open('jband_radial_profile_modeled.dat', "w")
    for i in range(0, len(r_au)):
        file.write('%.15e %.15e \n' % (r_au[i], brightness[i]))

    #data_mod=get_profile(data_mod,pxsize,lim)

    return None
Exemple #13
0
def extract_ifu(input_model, source_type, extract_params):
    """This function does the extraction.

    Parameters
    ----------
    input_model: IFUCubeModel
        The input model.

    source_type: string
        "point" or "extended"

    extract_params: dict
        The extraction parameters for aperture photometry.

    Returns
    -------
        (ra, dec, wavelength, net, background, dq)
    """

    data = input_model.data
    shape = data.shape
    if len(shape) != 3:
        log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape))
        raise RuntimeError("The IFU cube should be 3-D.")

    # We need to allocate net, background, and dq arrays no matter what.
    net = np.zeros(shape[0], dtype=np.float64)
    background = np.zeros(shape[0], dtype=np.float64)
    dq = np.zeros(shape[0], dtype=np.int32)

    x_center = extract_params['x_center']
    y_center = extract_params['y_center']
    if x_center is None:
        x_center = float(shape[2]) / 2.
    else:
        x_center = float(x_center)
    if y_center is None:
        y_center = float(shape[1]) / 2.
    else:
        y_center = float(y_center)

    method = extract_params['method']
    # subpixels is only needed if method = 'subpixel'.
    subpixels = extract_params['subpixels']

    subtract_background = extract_params['subtract_background']
    smaller_axis = float(min(shape[1], shape[2]))  # for defaults
    if source_type == 'point':
        radius = extract_params['radius']
        if radius is None:
            radius = smaller_axis / 4.
        if subtract_background:
            inner_bkg = extract_params['inner_bkg']
            if inner_bkg is None:
                inner_bkg = radius
            outer_bkg = extract_params['outer_bkg']
            if outer_bkg is None:
                outer_bkg = min(inner_bkg * math.sqrt(2.),
                                smaller_axis / 2. - 1.)
            if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg:
                log.debug("Turning background subtraction off, due to "
                          "the values of inner_bkg and outer_bkg.")
                subtract_background = False
        width = None
        height = None
        theta = None
    else:
        width = extract_params['width']
        if width is None:
            width = smaller_axis / 2.
        height = extract_params['height']
        if height is None:
            height = smaller_axis / 2.
        theta = extract_params['theta'] * math.pi / 180.
        radius = None
        subtract_background = False
        inner_bkg = None
        outer_bkg = None

    log.debug("IFU 1-D extraction parameters:")
    log.debug("  x_center = %s", str(x_center))
    log.debug("  y_center = %s", str(y_center))
    if source_type == 'point':
        log.debug("  radius = %s", str(radius))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  inner_bkg = %s", str(inner_bkg))
        log.debug("  outer_bkg = %s", str(outer_bkg))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))
    else:
        log.debug("  width = %s", str(width))
        log.debug("  height = %s", str(height))
        log.debug("  theta = %s degrees", str(extract_params['theta']))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))

    # Check for out of bounds.
    # The problem with having the background aperture extend beyond the
    # image is that the normalization would not account for the resulting
    # decrease in the area of the annulus, so the background subtraction
    # would be systematically low.
    outside = False
    f_nx = float(shape[2])
    f_ny = float(shape[1])
    if x_center < 0. or x_center >= f_nx - 1. or \
       y_center < 0. or y_center >= f_ny - 1.:
        outside = True
        log.error("Target location is outside the image.")
    if subtract_background and \
       (x_center - outer_bkg < -0.5 or x_center + outer_bkg > f_nx - 0.5 or
        y_center - outer_bkg < -0.5 or y_center + outer_bkg > f_ny - 0.5):
        outside = True
        log.error("Background region extends outside the image.")

    if outside:
        (ra, dec) = (0., 0.)
        wavelength = np.zeros(shape[0], dtype=np.float64)
        dq[:] = dqflags.pixel['DO_NOT_USE']
        return (ra, dec, wavelength, net, background, dq)  # all bad

    if hasattr(input_model.meta, 'wcs'):
        wcs = input_model.meta.wcs
    else:
        log.warning("WCS function not found in input.")
        wcs = None

    if wcs is not None:
        x_array = np.empty(shape[0], dtype=np.float64)
        x_array.fill(float(shape[2]) / 2.)
        y_array = np.empty(shape[0], dtype=np.float64)
        y_array.fill(float(shape[1]) / 2.)
        z_array = np.arange(shape[0], dtype=np.float64)  # for wavelengths
        ra, dec, wavelength = wcs(x_array, y_array, z_array)
        nelem = len(wavelength)
        ra = ra[nelem // 2]
        dec = dec[nelem // 2]
    else:
        (ra, dec) = (0., 0.)
        wavelength = np.arange(1, shape[0] + 1, dtype=np.float64)

    position = (x_center, y_center)
    if source_type == 'point':
        aperture = CircularAperture(position, r=radius)
        if subtract_background:
            annulus = CircularAnnulus(position,
                                      r_in=inner_bkg,
                                      r_out=outer_bkg)
            normalization = aperture.area() / annulus.area()
    else:
        aperture = RectangularAperture(position, width, height, theta)
        # No background is computed for an extended source.

    for k in range(shape[0]):
        phot_table = aperture_photometry(data[k, :, :],
                                         aperture,
                                         method=method,
                                         subpixels=subpixels)
        net[k] = float(phot_table['aperture_sum'][0])
        if subtract_background:
            bkg_table = aperture_photometry(data[k, :, :],
                                            annulus,
                                            method=method,
                                            subpixels=subpixels)
            background[k] = float(bkg_table['aperture_sum'][0])
            net[k] = net[k] - background[k] * normalization

    # Check for NaNs in the wavelength array, flag them in the dq array,
    # and truncate the arrays if NaNs are found at endpoints (unless the
    # entire array is NaN).
    nan_mask = np.isnan(wavelength)
    n_nan = nan_mask.sum(dtype=np.intp)
    if n_nan > 0:
        log.warning("%d NaNs in wavelength array.", n_nan)
        dq[nan_mask] = np.bitwise_or(dq[nan_mask], dqflags.pixel['DO_NOT_USE'])
        not_nan = np.logical_not(nan_mask)
        flag = np.where(not_nan)
        if len(flag[0]) > 0:
            n_trimmed = flag[0][0] + nelem - (flag[0][-1] + 1)
            if n_trimmed > 0:
                log.info("Output arrays have been trimmed by %d elements",
                         n_trimmed)
                slc = slice(flag[0][0], flag[0][-1] + 1)
                wavelength = wavelength[slc]
                net = net[slc]
                background = background[slc]
                dq = dq[slc]
        else:
            dq |= dqflags.pixel['DO_NOT_USE']

    return (ra, dec, wavelength, net, background, dq)
Exemple #14
0
def get_profile(file, pxsize, PA_disk, inc, d):

    ############################################################
    #
    # Get flux along the semi-major axis with a rectangular
    # aperture.
    #
    # file: the fits file of the observation
    # pxsize: pixel scale (arcsec/px)
    # PA_disk: position angle of the disk measured east-north (deg)
    # inc: disk's inclination (deg)
    # d: distance to the source (pc)
    #
    # returns a file with the brightness profile along the
    # semi-major axis and a file with the position and flux
    # value of the pixel located along the semi-major axis
    # in the "South-East" quadrant containing the maximum flux
    # value. The brightness profile returned is not normalized.
    #
    ############################################################

    ############################################################
    # Load data
    hdulist = fits.open(file)
    data_obs = hdulist[0].data  # adu's

    ############################################################
    # Derived properties
    angle_annulus = ((PA_disk - 90.0) * units.deg).to(units.rad).value
    e = np.sin((inc * units.deg).to(units.rad).value)
    xc = 0.5 * data_obs.shape[0]  # Image center in data coordinates
    yc = 0.5 * data_obs.shape[1]  # Image center in data coordinates
    w = 1.0
    h = 1.0
    lim = 120.0
    lim = toPX(lim, pxsize, d)
    xc_array = []
    yc_array = []

    ############################################################
    # Setting up aperture photometry
    x0 = xc
    y0 = yc
    xc_array.append(x0)
    yc_array.append(y0)
    for l in np.arange(-lim, lim, 1.0):
        xval = x0 + l * np.cos(angle_annulus)
        yval = y0 + l * np.sin(angle_annulus)
        xc_array.append(xval)
        yc_array.append(yval)
    positions = [(i, j) for (i, j) in zip(xc_array, yc_array)]
    apertures = RectangularAperture(positions, w, h, angle_annulus)
    """
    # Do a check?
    a=0.01
    vmin=np.percentile(data_obs,a)
    vmax=np.percentile(data_obs,100-a)
    plt.imshow(data_obs,clim=(vmin,vmax))
    apertures.plot(color='red',lw=1)
    plt.show()
    sys.exit()
    """

    ############################################################
    # Performing aperture photometry
    phot_table = aperture_photometry(data_obs, apertures)
    r_au = [
        toAU(phot_table['xcenter'][i].value, phot_table['ycenter'][i].value,
             xc, yc, pxsize, d) for i in range(0, len(phot_table))
    ]
    brightness = [
        phot_table['aperture_sum'][i] for i in range(0, len(phot_table))
    ]
    for i in range(0, len(brightness)):
        brightness[i] = brightness[i] / apertures[i].area
    """
    # Do a check?
    fig=plt.figure()
    ax=plt.axes()
    ax.plot(r_au,brightness,'.')
    ax.set_xlabel(r"Projected radial distance (AU)")
    ax.set_ylabel("Density flux (a.u.)")
    plt.show()
    sys.exit()
    """

    ############################################################
    # Finding maximum value along semi-major axis
    rstart = 40.0
    rend = 80.0
    bmax = []
    for i in range(0, len(r_au)):
        if rstart <= r_au[i] <= rend:
            bmax.append(brightness[i])
    bmax = np.array(bmax)
    imax = np.where(brightness == max(bmax))[0][0]

    ############################################################
    # Writing files
    f1 = open("info_max_Qphi.dat", "w")
    f2 = open("radial_cut_0FWHM.dat", "w")
    f1.write("r_max=%.2f (AU)\n" % r_au[imax])
    f1.write("PA_max=%.2f (deg)\n" % PA_disk)
    f1.write("B_max=%.15f (a.u.)\n" % brightness[imax])
    for i in range(0, len(r_au)):
        f2.write("%.5e %.5e \n" % (r_au[i], brightness[i]))
    f1.close()
    f2.close()

    return None
Exemple #15
0
Fichier : ifu.py Projet : eteq/jwst
def extract_ifu(input_model, source_type, extract_params):
    """This function does the extraction.

    Parameters
    ----------
    input_model : IFUCubeModel
        The input model.

    source_type : string
        "point" or "extended"

    extract_params : dict
        The extraction parameters for aperture photometry.

    Returns
    -------
    ra, dec : float
        ra and dec are the right ascension and declination respectively
        at the nominal center of the image.

    wavelength : ndarray, 1-D
        The wavelength in micrometers at each pixel.

    net : ndarray, 1-D
        The count rate (or flux) minus the background at each pixel.

    background : ndarray, 1-D
        The background count rate that was subtracted from the total
        source count rate to get `net`.

    npixels : ndarray, 1-D, float64
        For each slice, this is the number of pixels that were added
        together to get `net`.

    dq : ndarray, 1-D, uint32
        The data quality array.
    """

    data = input_model.data
    shape = data.shape
    if len(shape) != 3:
        log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape))
        raise RuntimeError("The IFU cube should be 3-D.")

    # We need to allocate net, background, npixels, and dq arrays
    # no matter what.  We may need to divide by npixels, so the default
    # is 1 rather than 0.
    net = np.zeros(shape[0], dtype=np.float64)
    background = np.zeros(shape[0], dtype=np.float64)
    npixels = np.ones(shape[0], dtype=np.float64)

    dq = np.zeros(shape[0], dtype=np.uint32)

    x_center = extract_params['x_center']
    y_center = extract_params['y_center']
    if x_center is None:
        x_center = float(shape[2]) / 2.
    else:
        x_center = float(x_center)
    if y_center is None:
        y_center = float(shape[1]) / 2.
    else:
        y_center = float(y_center)

    method = extract_params['method']
    # subpixels is only needed if method = 'subpixel'.
    subpixels = extract_params['subpixels']

    subtract_background = extract_params['subtract_background']
    smaller_axis = float(min(shape[1], shape[2]))  # for defaults
    radius = None
    inner_bkg = None
    outer_bkg = None

    if source_type == 'point':
        radius = extract_params['radius']
        if radius is None:
            radius = smaller_axis / 4.
        if subtract_background:
            inner_bkg = extract_params['inner_bkg']
            if inner_bkg is None:
                inner_bkg = radius
            outer_bkg = extract_params['outer_bkg']
            if outer_bkg is None:
                outer_bkg = min(inner_bkg * math.sqrt(2.),
                                smaller_axis / 2. - 1.)
            if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg:
                log.debug("Turning background subtraction off, due to "
                          "the values of inner_bkg and outer_bkg.")
                subtract_background = False
        width = None
        height = None
        theta = None
    else:
        width = extract_params['width']
        if width is None:
            width = smaller_axis / 2.
        height = extract_params['height']
        if height is None:
            height = smaller_axis / 2.
        theta = extract_params['theta'] * math.pi / 180.
        subtract_background = False

    log.debug("IFU 1-D extraction parameters:")
    log.debug("  x_center = %s", str(x_center))
    log.debug("  y_center = %s", str(y_center))
    if source_type == 'point':
        log.debug("  radius = %s", str(radius))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  inner_bkg = %s", str(inner_bkg))
        log.debug("  outer_bkg = %s", str(outer_bkg))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))
    else:
        log.debug("  width = %s", str(width))
        log.debug("  height = %s", str(height))
        log.debug("  theta = %s degrees", str(extract_params['theta']))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))

    # Check for out of bounds.
    # The problem with having the background aperture extend beyond the
    # image is that the normalization would not account for the resulting
    # decrease in the area of the annulus, so the background subtraction
    # would be systematically low.
    outside = False
    f_nx = float(shape[2])
    f_ny = float(shape[1])
    if x_center < 0. or x_center >= f_nx - 1. or \
       y_center < 0. or y_center >= f_ny - 1.:
        outside = True
        log.error("Target location is outside the image.")
    if subtract_background and \
       (x_center - outer_bkg < -0.5 or x_center + outer_bkg > f_nx - 0.5 or
        y_center - outer_bkg < -0.5 or y_center + outer_bkg > f_ny - 0.5):
        outside = True
        log.error("Background region extends outside the image.")

    if outside:
        (ra, dec) = (0., 0.)
        wavelength = np.zeros(shape[0], dtype=np.float64)
        dq[:] = dqflags.pixel['DO_NOT_USE']
        return (ra, dec, wavelength, net, background, npixels, dq)  # all bad

    x0 = float(shape[2]) / 2.
    y0 = float(shape[1]) / 2.
    (ra, dec, wavelength) = get_coordinates(input_model, x0, y0)

    position = (x_center, y_center)
    if source_type == 'point':
        aperture = CircularAperture(position, r=radius)
        if subtract_background:
            annulus = CircularAnnulus(position,
                                      r_in=inner_bkg,
                                      r_out=outer_bkg)
            normalization = aperture.area() / annulus.area()
    else:
        aperture = RectangularAperture(position, width, height, theta)
        # No background is computed for an extended source.

    npixels[:] = aperture.area()
    for k in range(shape[0]):
        phot_table = aperture_photometry(data[k, :, :],
                                         aperture,
                                         method=method,
                                         subpixels=subpixels)
        net[k] = float(phot_table['aperture_sum'][0])
        if subtract_background:
            bkg_table = aperture_photometry(data[k, :, :],
                                            annulus,
                                            method=method,
                                            subpixels=subpixels)
            background[k] = float(bkg_table['aperture_sum'][0])
            net[k] = net[k] - background[k] * normalization

    # Check for NaNs in the wavelength array, flag them in the dq array,
    # and truncate the arrays if NaNs are found at endpoints (unless the
    # entire array is NaN).
    (wavelength, net, background, npixels, dq) = \
                nans_in_wavelength(wavelength, net, background, npixels, dq)

    return (ra, dec, wavelength, net, background, npixels, dq)
Exemple #16
0
def OutflowIntensity(model, dstar, group, wave):
    import numpy as np
    import matplotlib.pyplot as plt
    from hyperion.model import ModelOutput
    from photutils import aperture_photometry as ap
    from photutils import CircularAperture, RectangularAperture
    import astropy.constants as const

    pc = const.pc.cgs.value

    # m = ModelOutput('/Volumes/SD-Mac/model131.rtout')
    image = m.get_image(group=group, inclination=0, distance=dstar * pc, units='MJy/sr')

    # The radius of the aperture in arcsec
    # radius = 10
    x = 100
    y = 100
    # area = np.pi*radius**2 / 4.25e10 # in sr
    area = x*y / 4.25e10
    # The offset to the center
    offset = 50

    if wave != list:
        wave = [wave]

    iwav = np.argmin(np.abs(wave - image.wav))
    # Image in the unit of MJy/sr, change it into erg/s/cm2/Hz/sr
    factor = 1e-23*1e6
    val = image.val[::-1, :, iwav] * factor + 1e-30

    # Calculate the image width in arcseconds given the distance used above
    # get the max radius
    rmax = max(m.get_quantities().r_wall)
    w = np.degrees(rmax / image.distance) * 3600.

    pos_n = (len(val[0,:])/2.-1,len(val[0,:])/2.-1 + offset*len(val[0,:])/2/w)
    pos_s = (len(val[0,:])/2.-1,len(val[0,:])/2.-1 - offset*len(val[0,:])/2/w)
    # aper_n = CircularAperture(pos_n, r=radius * len(val[0,:])/2/w )
    # aper_s = CircularAperture(pos_s, r=radius * len(val[0,:])/2/w )

    # # plot to make sure the selection is correct
    # from astropy.convolution import Gaussian1DKernel, convolve
    # g = Gaussian1DKernel(stddev=20)
    #
    # fig = plt.figure(figsize=(8,6))
    # ax = fig.add_subplot(111)
    #
    # ax.plot(np.arange(-w, w, 2*w/len(val[0,:])), convolve(np.sum(val[len(val[0,:])/2.-11:len(val[0,:])/2.+9,:], axis=0), g, boundary='extend'), color='b')
    # ax.plot(np.arange(-w, w, 2*w/len(val[0,:])), convolve(np.sum(val[:,len(val[0,:])/2.-11:len(val[0,:])/2.+9], axis=1), g, boundary='extend'), color='r')
    # # ax.set_xscale('log')
    #
    # fig.savefig('/Users/yaolun/test/im_test.pdf', format='pdf', dpi=300, bbox_inches='tight')
    # fig.clf()

    aper_n = RectangularAperture(pos_n, w=x*len(val[0,:])/2/w, h=y*len(val[0,:])/2/w, theta=0)
    aper_s = RectangularAperture(pos_s, w=x*len(val[0,:])/2/w, h=y*len(val[0,:])/2/w, theta=0)

    # multiply the aperture size in sr and convert to Jy
    phot_n = ap(val, aper_n)['aperture_sum'].data * area * 1e23
    phot_s = ap(val, aper_s)['aperture_sum'].data * area * 1e23

    return phot_n, phot_s
Exemple #17
0
def extract_ifu(input_model, source_type, extract_params):
    """This function does the extraction.

    Parameters
    ----------
    input_model: IFUCubeModel
        The input model.

    source_type: string
        "point" or "extended"

    extract_params: dict
        The extraction parameters for aperture photometry.

    Returns
    -------
        (wavelength, net, background, dq)
    """

    data = input_model.data
    shape = data.shape
    if len(shape) != 3:
        log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape))
        raise RuntimeError("The IFU cube should be 3-D.")

    # We need to allocate net, background, and dq arrays no matter what.
    net = np.zeros(shape[0], dtype=np.float64)
    background = np.zeros(shape[0], dtype=np.float64)
    dq = np.zeros(shape[0], dtype=np.int32)

    x_center = extract_params['x_center']
    y_center = extract_params['y_center']
    if x_center is None:
        x_center = float(shape[2]) / 2.
    else:
        x_center = float(x_center)
    if y_center is None:
        y_center = float(shape[1]) / 2.
    else:
        y_center = float(y_center)

    method = extract_params['method']
    # subpixels is only needed if method = 'subpixel'.
    subpixels = extract_params['subpixels']

    subtract_background = extract_params['subtract_background']
    smaller_axis = float(min(shape[1], shape[2]))       # for defaults
    if source_type == 'point':
        radius = extract_params['radius']
        if radius is None:
            radius = smaller_axis / 4.
        if subtract_background:
            inner_bkg = extract_params['inner_bkg']
            if inner_bkg is None:
                inner_bkg = radius
            outer_bkg = extract_params['outer_bkg']
            if outer_bkg is None:
                outer_bkg = min(inner_bkg * math.sqrt(2.),
                                smaller_axis / 2. - 1.)
        width = None
        height = None
        theta = None
    else:
        width = extract_params['width']
        if width is None:
            width = smaller_axis / 2.
        height = extract_params['height']
        if height is None:
            height = smaller_axis / 2.
        theta = extract_params['theta'] * math.pi / 180.
        radius = None
        inner_bkg = None
        outer_bkg = None

    if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg:
        subtract_background = False

    log.debug("IFU 1-D extraction parameters:")
    log.debug("  x_center = %s", str(x_center))
    log.debug("  y_center = %s", str(y_center))
    if source_type == 'point':
        log.debug("  radius = %s", str(radius))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  inner_bkg = %s", str(inner_bkg))
        log.debug("  outer_bkg = %s", str(outer_bkg))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))
    else:
        log.debug("  width = %s", str(width))
        log.debug("  height = %s", str(height))
        log.debug("  theta = %s degrees", str(extract_params['theta']))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))

    # Check for out of bounds.
    # The problem with having the background aperture extend beyond the
    # image is that the normalization would not account for the resulting
    # decrease in the area of the annulus, so the background subtraction
    # would be systematically low.
    outside = False
    f_nx = float(shape[2])
    f_ny = float(shape[1])
    if x_center < 0. or x_center >= f_nx - 1. or \
       y_center < 0. or y_center >= f_ny - 1.:
        outside = True
        log.error("Target location is outside the image.")
    if subtract_background and \
       (x_center - outer_bkg < -0.5 or x_center + outer_bkg > f_nx - 0.5 or
        y_center - outer_bkg < -0.5 or y_center + outer_bkg > f_ny - 0.5):
            outside = True
            log.error("Background region extends outside the image.")

    if outside:
        wavelength = np.zeros(shape[0], dtype=np.float64)
        dq[:] = dqflags.pixel['DO_NOT_USE']
        return (wavelength, net, background, dq)        # all bad

    wcs = input_model.meta.wcs
    x_array = np.empty(shape[0], dtype=np.float64)
    x_array.fill(float(shape[2]) / 2.)
    y_array = np.empty(shape[0], dtype=np.float64)
    y_array.fill(float(shape[1]) / 2.)
    z_array = np.arange(shape[0], dtype=np.float64) # for wavelengths
    _, _, wavelength = wcs(x_array, y_array, z_array)

    position = (x_center, y_center)
    if source_type == 'point':
        aperture = CircularAperture(position, r=radius)
        if subtract_background:
            annulus = CircularAnnulus(position,
                                      r_in=inner_bkg, r_out=outer_bkg)
            normalization = aperture.area() / annulus.area()
    else:
        aperture = RectangularAperture(position, width, height, theta)
        # No background is computed for an extended source.

    for k in range(shape[0]):
        phot_table = aperture_photometry(data[k, :, :], aperture,
                                         method=method, subpixels=subpixels)
        net[k] = float(phot_table['aperture_sum'][0])
        if subtract_background:
            bkg_table = aperture_photometry(data[k, :, :], annulus,
                                            method=method, subpixels=subpixels)
            background[k] = float(bkg_table['aperture_sum'][0])
            net[k] = net[k] - background[k] * normalization

    return (wavelength, net, background, dq)
def do_Rect_phot(pos, FWHM, trail, ap_min=3., ap_factor=1.5, \
                 win=None, wout=None, hout=None,\
                 sky_nsigma=3., sky_iter=10 ):
    if win == None:
        win  = 4 * FWHM + trail
    if wout == None:
        wout = 8 * FWHM + trail
    if hout == None:
        hout = 8 * FWHM
    N = len(pos)
    if pos.ndim == 1:
        N = 1
    theta    = give_theta(number_of_stars=5)
    an       = RectAn(pos, w_in=win, w_out=wout, h_out=hout, theta=theta)
    ap_size  = np.max([ap_min, ap_factor*FWHM])
    aperture = RectAp(pos, w=(trail+ap_size), h=ap_size, theta=theta)
    flux     = aperture.do_photometry(image_reduc, method='exact')[0]
    # do phot and get sum from aperture. [0] is sum and [1] is error.
    #For test:
#FWHM = FWHM_moffat.copy()
#trail=trail_len.copy()
#win  = 4 * FWHM + trail
#wout = 8 * FWHM + trail
#hout = 8 * FWHM
#N=len(pos_star_fit)
#an       = RectAn(pos_star_fit, w_in=win, w_out=wout, h_out=hout, theta=(theta+np.pi/2))
#ap_size  = 1.5*FWHM_moffat
#aperture = RectAp(pos_star_fit, w=(trail+ap_size), h=ap_size, theta=(theta+np.pi/2))
#flux     = aperture.do_photometry(image_reduc, method='exact')[0]
#plt.figure(figsize=(12,12))
#plt.imshow(image_reduc, origin='lower', vmin=-10, vmax=1000)
#an.plot(color='white')
#aperture.plot(color='red')
    flux_ss  = np.zeros(N)
    error    = np.zeros(N)
    for i in range(0, N):
        mask_an    = (an.to_mask(method='center'))[i]
        #   cf: test = mask_an.cutout(image_reduc) <-- will make cutout image.
        sky_an     = mask_an.apply(image_reduc)
        all_sky    = sky_an[np.nonzero(sky_an)]
        # only annulus region will be saved as np.ndarray
        msky, stdev, nsky, nrej = sky_fit(all_sky, method='Mode', mode_option='sex')
        area       = aperture.area()
        flux_ss[i] = flux[i] - msky*area  # sky subtracted flux
        error[i]   = np.sqrt( flux_ss[i]/gain \
                           + area * stdev**2 \
                           + area**2 * stdev**2 / nsky )
        if inputs.star_img_save:
            from matplotlib import pyplot as plt
            mask_ap    = (aperture.to_mask(method='exact'))[i]
            star_ap_ss = mask_ap.apply(image_reduc-msky)
            sky_an_ss  = mask_an.apply(image_reduc-msky)
            plt.suptitle('{0}, Star ID={1} ({nsky:3d} {nrej:3d} {msky:7.2f} {stdev:7.2f})'.format(
                    inputs.filename, i, nsky=nsky, nrej=nrej, msky=msky, stdev=stdev ))
            ax1 = plt.subplot(1,2,1)
            im1 = ax1.imshow(sky_an_ss, origin='lower')
            plt.colorbar(im1, orientation='horizontal')
            ax2 = plt.subplot(1,2,2)
            im2 = ax2.imshow(star_ap_ss, origin='lower')
            plt.colorbar(im2, orientation='horizontal')
            plt.savefig('{0}.star{1}.png'.format(inputs.filename, i))
            plt.clf()
        if pos.ndim > 1:
            print('\t[{x:7.2f}, {y:7.2f}], {nsky:3d} {nrej:3d} {msky:7.2f} {stdev:7.2f} {flux:7.1f} {ferr:3.1f}'.format(\
                          x=pos[i][0], y=pos[i][1], \
                          nsky=nsky, nrej=nrej, msky=msky, stdev=stdev,\
                          flux=flux_ss[i], ferr=error[i]))
    return flux_ss, error
    def _prepare_mask(bbox, ap_r, ap_1, ap_2, method, subpixels, min_mask=0):
        """ Make the pill box mask array.
        Note
        ----
        To make an ndarray to represent the overlapping mask, the three
        (a rectangular and two elliptical) apertures are generated, but
        parallely shifted such that the bounding box has ``ixmin`` and
        ``iymin`` both zero. Then proper mask is generated as an
        ndarray. It is then used by ``PillBoxMaskMixin.to_mask`` to make
        an ``ApertureMask`` object by combining this mask with the
        original bounding box.

        Parameters
        ----------
        bbox : `~photutils.BoundingBox`
            The bounding box of the original aperture.

        ap_r : `~photutils.RectangularAperture`
            The rectangular aperture of a pill box.

        ap_1, ap_2 : `~photutils.EllipticalAperture`
            The elliptical apertures of a pill box. The order of
            left/right ellipses is not important for this method.

        method : See `~photutils.PillBoxMaskMixin.to_mask`

        subpixels : See `~photutils.PillBoxMaskMixin.to_mask`

        min_mask : float, optional
            The mask values smaller than this value is ignored (set to
            0). This is required because the subtraction of elliptical
            and rectangular masks give some negative values. One can set
            it to be ``1/subpixels**2`` because ``RectangularAperture``
            does not support ``method='exact'`` yet.

        Returns
        -------
        mask_pill : ndarray
            The mask of the pill box.
        """
        aps = []
        for ap in [ap_r, ap_1, ap_2]:
            pos_cent = ap.positions
            tmp_cent = pos_cent - np.array([bbox.ixmin, bbox.iymin])
            if hasattr(ap, 'w'):
                tmp_ap = RectangularAperture(positions=tmp_cent,
                                             w=ap.w,
                                             h=ap.h,
                                             theta=ap.theta)
            else:
                tmp_ap = EllipticalAperture(positions=tmp_cent,
                                            a=ap.a,
                                            b=ap.b,
                                            theta=ap.theta)
            aps.append(tmp_ap)

        bbox_shape = bbox.shape

        mask_kw = dict(method=method, subpixels=subpixels)
        mask_r = (aps[0].to_mask(**mask_kw).to_image(bbox_shape))
        mask_1 = (aps[1].to_mask(**mask_kw).to_image(bbox_shape))
        mask_2 = (aps[2].to_mask(**mask_kw).to_image(bbox_shape))

        # Remove both machine epsilon artifact & negative mask values:
        mask_pill_1 = mask_1 - mask_r
        mask_pill_1[mask_pill_1 < min_mask] = 0
        mask_pill_2 = mask_2 - mask_r
        mask_pill_2[mask_pill_2 < min_mask] = 0

        mask_pill = mask_r + mask_pill_1 + mask_pill_2

        # Overlap of elliptical parts may make value > 1:
        mask_pill[mask_pill > 1] = 1

        return mask_pill
Exemple #20
0
 def rectangle(pos, l, w, t):
     return RectangularAperture(pos, l, w, t)
Exemple #21
0
def square(pos, l, w, theta):
    return RectangularAperture(pos, l, w, theta)
Exemple #22
0
def extract_ifu(input_model, source_type, extract_params):
    """This function does the extraction.

    Parameters
    ----------
    input_model : IFUCubeModel
        The input model.

    source_type : string
        "point" or "extended"

    extract_params : dict
        The extraction parameters for aperture photometry.

    Returns
    -------
    ra, dec : float
        ra and dec are the right ascension and declination respectively
        at the nominal center of the image.

    wavelength : ndarray, 1-D
        The wavelength in micrometers at each plane of the IFU cube.

    temp_flux : ndarray, 1-D
        The sum of the data values in the extraction aperture minus the
        sum of the data values in the background region (scaled by the
        ratio of areas), for each plane.
        The data values are in units of surface brightness, so this value
        isn't really the flux, it's an intermediate value.  Dividing by
        `npixels` (to compute the average) will give the value for the
        `surf_bright` (surface brightness) column, and multiplying by
        the solid angle of a pixel will give the flux for a point source.

    background : ndarray, 1-D
        The background count rate that was subtracted from the total
        source data values to get `temp_flux`.

    npixels : ndarray, 1-D, float64
        For each slice, this is the number of pixels that were added
        together to get `temp_flux`.

    dq : ndarray, 1-D, uint32
        The data quality array.
    """

    data = input_model.data
    shape = data.shape
    if len(shape) != 3:
        log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape))
        raise RuntimeError("The IFU cube should be 3-D.")

    # We need to allocate temp_flux, background, npixels, and dq arrays
    # no matter what.  We may need to divide by npixels, so the default
    # is 1 rather than 0.
    temp_flux = np.zeros(shape[0], dtype=np.float64)
    background = np.zeros(shape[0], dtype=np.float64)
    npixels = np.ones(shape[0], dtype=np.float64)

    dq = np.zeros(shape[0], dtype=np.uint32)

    # For an extended target, the entire aperture will be extracted, so
    # it makes no sense to shift the extraction location.
    if source_type != "extended":
        ra_targ = input_model.meta.target.ra
        dec_targ = input_model.meta.target.dec
        locn = locn_from_wcs(input_model, ra_targ, dec_targ)
        if locn is None or np.isnan(locn[0]):
            log.warning("Couldn't determine pixel location from WCS, so "
                        "nod/dither correction will not be applied.")
            x_center = extract_params['x_center']
            y_center = extract_params['y_center']
            if x_center is None:
                x_center = float(shape[-1]) / 2.
            else:
                x_center = float(x_center)
            if y_center is None:
                y_center = float(shape[-2]) / 2.
            else:
                y_center = float(y_center)
        else:
            (x_center, y_center) = locn
            log.info("Using x_center = %g, y_center = %g, based on "
                     "TARG_RA and TARG_DEC.", x_center, y_center)

    method = extract_params['method']
    # subpixels is only needed if method = 'subpixel'.
    subpixels = extract_params['subpixels']

    subtract_background = extract_params['subtract_background']
    smaller_axis = float(min(shape[-2], shape[-1]))     # for defaults
    radius = None
    inner_bkg = None
    outer_bkg = None

    if source_type == 'extended':
        # Ignore any input parameters, and extract the whole image.
        width = float(shape[-1])
        height = float(shape[-2])
        x_center = width / 2. - 0.5
        y_center = height / 2. - 0.5
        theta = 0.
        subtract_background = False
    else:
        radius = extract_params['radius']
        if radius is None:
            radius = smaller_axis / 4.
        if subtract_background:
            inner_bkg = extract_params['inner_bkg']
            if inner_bkg is None:
                inner_bkg = radius
            outer_bkg = extract_params['outer_bkg']
            if outer_bkg is None:
                outer_bkg = min(inner_bkg * math.sqrt(2.),
                                smaller_axis / 2. - 1.)
            if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg:
                log.debug("Turning background subtraction off, due to "
                          "the values of inner_bkg and outer_bkg.")
                subtract_background = False
        width = None
        height = None
        theta = None

    log.debug("IFU 1-D extraction parameters:")
    log.debug("  x_center = %s", str(x_center))
    log.debug("  y_center = %s", str(y_center))
    if source_type == 'point':
        log.debug("  radius = %s", str(radius))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  inner_bkg = %s", str(inner_bkg))
        log.debug("  outer_bkg = %s", str(outer_bkg))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))
    else:
        log.debug("  width = %s", str(width))
        log.debug("  height = %s", str(height))
        log.debug("  theta = %s degrees", str(theta))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))

    x0 = float(shape[2]) / 2.
    y0 = float(shape[1]) / 2.
    (ra, dec, wavelength) = get_coordinates(input_model, x0, y0)

    position = (x_center, y_center)
    if source_type == 'point':
        aperture = CircularAperture(position, r=radius)
    else:
        aperture = RectangularAperture(position, width, height, theta)

    if subtract_background and inner_bkg is not None and outer_bkg is not None:
        annulus = CircularAnnulus(position, r_in=inner_bkg, r_out=outer_bkg)
    else:
        annulus = None

    # Compute the area of the aperture and possibly also of the annulus.
    normalization = 1.
    temp = np.ones(shape[-2:], dtype=np.float64)
    phot_table = aperture_photometry(temp, aperture,
                                     method=method, subpixels=subpixels)
    aperture_area = float(phot_table['aperture_sum'][0])
    if LooseVersion(photutils.__version__) >= '0.7':
        log.debug("aperture.area = %g; aperture_area = %g",
                  aperture.area, aperture_area)
    else:
        log.debug("aperture.area() = %g; aperture_area = %g",
                  aperture.area(), aperture_area)

    if subtract_background and annulus is not None:
        # Compute the area of the annulus.
        phot_table = aperture_photometry(temp, annulus,
                                         method=method, subpixels=subpixels)
        annulus_area = float(phot_table['aperture_sum'][0])
        if LooseVersion(photutils.__version__) >= '0.7':
            log.debug("annulus.area = %g; annulus_area = %g",
                      annulus.area, annulus_area)
        else:
            log.debug("annulus.area() = %g; annulus_area = %g",
                      annulus.area(), annulus_area)
        if annulus_area > 0.:
            normalization = aperture_area / annulus_area
        else:
            log.warning("Background annulus has no area, so background "
                        "subtraction will be turned off.")
            subtract_background = False
    del temp

    npixels[:] = aperture_area
    for k in range(shape[0]):
        phot_table = aperture_photometry(data[k, :, :], aperture,
                                         method=method, subpixels=subpixels)
        temp_flux[k] = float(phot_table['aperture_sum'][0])
        if subtract_background:
            bkg_table = aperture_photometry(data[k, :, :], annulus,
                                            method=method, subpixels=subpixels)
            background[k] = float(bkg_table['aperture_sum'][0])
            temp_flux[k] = temp_flux[k] - background[k] * normalization

    # Check for NaNs in the wavelength array, flag them in the dq array,
    # and truncate the arrays if NaNs are found at endpoints (unless the
    # entire array is NaN).
    (wavelength, temp_flux, background, npixels, dq) = \
        nans_in_wavelength(wavelength, temp_flux, background, npixels, dq)

    return (ra, dec, wavelength, temp_flux, background, npixels, dq)
Exemple #23
0
def A_photometry(image_data,
                 bg_err,
                 factor=1,
                 ape_sum=None,
                 ape_sum_err=None,
                 cx=15,
                 cy=15,
                 r=2.5,
                 a=5,
                 b=5,
                 w_r=5,
                 h_r=5,
                 theta=0,
                 shape='Circular',
                 method='center'):
    """
    Performs aperture photometry, first by creating the aperture (Circular,
    Rectangular or Elliptical), then it sums up the flux that falls into the 
    aperture.

    Args:
        image_data (3D array): Data cube of images (2D arrays of pixel values).
        bg_err (1D array): Array of uncertainties on pixel value.
        factor (float, optional): Electron count to photon count factor. Default is 1 if none given.
        ape_sum (1D array, optional): Array of flux to append new flux values to.
            If None, the new values will be appended to an empty array
        ape_sum_err (1D array, optional): Array of flux uncertainty to append new flux uncertainty values to.
            If None, the new values will be appended to an empty array.
        cx (int, optional): x-coordinate of the center of the aperture. Default is 15.
        cy (int, optional): y-coordinate of the center of the aperture. Default is 15.
        r (int, optional): If shape is 'Circular', r is the radius for the circular aperture. Default is 2.5.
        a (int, optional): If shape is 'Elliptical', a is the semi-major axis for elliptical aperture (x-axis). Default is 5.
        b (int, optional): If shape is 'Elliptical', b is the semi-major axis for elliptical aperture (y-axis). Default is 5.
        w_r (int, optional): If shape is 'Rectangular', w_r is the full width for rectangular aperture (x-axis). Default is 5.
        h_r (int, optional): If shape is 'Rectangular', h_r is the full height for rectangular aperture (y-axis). Default is 5.
        theta (int, optional): If shape is 'Elliptical' or 'Rectangular', theta is the angle of the rotation angle in radians
            of the semimajor axis from the positive x axis. The rotation angle increases counterclockwise. Default is 0.
        shape (string, optional): shape is the shape of the aperture. Possible aperture shapes are 'Circular',
            'Elliptical', 'Rectangular'. Default is 'Circular'.
        method (string, optional): The method used to determine the overlap of the aperture on the pixel grid. Possible 
            methods are 'exact', 'subpixel', 'center'. Default is 'center'.

    Returns:
        tuple: ape_sum (1D array) Array of flux with new flux appended.
            ape_sum_err (1D array) Array of flux uncertainties with new flux uncertainties appended.

    """

    if ape_sum is None:
        ape_sum = []
    if ape_sum_err is None:
        ape_sum_err = []

    l, h, w = image_data.shape
    tmp_sum = []
    tmp_err = []
    movingCentroid = (isinstance(cx, Iterable) or isinstance(cy, Iterable))
    if not movingCentroid:
        position = [cx, cy]
        if (shape == 'Circular'):
            aperture = CircularAperture(position, r=r)
        elif (shape == 'Elliptical'):
            aperture = EllipticalAperture(position, a=a, b=b, theta=theta)
        elif (shape == 'Rectangular'):
            aperture = RectangularAperture(position, w=w_r, h=h_r, theta=theta)
    for i in range(l):
        if movingCentroid:
            position = [cx[i], cy[i]]
            if (shape == 'Circular'):
                aperture = CircularAperture(position, r=r)
            elif (shape == 'Elliptical'):
                aperture = EllipticalAperture(position, a=a, b=b, theta=theta)
            elif (shape == 'Rectangular'):
                aperture = RectangularAperture(position,
                                               w=w_r,
                                               h=h_r,
                                               theta=theta)
        data_error = calc_total_error(image_data[i, :, :],
                                      bg_err[i],
                                      effective_gain=1)
        phot_table = aperture_photometry(
            image_data[i, :, :], aperture, error=data_error,
            method=method)  #, pixelwise_error=False)
        tmp_sum.extend(phot_table['aperture_sum'] * factor)
        tmp_err.extend(phot_table['aperture_sum_err'] * factor)
    # removing outliers
    tmp_sum = sigma_clip(tmp_sum, sigma=4, maxiters=2, cenfunc=np.ma.median)
    tmp_err = sigma_clip(tmp_err, sigma=4, maxiters=2, cenfunc=np.ma.median)
    ape_sum.extend(tmp_sum)
    ape_sum_err.extend(tmp_err)
    return ape_sum, ape_sum_err
Exemple #24
0
def extract_ifu(input_model, source_type, extract_params):
    """This function does the extraction.

    Parameters
    ----------
    input_model : IFUCubeModel
        The input model.

    source_type : string
        "POINT" or "EXTENDED"

    extract_params : dict
        The extraction parameters for aperture photometry.

    Returns
    -------
    ra, dec : float
        ra and dec are the right ascension and declination respectively
        at the nominal center of the image.

    wavelength : ndarray, 1-D
        The wavelength in micrometers at each plane of the IFU cube.

    temp_flux : ndarray, 1-D
        The sum of the data values in the extraction aperture minus the
        sum of the data values in the background region (scaled by the
        ratio of areas), for each plane.
        The data values are in units of surface brightness, so this value
        isn't really the flux, it's an intermediate value.  Dividing by
        `npixels` (to compute the average) will give the value for the
        `surf_bright` (surface brightness) column, and multiplying by
        the solid angle of a pixel will give the flux for a point source.

    background : ndarray, 1-D
        The background count rate that was subtracted from the total
        source data values to get `temp_flux`.

    npixels : ndarray, 1-D, float64
        For each slice, this is the number of pixels that were added
        together to get `temp_flux`.

    dq : ndarray, 1-D, uint32
        The data quality array.

    npixels_annulus : ndarray, 1-D, float64
        For each slice, this is the number of pixels that were added
        together to get `temp_flux` for an annulus region.

    radius_match: ndarray,1-D, float64
        The size of the extract radius in pixels used at each wavelength of the IFU cube

    x_center, y_center : float
        The x and y center of the extraction region
    """

    data = input_model.data
    weightmap = input_model.weightmap

    shape = data.shape
    if len(shape) != 3:
        log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape))
        raise RuntimeError("The IFU cube should be 3-D.")

    # We need to allocate temp_flux, background, npixels, and dq arrays
    # no matter what.  We may need to divide by npixels, so the default
    # is 1 rather than 0.
    temp_flux = np.zeros(shape[0], dtype=np.float64)
    background = np.zeros(shape[0], dtype=np.float64)
    npixels = np.ones(shape[0], dtype=np.float64)
    npixels_annulus = np.ones(shape[0], dtype=np.float64)

    dq = np.zeros(shape[0], dtype=np.uint32)

    # For an extended target, the entire aperture will be extracted, so
    # it makes no sense to shift the extraction location.
    if source_type != "EXTENDED":
        ra_targ = input_model.meta.target.ra
        dec_targ = input_model.meta.target.dec
        locn = locn_from_wcs(input_model, ra_targ, dec_targ)

        if locn is None or np.isnan(locn[0]):
            log.warning("Couldn't determine pixel location from WCS, so "
                        "source offset correction will not be applied.")

            x_center = float(shape[-1]) / 2.
            y_center = float(shape[-2]) / 2.

        else:
            (x_center, y_center) = locn
            log.info("Using x_center = %g, y_center = %g, based on "
                     "TARG_RA and TARG_DEC.", x_center, y_center)

    method = extract_params['method']
    subpixels = extract_params['subpixels']
    subtract_background = extract_params['subtract_background']

    radius = None
    inner_bkg = None
    outer_bkg = None
    width = None
    height = None
    theta = None
    # pull wavelength plane out of input data.
    # using extract 1d wavelength, interpolate the radius, inner_bkg, outer_bkg to match input wavelength

    # find the wavelength array of the IFU cube
    x0 = float(shape[2]) / 2.
    y0 = float(shape[1]) / 2.
    (ra, dec, wavelength) = get_coordinates(input_model, x0, y0)

    # interpolate the extraction parameters to the wavelength of the IFU cube
    radius_match = None
    if source_type == 'POINT':
        wave_extract = extract_params['wavelength'].flatten()
        inner_bkg = extract_params['inner_bkg'].flatten()
        outer_bkg = extract_params['outer_bkg'].flatten()
        radius = extract_params['radius'].flatten()

        frad = interp1d(wave_extract, radius, bounds_error=False, fill_value="extrapolate")
        radius_match = frad(wavelength)
        # radius_match is in arc seconds - need to convert to pixels
        # the spatial scale is the same for all wavelengths do we only need to call compute_scale once.

        if locn is None:
            locn_use = (input_model.meta.wcsinfo.crval1, input_model.meta.wcsinfo.crval2, wavelength[0])
        else:
            locn_use = (ra_targ, dec_targ, wavelength[0])

        scale_degrees =  compute_scale(
            input_model.meta.wcs,
            locn_use,
            disp_axis=input_model.meta.wcsinfo.dispersion_direction)

        scale_arcsec = scale_degrees*3600.00
        radius_match /= scale_arcsec

        finner = interp1d(wave_extract, inner_bkg, bounds_error=False, fill_value="extrapolate")
        inner_bkg_match = finner(wavelength)/scale_arcsec

        fouter = interp1d(wave_extract, outer_bkg, bounds_error=False, fill_value="extrapolate")
        outer_bkg_match = fouter(wavelength)/scale_arcsec

    elif  source_type == 'EXTENDED':
        # Ignore any input parameters, and extract the whole image.
        width = float(shape[-1])
        height = float(shape[-2])
        x_center = width / 2. - 0.5
        y_center = height / 2. - 0.5
        theta = 0.
        subtract_background = False

    log.debug("IFU 1-D extraction parameters:")
    log.debug("  x_center = %s", str(x_center))
    log.debug("  y_center = %s", str(y_center))
    if source_type == 'POINT':
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))
    else:
        log.debug("  width = %s", str(width))
        log.debug("  height = %s", str(height))
        log.debug("  theta = %s degrees", str(theta))
        log.debug("  subtract_background = %s", str(subtract_background))
        log.debug("  method = %s", method)
        if method == "subpixel":
            log.debug("  subpixels = %s", str(subpixels))

    position = (x_center, y_center)

    # get aperture for extended it will not change with wavelength
    if source_type == 'EXTENDED':
        aperture = RectangularAperture(position, width, height, theta)
        annulus = None

    for k in range(shape[0]):
        inner_bkg = None
        outer_bkg = None

        if source_type == 'POINT':
            radius = radius_match[k] # this radius has been converted to pixels
            aperture = CircularAperture(position, r=radius)
            inner_bkg = inner_bkg_match[k]
            outer_bkg = outer_bkg_match[k]
            if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg:
                log.debug("Turning background subtraction off, due to "
                          "the values of inner_bkg and outer_bkg.")
                subtract_background = False

        if subtract_background and inner_bkg is not None and outer_bkg is not None:
            annulus = CircularAnnulus(position, r_in=inner_bkg, r_out=outer_bkg)
        else:
            annulus = None

        subtract_background_plane = subtract_background
        # Compute the area of the aperture and possibly also of the annulus.
        # for each wavelength bin (taking into account empty spaxels)
        normalization = 1.
        temp = weightmap[k,:,:]
        temp[temp>1] = 1
        aperture_area = 0
        annulus_area = 0

        # aperture_photometry - using weight map
        phot_table = aperture_photometry(temp, aperture,
                                         method=method, subpixels=subpixels)

        aperture_area = float(phot_table['aperture_sum'][0])

        if LooseVersion(photutils.__version__) >= '0.7':
            log.debug("aperture.area = %g; aperture_area = %g",
                      aperture.area, aperture_area)
        else:
            log.debug("aperture.area() = %g; aperture_area = %g",
                      aperture.area(), aperture_area)

        if(aperture_area ==0 and aperture.area > 0):
            aperture_area = aperture.area

        if subtract_background and annulus is not None:
            # Compute the area of the annulus.
            phot_table = aperture_photometry(temp, annulus,
                                             method=method, subpixels=subpixels)
            annulus_area = float(phot_table['aperture_sum'][0])

            if LooseVersion(photutils.__version__) >= '0.7':
                log.debug("annulus.area = %g; annulus_area = %g",
                          annulus.area, annulus_area)
            else:
                log.debug("annulus.area() = %g; annulus_area = %g",
                          annulus.area(), annulus_area)

            if(annulus_area ==0 and annulus.area > 0):
                annulus_area = annulus.area

            if annulus_area > 0.:
                normalization = aperture_area / annulus_area
            else:
                log.warning("Background annulus has no area, so background "
                            "subtraction will be turned off. %g" ,k)
                subtract_background_plane = False
        del temp

        npixels[k] = aperture_area
        npixels_annulus[k] = 0.0
        if annulus is not None:
            npixels_annulus[k] = annulus_area
        # aperture_photometry - using data

        phot_table = aperture_photometry(data[k, :, :], aperture,
                                         method=method, subpixels=subpixels)
        temp_flux[k] = float(phot_table['aperture_sum'][0])
        if subtract_background_plane:
            bkg_table = aperture_photometry(data[k, :, :], annulus,
                                            method=method, subpixels=subpixels)
            background[k] = float(bkg_table['aperture_sum'][0])
            temp_flux[k] = temp_flux[k] - background[k] * normalization

    # Check for NaNs in the wavelength array, flag them in the dq array,
    # and truncate the arrays if NaNs are found at endpoints (unless the
    # entire array is NaN).
    (wavelength, temp_flux, background, npixels, dq, npixels_annulus) = \
        nans_in_wavelength(wavelength, temp_flux, background, npixels, dq, npixels_annulus)

    return (ra, dec, wavelength, temp_flux, background, npixels, dq,
            npixels_annulus, radius_match, x_center, y_center)
Exemple #25
0
def A_photometry(bg_err,
                 cx=15,
                 cx_med=15,
                 cy=15,
                 cy_med=15,
                 r=[2.5],
                 a=[5],
                 b=[5],
                 w_r=[5],
                 h_r=[5],
                 theta=[0],
                 scale=1,
                 shape='Circular',
                 methods=['center', 'exact'],
                 moveCentroids=[True],
                 i=0,
                 img_data=None):
    """Performs aperture photometry, first by creating the aperture then summing the flux within the aperture.

    Note that this will implicitly use the global variable image_stack (3D array) to allow for parallel computing
        with large (many GB) datasets.

    Args:
        bg_err (1D array): Array of uncertainties on pixel value.
        cx (int/array, optional): x-coordinate(s) of the center of the aperture. Default is 15.
        cy (int/array, optional): y-coordinate(s) of the center of the aperture. Default is 15.
        r (int/array, optional): If shape is 'Circular', the radii to try for aperture photometry
            in units of pixels. Default is just 2.5 pixels.
        a (int/array, optional): If shape is 'Elliptical', the semi-major axes to try for elliptical aperture
            in units of pixels. Default is 5.
        b (int/array, optional): If shape is 'Elliptical', the semi-minor axes to try for elliptical aperture
            in units of pixels. Default is 5.
        w_r (int/array, optional): If shape is 'Rectangular', the full widths to try for rectangular aperture (x-axis).
            Default is 5.
        h_r (int/array, optional): If shape is 'Rectangular', the full heights to try for rectangular aperture (y-axis).
            Default is 5.
        theta (int/array, optional): If shape is 'Elliptical' or 'Rectangular', the rotation angles in radians
            of the semimajor axis from the positive x axis. The rotation angle increases counterclockwise. Default is 0.
        scale (int, optional): If the image is oversampled, scaling factor for centroid and bounds,
            i.e, give centroid in terms of the pixel value of the initial image.
        shape (string, optional): shape is the shape of the aperture. Possible aperture shapes are 'Circular',
            'Elliptical', 'Rectangular'. Default is 'Circular'.
        methods (iterable, optional): The methods used to determine the overlap of the aperture on the pixel grid. Possible 
            methods are 'exact', 'subpixel', 'center'. Default is ['center', 'exact'].
        i (int, optional): The current frame number being examined.
        img_data (3D array): The image stack being analyzed if not using the global variable to allow for parallel computing.

    Returns:
        tuple: results (2D array) Array of flux and flux errors, of shape (nMethods*nSizes, 2), where the nSizes loop
            is nested inside the nMethods loop which is itself nested inside the moveCentoids loop.

    """

    # Access the global variable
    if img_data is None:
        global image_stack
    else:
        image_stack = img_data

    if not isinstance(r, Iterable):
        r = [r]
    if not isinstance(cx, Iterable):
        cx = [cx]
    if not isinstance(cy, Iterable):
        cy = [cy]
    if not isinstance(a, Iterable):
        a = [a]
    if not isinstance(b, Iterable):
        b = [b]
    if not isinstance(w_r, Iterable):
        w_r = [w_r]
    if not isinstance(h_r, Iterable):
        h_r = [h_r]
    if not isinstance(theta, Iterable):
        theta = [theta]
    if not isinstance(methods, Iterable):
        methods = [methods]

    data_error = calc_total_error(image_stack[i, :, :],
                                  bg_err[i],
                                  effective_gain=1)

    results = []

    for moveCentroid in moveCentroids:
        # Set up aperture(s)
        apertures = []
        if not moveCentroid:
            position = [cx_med * scale, cy_med * scale]
        else:
            position = [cx[i] * scale, cy[i] * scale]
        if (shape == 'Circular'):
            for j in range(len(r)):
                apertures.append(CircularAperture(position, r=r[j] * scale))
        elif (shape == 'Elliptical'):
            for j in range(len(a)):
                apertures.append(
                    EllipticalAperture(position,
                                       a=a[j] * scale,
                                       b=b[j] * scale,
                                       theta=theta[j]))
        elif (shape == 'Rectangular'):
            for j in range(len(w_r)):
                apertures.append(
                    RectangularAperture(position,
                                        w=w_r[j] * scale,
                                        h=h_r[j] * scale,
                                        theta=theta[j]))

        for method in methods:
            phot_table = aperture_photometry(image_stack[i, :, :],
                                             apertures,
                                             error=data_error,
                                             method=method)
            results.extend([
                float(phot_table[f'aperture_sum_{j}'])
                for j in range(len(apertures))
            ])

    return np.array(results)
Exemple #26
0
def show_stamps(pscs,
                frame_idx=None,
                stamp_size=11,
                aperture_position=None,
                aperture_size=None,
                show_residual=False,
                stretch=None,
                save_name=None,
                show_max=False,
                show_pixel_grid=False,
                **kwargs):

    if aperture_position is None:
        midpoint = (stamp_size - 1) / 2
        aperture_position = (midpoint, midpoint)

    if aperture_size:
        aperture = RectangularAperture(aperture_position,
                                       w=aperture_size,
                                       h=aperture_size,
                                       theta=0)

    ncols = len(pscs)

    if show_residual:
        ncols += 1

    nrows = 1

    fig = Figure()
    FigureCanvas(fig)
    fig.set_figheight(4)
    fig.set_figwidth(8)

    if frame_idx is not None:
        s0 = pscs[0][frame_idx]
        s1 = pscs[1][frame_idx]
    else:
        s0 = pscs[0]
        s1 = pscs[1]

    if stretch == 'log':
        stretch = LogStretch()
    else:
        stretch = LinearStretch()

    norm = ImageNormalize(s0, interval=MinMaxInterval(), stretch=stretch)

    ax1 = fig.add_subplot(nrows, ncols, 1)

    im = ax1.imshow(s0, cmap=get_palette(), norm=norm)

    if aperture_size:
        aperture.plot(color='r', lw=4, ax=ax1)
        # annulus.plot(color='c', lw=2, ls='--', ax=ax1)

    # create an axes on the right side of ax. The width of cax will be 5%
    # of ax and the padding between cax and ax will be fixed at 0.05 inch.
    # https://stackoverflow.com/questions/18195758/set-matplotlib-colorbar-size-to-match-graph
    divider = make_axes_locatable(ax1)
    cax = divider.append_axes("right", size="5%", pad=0.05)
    fig.colorbar(im, cax=cax)
    ax1.set_title('Target')

    # Comparison
    ax2 = fig.add_subplot(nrows, ncols, 2)
    im = ax2.imshow(s1, cmap=get_palette(), norm=norm)

    if aperture_size:
        aperture.plot(color='r', lw=4, ax=ax1)
        # annulus.plot(color='c', lw=2, ls='--', ax=ax1)

    divider = make_axes_locatable(ax2)
    cax = divider.append_axes("right", size="5%", pad=0.05)
    fig.colorbar(im, cax=cax)
    ax2.set_title('Comparison')

    if show_pixel_grid:
        add_pixel_grid(ax1, stamp_size, stamp_size, show_superpixel=False)
        add_pixel_grid(ax2, stamp_size, stamp_size, show_superpixel=False)

    if show_residual:
        ax3 = fig.add_subplot(nrows, ncols, 3)

        # Residual
        residual = s0 - s1
        im = ax3.imshow(residual,
                        cmap=get_palette(),
                        norm=ImageNormalize(residual,
                                            interval=MinMaxInterval(),
                                            stretch=LinearStretch()))

        divider = make_axes_locatable(ax3)
        cax = divider.append_axes("right", size="5%", pad=0.05)
        fig.colorbar(im, cax=cax)
        ax3.set_title('Noise Residual')
        ax3.set_title('Residual RMS: {:.01%}'.format(residual.std()))
        ax3.set_yticklabels([])
        ax3.set_xticklabels([])

        if show_pixel_grid:
            add_pixel_grid(ax1, stamp_size, stamp_size, show_superpixel=False)

    # Turn off tick labels
    ax1.set_yticklabels([])
    ax1.set_xticklabels([])
    ax2.set_yticklabels([])
    ax2.set_xticklabels([])

    if save_name:
        try:
            fig.savefig(save_name)
        except Exception as e:
            warn("Can't save figure: {}".format(e))

    return fig
Exemple #27
0
def asymmetric_elongated_aperture_photom(scidata, starts, inverted, jpeg='', plot=False, sat_adu = 16383, spectral_band='G'):
    '''
    Compute photometry using rectangular apertures.
    This is a good method to use for "April 2014" de Bruijn encoding
    '''
    logger = logging.getLogger()
    
    logger.debug(f'science image is dimensions: {np.shape(scidata)}')
    
    match, factor = table_matches_image(starts, scidata)
    if not match:
        logger.warning(f'table data does not match image data. Dividing table coordinates by a factor of {factor:.2f} to table data (spectral band is {spectral_band})')
        starts['x_image'] /= factor
        starts['y_image'] /= factor
    
    positions, radii, thetas = aperture_centers_weighted_start_differential(starts)

    # get median pixel value inside the fireball bounding box
    median_bckgnd = np.median(get_bounding_box(scidata, starts))


    if plot:
        plt.close()
        fig = plt.gcf()
        ax = fig.gca()
        plt.imshow(jpeg, cmap=plt.cm.gray)

    starts['aperture_sum'] = np.nan
    starts['bckgng_sub_measurements'] = np.nan
    starts['SNR'] = np.nan
    starts['signal'] = np.nan
    starts['exp_time'] = np.nan
    starts['aperture_sum_err'] = np.nan
    starts['aperture_sum_err_plus'] = np.nan
    starts['aperture_sum_err_minus'] = np.nan
    starts['aperture_saturated'] = False

    for i in range(len(starts)-1):
        starts[i]['exp_time'] = LC_open_time(starts, i, i+1, inverted)

    for el, pos, radius, theta in zip(starts[:-1], positions, radii, thetas):
        
        #zero_order_light = scidata[int(pos[1])][int(pos[0])]
        
        aperture = RectangularAperture(pos, w=radius*2, h=7.*2, theta=theta) # 7
        
        # determine if any of the pixels in the aperture have reached saturation level,
        # flag accordingly
        aperture_mask = aperture.to_mask('center')
        aperture_values = aperture_mask.multiply(scidata)
        if np.any(aperture_values == sat_adu):
            el['aperture_saturated'] = True
        
        
        # do photometry
        aperture_result = aperture_photometry(scidata, aperture, method='subpixel', subpixels=16)
        
        
        el['aperture_sum'] = aperture_result['aperture_sum'].data
        #el['aperture_sum_err'] = aperture_result['aperture_sum_err'].data TODO FIXME
        
        el['bckgng_sub_measurements'] = el['aperture_sum'] -  aperture.area*median_bckgnd
        el['SNR'] = el['aperture_sum'] / np.sqrt(el['aperture_sum'] + aperture.area*median_bckgnd)
        el['aperture_sum_err_plus'] = 1/el['SNR']
        el['aperture_sum_err_minus'] = 1/el['SNR']
        if plot:
            aperture.plot(color='white')


    
    if plot:
        #min_x, min_y, max_x, max_y = get_bounds([p['x_image'] for p in points], pos2)
        ax.set_xlim([np.min(starts['x_image']), np.max(starts['x_image'])])
        ax.set_ylim([np.min(starts['y_image']), np.max(starts['y_image'])])
        
        full_path = starts.meta['self_file_name']
        dirname = os.path.dirname(full_path)
        basename = os.path.basename(full_path).split('.')[0]
        fname = os.path.join(dirname, basename + "_aperture_photometry_"+spectral_band+".jpg")
        plt.savefig(fname, dpi=150)

    return starts
Exemple #28
0
def analyse_source(source, cube, plot=False, return_fit_params=False):
    """
    Convenience method to spatially analyse a source.
    A 30x30 pixels 'flux map' is build from the sum of a few frames around each detected lines for the source.

    Two analysis are then performed:

    * Aperture photometry from the center of the source, to estimate a flux growth function and fit it with a custom erf function.
    * A Gaussian 2D fit of the PSF on the flux map



    This method can be used in a parallel process.
    Parameters
    ----------
    source : :class:`~pandas:pandas.Series`
        A row from a :class:`~pandas:pandas.DataFrame` containing detected sources. Should have columns ``xpos``, ``ypos`` (Not astropy convention), ``velocity``, ``*_detected`` whare * is a line name, containing True or False for each line.
    cube : :class:`~ORCS:orcs.process.SpectralCube`
        SpectralCube instance where we are looking at the source
    plot : bool, Default = False
        (Optional) If True, the two fits are plotted
    return_fit_params : bool, Default = False
        (Optional) If True, returns the full fits parameters

    Returns
    -------
    res : dict
        A dictionnary containing all the relevant fitted quantities.

        +-----------------------+---------------------------------------------------------------------------------------------------+
        |Parameter              |Description                                                                                        |
        +=======================+===================================================================================================+
        |flux_map_ks_pvalue     |Estimates the 'randomness' of the flux map, i.e if it's just noise or if we actually have a signal |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |flux_r                 |Flux at different radius *r*                                                                       |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |flux_err_r             |Flux error varying with *r*                                                                        |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |erf_amplitude          |Amplitude estimated from erf fit                                                                   |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |erf_amplitude_err      |Amplitude error                                                                                    |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |erf_xfwhm              |x-axis fwhm from erf fit                                                                           |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |erf_yfwhm              |y-axis fwhm from erf fit                                                                           |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |erf_fwhm               |Fwhm defined as *r* at which half of the max flux is reached                                       |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |flux_fraction_3        |Ratio between flux measured at 3 pixels from the center and max flux                               |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |model_flux_fraction_15 |Ratio between estimated flux at 15 pixels from the center and max flux                             |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |modeled_flux_r         |Modeled flux varying with *r*                                                                      |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |psf_snr                |Ratio between amplitude of the 2D fit and noise in the flux map                                    |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |psf_amplitude          |Amplitude of the 2D fit                                                                            |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |psf_xfwhm              |x-axis fwhm from 2D fit                                                                            |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |psf_yfwhm              |y-axis fwhm from 2D fit                                                                            |
        +-----------------------+---------------------------------------------------------------------------------------------------+
        |psf_ks_pvalue          |Randomness of the residuals map                                                                    |
        +-----------------------+---------------------------------------------------------------------------------------------------+



    """
    result = {}
    try:
        from astropy.stats import sigma_clipped_stats, gaussian_sigma_to_fwhm
        from sitelle.constants import SN2_LINES, SN3_LINES
        from sitelle.region import centered_square_region
        from orb.utils.spectrum import line_shift
        from orb.core import Lines

        filter_name = cube.params.filter_name

        if filter_name == 'SN2':
            LINES = SN2_LINES
        elif filter_name == 'SN3':
            LINES = SN3_LINES
        else:
            raise ValueError(filter_name)

        ## We build a flux map of the detected lines
        try:
            detected_lines = [
                line_name for line_name in LINES
                if source['%s_detected' %
                          line_name.lower().replace('[', '').replace(']', '')]
            ]
        except KeyError as e:
            raise ValueError('No columns *_detected in the source')
        if detected_lines == []:
            return pd.Series(result)

        x, y = source.as_matrix(['xpos', 'ypos']).astype(int)
        big_box = centered_square_region(x, y, 30)
        medium_box = centered_square_region(15, 15, 5)
        small_box = centered_square_region(15, 15, 3)
        data = cube._extract_spectra_from_region(big_box, silent=True)
        mask = np.ones((30, 30))
        mask[medium_box] = 0
        bkg_spec = np.nanmedian(data[np.nonzero(mask)], axis=0)
        data -= bkg_spec

        axis = cube.params.base_axis
        spec = np.nansum(data[small_box], axis=0)

        line_pos = np.atleast_1d(
            Lines().get_line_cm1(detected_lines) +
            line_shift(source['velocity'],
                       Lines().get_line_cm1(detected_lines),
                       wavenumber=True))
        pos_min = line_pos - cube.params.line_fwhm
        pos_max = line_pos + cube.params.line_fwhm
        pos_index = np.array([[
            np.argmin(np.abs(axis - pos_min[i])),
            np.argmin(np.abs(axis - pos_max[i]))
        ] for i in range(pos_min.shape[0])])

        bandpass_size = 0
        flux_map = np.zeros(data.shape[:-1])
        for line_detection in pos_index:
            bandpass_size += line_detection[1] - line_detection[0]
            flux_map += np.nansum(data[:, :,
                                       line_detection[0]:line_detection[1]],
                                  axis=-1)

        _, _, std_map = sigma_clipped_stats(data, axis=-1)
        flux_noise_map = np.sqrt(bandpass_size) * std_map

        #Test for randomness of the flux_map
        from scipy import stats
        result['flux_map_ks_pvalue'] = stats.kstest(
            (flux_map / flux_noise_map).flatten(), 'norm').pvalue

        #Fit of the growth function
        from photutils import RectangularAperture
        from scipy.special import erf
        from scipy.optimize import curve_fit

        try:
            _x0 = source['xcentroid'] - x + 15.
            _y0 = source['ycentroid'] - y + 15.
        except:
            _x0 = source['xpos'] - x + 15.
            _y0 = source['ypos'] - y + 15.

        flux_r = [0.]
        flux_err_r = [np.nanmin(flux_noise_map)]

        r_max = 15
        r_range = np.arange(1, r_max + 1)
        for r in r_range:
            #             aper = CircularAperture((_x0,_y0), r)
            aper = RectangularAperture((_x0, _y0), r, r, 0)
            flux_r.append(aper.do_photometry(flux_map)[0][0])
            flux_err_r.append(
                np.sqrt(aper.do_photometry(flux_noise_map**2)[0][0]))

        flux_r = np.atleast_1d(flux_r)
        flux_err_r = np.atleast_1d(flux_err_r)

        result['flux_r'] = flux_r
        result['flux_err_r'] = flux_err_r
        try:

            def model(r, x0, y0, sx, sy, A):
                return A * erf((r / 2. - x0) / (2 * sx * np.sqrt(2))) * erf(
                    (r / 2. - y0) / (2 * sy * np.sqrt(2)))

            R = np.arange(r_max + 1)
            p, cov = curve_fit(model,
                               R,
                               flux_r,
                               p0=[0, 0, 1.5, 1.5,
                                   flux_map.max()],
                               bounds=([-2, -2, -np.inf, -np.inf, -np.inf],
                                       [2, 2, np.inf, np.inf, np.inf]),
                               sigma=flux_err_r,
                               absolute_sigma=True,
                               maxfev=10000)
            if (p[2] < 0) != (p[3] < 0):
                if p[-1] < 0:
                    p[-1] = -p[-1]
                    if p[2] < 0:
                        p[2] = -p[2]
                    elif p[3] < 0:
                        p[3] = -p[3]
            if plot:
                f, ax = plt.subplots()
                ax.plot(R, model(R, *p), label='Fit')
                ax.errorbar(R, flux_r, flux_err_r, label='Flux')
                ax.set_ylabel('Flux')
                ax.set_xlabel('Radius from source')
                ax.legend()

            from scipy.optimize import bisect
            fwhm = bisect(lambda x: model(x, *p) - p[-1] / 2, 0.1, 10)
            result['erf_amplitude'] = p[-1]
            result['erf_amplitude_err'] = np.sqrt(np.diag(cov))[-1]
            result['erf_xfwhm'] = gaussian_sigma_to_fwhm * p[2]
            result['erf_yfwhm'] = gaussian_sigma_to_fwhm * p[3]
            result['erf_ks_pvalue'] = stats.kstest(
                (flux_r - model(R, *p)) / flux_err_r, 'norm').pvalue
            result['erf_fwhm'] = fwhm

            result['flux_fraction_3'] = flux_r[3] / p[-1]
            result['model_flux_fraction_15'] = model(R, *
                                                     p)[r_range[-1]] / p[-1]

            result['modeled_flux_r'] = model(R, *p)

        except Exception as e:
            print(e)
            pass

        ## 2D fit of the PSF
        from astropy.modeling import models, fitting

        fitter = fitting.LevMarLSQFitter()
        X, Y = np.mgrid[:30, :30]

        flux_std = np.nanmean(flux_noise_map)

        gauss_model = models.Gaussian2D(amplitude=np.nanmax(flux_map /
                                                            flux_std),
                                        x_mean=_y0,
                                        y_mean=_x0)
        gauss_model.bounds['x_mean'] = (14, 16)
        gauss_model.bounds['y_mean'] = (14, 16)
        gauss_fit = fitter(gauss_model, X, Y, flux_map / flux_std)

        if plot is True:
            f, ax = plt.subplots(1, 3, figsize=(8, 3))
            v_min = np.nanmin(flux_map)
            v_max = np.nanmax(flux_map)
            plot_map(flux_map, ax=ax[0], cmap='RdBu_r', vmin=v_min, vmax=v_max)
            ax[0].set_title("Data")
            plot_map(gauss_fit(X, Y) * flux_std,
                     ax=ax[1],
                     cmap='RdBu_r',
                     vmin=v_min,
                     vmax=v_max)
            ax[1].set_title("Model")
            plot_map(flux_map - gauss_fit(X, Y) * flux_std,
                     ax=ax[2],
                     cmap='RdBu_r',
                     vmin=v_min,
                     vmax=v_max)
            ax[2].set_title("Residual")

        result['psf_snr'] = gauss_fit.amplitude[0]
        result['psf_amplitude'] = flux_std * gauss_fit.amplitude[
            0] * 2 * np.pi * gauss_fit.x_stddev * gauss_fit.y_stddev
        result['psf_xfwhm'] = gauss_fit.x_fwhm
        result['psf_yfwhm'] = gauss_fit.y_fwhm
        normalized_res = (flux_map -
                          gauss_fit(X, Y) * flux_std) / flux_noise_map
        result['psf_ks_pvalue'] = stats.kstest(normalized_res.flatten(),
                                               'norm').pvalue
        if return_fit_params:
            return pd.Series(result), p, gauss_fit
        else:
            return pd.Series(result)
    except Exception as e:
        print e
        return pd.Series(result)
Exemple #29
0
def measure_flux(fitsimage,
                 detections=None,
                 pixel_coords=None,
                 skycoords=None,
                 method='single-aperture',
                 a=None,
                 b=None,
                 theta=None,
                 n_boostrap=100,
                 minimal_aperture_size=1,
                 aperture_size=None,
                 aperture_scale=6.0,
                 gaussian_segment_scale=4.0,
                 plot=False,
                 ax=None,
                 color='white',
                 debug=False):
    """Accurate flux measure

    Args:
        fitsimage: the FitsImage class
        detections: astropy.table, including all source position and shapes
        pixel_coords: the pixel coordinates of the detections
        skycoords: the sky coordinates of the detections.
        aperture_size: the fixed size of the aperture, in arcsec
        a,b,theta: the size of the source, in arcsec and deg
        minimal_aperture_size: if the aperture_size is None, this can control the
            minial aperture_size for the fain source, where the adaptive aperture 
            could not be securely measured
        aperture_scale: the source shape determined aperture, lower priority than
                        aperture_size
    Note:
        When several coordinates parameters are provided, detections has the
        higher priority
    """
    pixel2arcsec = fitsimage.pixel2deg_ra * 3600
    arcsec2pixel = 1 / pixel2arcsec
    if detections is not None:
        if len(detections) < 1:
            print('No source founded...')
            return None, None
        dets_colnames = detections.colnames
        # if ('x' in dets_colnames) and ('y' in dets_colnames):
        # pixel_coords = np.array(list(zip(detections['x'], detections['y'])))
        if ('ra' in dets_colnames) and ('dec' in dets_colnames):
            ra = np.array([detections['ra']])
            dec = np.array([detections['dec']])
            skycoords = SkyCoord(ra.flatten(), dec.flatten(), unit='deg')
            pixel_coords = np.array(
                list(zip(*skycoord_to_pixel(skycoords, fitsimage.wcs))))
        if aperture_scale is not None:
            if a is None:  # in arcsec
                if 'a' in detections.colnames:
                    a = detections['a']
                else:
                    a = fitsimage.bmaj * 0.5 * 3600
            if b is None:
                if 'b' in detections.colnames:
                    b = detections['b']
                else:
                    b = fitsimage.bmin * 0.5 * 3600
            if theta is None:  # in deg
                if 'theta' in detections.colnames:
                    theta = detections['theta']
                else:
                    theta = fitsimage.bpa
    elif skycoords is not None:
        pixel_coords = np.array(
            list(zip(*skycoord_to_pixel(skycoords, fitsimage.wcs))))
        if a is None:
            a = fitsimage.bmaj * 0.5 * 3600
        if b is None:
            b = fitsimage.bmin * 0.5 * 3600
        if theta is None:
            theta = fitsimage.bpa  # in deg
    elif pixel_coords is not None:
        if a is None:
            a = fitsimage.bmaj * 0.5 * 3600
        if b is None:
            b = fitsimage.bmin * 0.5 * 3600
        if theta is None:
            theta = fitsimage.bpa  # in deg
    else:
        print("Nothing to do...")
        return None, None
    n_sources = len(pixel_coords)

    # define aperture for all the detections
    if aperture_scale is not None:
        if isinstance(a, (tuple, list, np.ndarray)):
            a_aper = aperture_scale * a * arcsec2pixel
            # a_aper = aperture_scale*a*u.arcsec
        else:
            a_aper = np.full(n_sources, aperture_scale * a * arcsec2pixel)
        if isinstance(b, (tuple, list, np.ndarray)):
            b_aper = aperture_scale * b * arcsec2pixel
            # b_aper = aperture_scale*b*u.arcsec
        else:
            b_aper = np.full(n_sources, aperture_scale * b * arcsec2pixel)
        if minimal_aperture_size is not None:
            minimal_aperture_size_in_pixel = minimal_aperture_size * arcsec2pixel
            a_aper[
                a_aper <
                minimal_aperture_size_in_pixel] = minimal_aperture_size_in_pixel
            b_aper[
                b_aper <
                minimal_aperture_size_in_pixel] = minimal_aperture_size_in_pixel
        if not isinstance(theta, (tuple, list, np.ndarray)):
            theta = np.full(n_sources, theta)
    if aperture_size is not None:
        aperture_size_pixel = aperture_size * arcsec2pixel
        a_aper = np.full(n_sources, aperture_size_pixel)
        b_aper = np.full(n_sources, aperture_size_pixel)
        theta = np.full(n_sources, 0)
    apertures = []
    for i, coord in enumerate(pixel_coords):
        apertures.append(
            EllipticalAperture(coord, a_aper[i], b_aper[i], theta[i]))
        # apertures.append(SkyEllipticalAperture(skycoords, a_aper[i], b_aper[i], theta[i]))
    detections_mask = np.zeros(fitsimage.imagesize, dtype=bool)
    for mask in apertures:
        image_aper_mask = mask.to_mask().to_image(shape=fitsimage.imagesize)
        if image_aper_mask is not None:
            detections_mask = detections_mask + image_aper_mask
        else:
            continue
    detections_mask = (detections_mask > 0) | fitsimage.imagemask

    detections_apers = []
    flux = np.zeros(n_sources)
    fluxerr = np.zeros(n_sources)
    if method == 'single-aperture':
        # measuring flux density
        for i, aper in enumerate(apertures):
            x, y = pixel_coords[i]
            pixel_fluxscale = 1. / (fitsimage.beamsize)
            detections_apers.append(
                EllipticalAperture([x, y], a_aper[i], b_aper[i], theta[i]))
            if fitsimage.has_pbcor:
                phot_table = aperture_photometry(fitsimage.image_pbcor,
                                                 aper,
                                                 mask=fitsimage.imagemask)
            else:
                phot_table = aperture_photometry(fitsimage.image,
                                                 aper,
                                                 mask=fitsimage.imagemask)
            flux[i] = phot_table['aperture_sum'].value * pixel_fluxscale
            # measuring the error of flux density
            # run the boostrap for random aperture with the image
            pixel_x = np.random.random(n_boostrap) * fitsimage.imagesize[
                1]  # 1 for x axis
            pixel_y = np.random.random(n_boostrap) * fitsimage.imagesize[
                0]  # 0 for y axis
            # points_select = (pixel_x**2 + pixel_y**2) < (np.min(fitsimage.imagesize)-np.max([a,b]))**2
            # points_select = (pixel_x-**2 + pixel_y**2) > (np.max([a,b]))**2
            # pixel_coords_boostrap = np.vstack([pixel_x[points_select], pixel_y[points_select]]).T
            pixel_coords_boostrap = np.vstack([pixel_x, pixel_y]).T
            apertures_boostrap = EllipticalAperture(pixel_coords_boostrap,
                                                    a_aper[i], b_aper[i],
                                                    theta[i])
            noise_boostrap = aperture_photometry(fitsimage.image,
                                                 apertures_boostrap,
                                                 mask=detections_mask)
            fluxerr[i] = np.std(
                np.ma.masked_invalid(
                    noise_boostrap['aperture_sum'])) * pixel_fluxscale
            # fluxerr[i] = np.std(noise_boostrap['aperture_sum']) * pixel_fluxscale
            if fitsimage.has_pbcor:
                pixel_pbcor = 1. / fitsimage.image_pb[int(np.round(x)),
                                                      int(np.round(y))]
                fluxerr[i] = fluxerr[i] * pixel_pbcor
    if method == 'gaussian':
        seg_size = gaussian_segment_scale * np.int(fitsimage.bmaj_pixel)
        segments = RectangularAperture(pixel_coords,
                                       seg_size,
                                       seg_size,
                                       theta=0)
        segments_mask = segments.to_mask(method='center')
        for i, s in enumerate(segments_mask):
            x, y = pixel_coords[i]
            pixel_fluxscale = 1 / (fitsimage.beamsize)
            image_cutout = s.cutout(fitsimage.image)
            gaussian_fitting = gaussian_2Dfitting(image_cutout, debug=debug)
            flux[i] = gaussian_fitting['flux'] * pixel_fluxscale
            # boostrap for noise measurement
            a_fitted_aper = 1.0 * 2.355 * gaussian_fitting[
                'x_stddev']  # 2xFWHM of gaussian
            b_fitted_aper = 1.0 * 2.355 * gaussian_fitting['y_stddev']
            theta_fitted = gaussian_fitting['theta']
            detections_apers.append(
                EllipticalAperture([x, y], a_fitted_aper, b_fitted_aper,
                                   theta_fitted))
            pixel_x = np.random.random(n_boostrap) * fitsimage.imagesize[
                1]  # 1 for x axis
            pixel_y = np.random.random(n_boostrap) * fitsimage.imagesize[
                0]  # 0 for y axis
            pixel_coords_boostrap = np.vstack([pixel_x, pixel_y]).T
            apertures_boostrap = EllipticalAperture(pixel_coords_boostrap,
                                                    a_fitted_aper,
                                                    b_fitted_aper,
                                                    theta_fitted)
            noise_boostrap = aperture_photometry(fitsimage.image,
                                                 apertures_boostrap,
                                                 mask=detections_mask)
            fluxerr[i] = np.std(
                np.ma.masked_invalid(
                    noise_boostrap['aperture_sum'])) * pixel_fluxscale

            if fitsimage.has_pbcor:
                pixel_pbcor = 1. / fitsimage.image_pb[int(np.round(x)),
                                                      int(np.round(y))]
                flux[i] = flux[i] * pixel_pbcor
                fluxerr[i] = fluxerr[i] * pixel_pbcor

    if plot:
        if ax is None:
            fig, ax = plt.subplots(figsize=(8, 6))
        im = ax.imshow(fitsimage.image,
                       interpolation='nearest',
                       vmin=-0.2 * fitsimage.std,
                       vmax=10.0 * fitsimage.std,
                       origin='lower')
        plt.colorbar(im, fraction=0.046, pad=0.04)

        for i in range(n_sources):
            obj = pixel_coords[i]
            im = detections_apers[i].plot(color=color, lw=1, alpha=0.8)
            ax.text(
                obj[0],
                (1.0 - 2.0 * detections_apers[i].a / fitsimage.imagesize[0]) *
                obj[1],
                "{:.2f}mJy".format(flux[i] * 1e3),
                color=color,
                horizontalalignment='center',
                verticalalignment='top',
            )
        # # only for test
        # for ap in apertures_boostrap:
        # im = ap.plot(color='gray', lw=2, alpha=0.2)

    return flux, fluxerr