Example #1
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)
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