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