def __call__(self, img): if img.opts.psf_vary_do: mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Psf_Vary") mylogger.userinfo(mylog, '\nEstimating PSF variations') opts = img.opts dir = img.basedir + '/misc/' plot = False # debug figures image = img.ch0_arr try: from astropy.io import fits as pyfits old_pyfits = False except ImportError, err: from distutils.version import StrictVersion import pyfits if StrictVersion(pyfits.__version__) < StrictVersion('2.2'): old_pyfits = True else: old_pyfits = False if old_pyfits: mylog.warning('PyFITS version is too old: psf_vary module skipped') return over = 2 generators = opts.psf_generators; nsig = opts.psf_nsig; kappa2 = opts.psf_kappa2 snrtop = opts.psf_snrtop; snrbot = opts.psf_snrbot; snrcutstack = opts.psf_snrcutstack gencode = opts.psf_gencode; primarygen = opts.psf_primarygen; itess_method = opts.psf_itess_method tess_sc = opts.psf_tess_sc; tess_fuzzy= opts.psf_tess_fuzzy bright_snr_cut = opts.psf_high_snr s_only = opts.psf_stype_only if opts.psf_snrcut < 5.0: mylogger.userinfo(mylog, "Value of psf_snrcut too low; increasing to 5") snrcut = 5.0 else: snrcut = opts.psf_snrcut img.psf_snrcut = snrcut if opts.psf_high_snr != None: if opts.psf_high_snr < 10.0: mylogger.userinfo(mylog, "Value of psf_high_snr too low; increasing to 10") high_snrcut = 10.0 else: high_snrcut = opts.psf_high_snr else: high_snrcut = opts.psf_high_snr img.psf_high_snr = high_snrcut wtfns=['unity', 'roundness', 'log10', 'sqrtlog10'] if 0 <= itess_method < 4: tess_method=wtfns[itess_method] else: tess_method='unity' ### now put all relevant gaussian parameters into a list ngaus = img.ngaus nsrc = img.nsrc num = N.zeros(nsrc, dtype=N.int32) peak = N.zeros(nsrc) xc = N.zeros(nsrc) yc = N.zeros(nsrc) bmaj = N.zeros(nsrc) bmin = N.zeros(nsrc) bpa = N.zeros(nsrc) code = N.array(['']*nsrc); rms = N.zeros(nsrc) src_id_list = [] for i, src in enumerate(img.sources): src_max = 0.0 for gmax in src.gaussians: # Take only brightest Gaussian per source if gmax.peak_flux > src_max: src_max = gmax.peak_flux g = gmax num[i] = i peak[i] = g.peak_flux xc[i] = g.centre_pix[0] yc[i] = g.centre_pix[1] bmaj[i] = g.size_pix[0] bmin[i] = g.size_pix[1] bpa[i] = g.size_pix[2] code[i] = img.sources[g.source_id].code rms[i] = img.islands[g.island_id].rms gauls = (num, peak, xc, yc, bmaj, bmin, bpa, code, rms) tr_gauls = self.trans_gaul(gauls) # takes gaussians with code=S and snr > snrcut. if s_only: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut and n[7] == 'S'] else: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut] g_gauls = self.trans_gaul(tr) # computes statistics of fitted sizes. Same as psfvary_fullstat.f in fBDSM. bmaj_a, bmaj_r, bmaj_ca, bmaj_cr, ni = _cbdsm.bstat(bmaj, None, nsig) bmin_a, bmin_r, bmin_ca, bmin_cr, ni = _cbdsm.bstat(bmin, None, nsig) bpa_a, bpa_r, bpa_ca, bpa_cr, ni = _cbdsm.bstat(bpa, None, nsig) # get subset of sources deemed to be unresolved. Same as size_ksclip_wenss.f in fBDSM. flag_unresolved = self.get_unresolved(g_gauls, img.beam, nsig, kappa2, over, img.psf_high_snr, plot) # see how much the SNR-weighted sizes of unresolved sources differ from the synthesized beam. wtsize_beam_snr = self.av_psf(g_gauls, img.beam, flag_unresolved) # filter out resolved sources tr_gaul = self.trans_gaul(g_gauls) tr = [n for i, n in enumerate(tr_gaul) if flag_unresolved[i]] g_gauls = self.trans_gaul(tr) mylogger.userinfo(mylog, 'Number of unresolved sources', str(len(g_gauls[0]))) # get a list of voronoi generators. vorogenS has values (and not None) if generators='field'. vorogenP, vorogenS = self.get_voronoi_generators(g_gauls, generators, gencode, snrcut, snrtop, snrbot, snrcutstack) mylogger.userinfo(mylog, 'Number of generators for PSF variation', str(len(vorogenP[0]))) if len(vorogenP[0]) < 3: mylog.warning('Insufficient number of generators') return mylogger.userinfo(mylog, 'Tesselating image') # group generators into tiles tile_prop = self.edit_vorogenlist(vorogenP, frac=0.9) # tesselate the image volrank, vorowts = self.tesselate(vorogenP, vorogenS, tile_prop, tess_method, tess_sc, tess_fuzzy, \ generators, gencode, image.shape) if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.volrank.fits', volrank, img, dir) tile_list, tile_coord, tile_snr = tile_prop ntile = len(tile_list) bar = statusbar.StatusBar('Determining PSF variation ............... : ', 0, ntile) mylogger.userinfo(mylog, 'Number of tiles for PSF variation', str(ntile)) # For each tile, calculate the weighted averaged psf image. Also for all the sources in the image. cdelt = list(img.wcs_obj.acdelt[0:2]) factor=3. psfimages, psfcoords, totpsfimage, psfratio, psfratio_aper = self.psf_in_tile(image, img.beam, g_gauls, \ cdelt, factor, snrcutstack, volrank, tile_prop, plot, img) npsf = len(psfimages) if opts.psf_use_shap: # use totpsfimage to get beta, centre and nmax for shapelet decomposition. Use nmax=5 or 6 mask=N.zeros(totpsfimage.shape, dtype=bool) (m1, m2, m3)=func.moment(totpsfimage, mask) betainit=sqrt(m3[0]*m3[1])*2.0 * 1.4 tshape = totpsfimage.shape cen = N.array(N.unravel_index(N.argmax(totpsfimage), tshape))+[1,1] cen = tuple(cen) nmax = 12 basis = 'cartesian' betarange = [0.5,sqrt(betainit*max(tshape))] beta, error = sh.shape_varybeta(totpsfimage, mask, basis, betainit, cen, nmax, betarange, plot) if error == 1: print ' Unable to find minimum in beta' # decompose all the psf images using the beta from above nmax=12; psf_cf=[] for i in range(npsf): psfim = psfimages[i] cf = sh.decompose_shapelets(psfim, mask, basis, beta, cen, nmax, mode='') psf_cf.append(cf) if img.opts.quiet == False: bar.increment() bar.stop() # transpose the psf image list xt, yt = N.transpose(tile_coord) tr_psf_cf = N.transpose(N.array(psf_cf)) # interpolate the coefficients across the image. Ok, interpolate in scipy for # irregular grids is crap. doesnt even pass through some of the points. # for now, fit polynomial. compress = 100.0 x, y = N.transpose(psfcoords) if len(x) < 3: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return psf_coeff_interp, xgrid, ygrid = self.interp_shapcoefs(nmax, tr_psf_cf, psfcoords, image.shape, \ compress, plot) psfshape = psfimages[0].shape skip = 5 aa = self.create_psf_grid(psf_coeff_interp, image.shape, xgrid, ygrid, skip, nmax, psfshape, \ basis, beta, cen, totpsfimage, plot) img.psf_images = aa else: if ntile < 4: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return else: # Fit stacked PSFs with Gaussians and measure aperture fluxes bm_pix = N.array([img.pixel_beam()[0]*fwsig, img.pixel_beam()[1]*fwsig, img.pixel_beam()[2]]) psf_maj = N.zeros(npsf) psf_min = N.zeros(npsf) psf_pa = N.zeros(npsf) if img.opts.quiet == False: bar.start() for i in range(ntile): psfim = psfimages[i] mask = N.zeros(psfim.shape, dtype=bool) x_ax, y_ax = N.indices(psfim.shape) maxv = N.max(psfim) p_ini = [maxv, (psfim.shape[0]-1)/2.0*1.1, (psfim.shape[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, bm_pix[1]/fwsig*1.1, bm_pix[2]*2] para, ierr = func.fit_gaus2d(psfim, p_ini, x_ax, y_ax, mask) ### first extent is major if para[3] < para[4]: para[3:5] = para[4:2:-1] para[5] += 90 ### clip position angle para[5] = divmod(para[5], 180)[1] psf_maj[i] = para[3] psf_min[i] = para[4] posang = para[5] while posang >= 180.0: posang -= 180.0 psf_pa[i] = posang if img.opts.quiet == False: bar.increment() bar.stop() # Interpolate Gaussian parameters if img.aperture == None: psf_maps = [psf_maj, psf_min, psf_pa, psfratio] else: psf_maps = [psf_maj, psf_min, psf_pa, psfratio, psfratio_aper] nimgs = len(psf_maps) bar = statusbar.StatusBar('Interpolating PSF images ................ : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.interp_prop), psf_maps, itertools.repeat(psfcoords), itertools.repeat(image.shape)), numcores=opts.ncores, bar=bar) if img.aperture == None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Smooth if desired if img.opts.psf_smooth != None: sm_scale = img.opts.psf_smooth / img.pix2beam([1.0, 1.0, 0.0])[0] / 3600.0 # pixels if img.opts.aperture == None: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int] else: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int] nimgs = len(psf_maps) bar = statusbar.StatusBar('Smoothing PSF images .................... : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.blur_image), psf_maps, itertools.repeat(sm_scale)), numcores=opts.ncores, bar=bar) if img.aperture == None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Make sure all smoothed, interpolated images are ndarrays psf_maj_int = N.array(psf_maj_int) psf_min_int = N.array(psf_min_int) psf_pa_int = N.array(psf_pa_int) psf_ratio_int = N.array(psf_ratio_int) if img.aperture == None: psf_ratio_aper_int = N.zeros(psf_maj_int.shape, dtype=N.float32) else: psf_ratio_aper_int = N.array(psf_ratio_aper_int, dtype=N.float32) # Blank with NaNs if needed mask = img.mask_arr if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) psf_maj_int[pix_masked] = N.nan psf_min_int[pix_masked] = N.nan psf_pa_int[pix_masked] = N.nan psf_ratio_int[pix_masked] = N.nan psf_ratio_aper_int[pix_masked] = N.nan # Store interpolated images. The major and minor axis images are # the sigma in units of arcsec, the PA image in units of degrees east of # north, the ratio images in units of 1/beam. img.psf_vary_maj_arr = psf_maj_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_min_arr = psf_min_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_pa_arr = psf_pa_int img.psf_vary_ratio_arr = psf_ratio_int # in 1/beam img.psf_vary_ratio_aper_arr = psf_ratio_aper_int # in 1/beam if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_maj.fits', img.psf_vary_maj_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_min.fits', img.psf_vary_min_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_pa.fits', img.psf_vary_pa_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio.fits', img.psf_vary_ratio_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio_aper.fits', img.psf_vary_ratio_aper_arr, img, dir) # Loop through source and Gaussian lists and deconvolve the sizes using appropriate beam bar2 = statusbar.StatusBar('Correcting deconvolved source sizes ..... : ', 0, img.nsrc) if img.opts.quiet == False: bar2.start() for src in img.sources: src_pos = img.sky2pix(src.posn_sky_centroid) src_pos_int = (int(src_pos[0]), int(src_pos[1])) gaus_c = img.gaus2pix(src.size_sky, src.posn_sky_centroid) gaus_bm = [psf_maj_int[src_pos_int]*fwsig, psf_min_int[src_pos_int]*fwsig, psf_pa_int[src_pos_int]*fwsig] gaus_dc, err = func.deconv2(gaus_bm, gaus_c) src.deconv_size_sky = img.pix2gaus(gaus_dc, src_pos) src.deconv_size_skyE = [0.0, 0.0, 0.0] for g in src.gaussians: gaus_c = img.gaus2pix(g.size_sky, src.posn_sky_centroid) gaus_dc, err = func.deconv2(gaus_bm, gaus_c) g.deconv_size_sky = img.pix2gaus(gaus_dc, g.centre_pix) g.deconv_size_skyE = [0.0, 0.0, 0.0] if img.opts.quiet == False: bar2.spin() if img.opts.quiet == False: bar2.increment() bar2.stop() img.completed_Ops.append('psf_vary')
class Op_gaul2srl(Op): """ Slightly modified from fortran. """ def __call__(self, img): # for each island, get the gaussians into a list and then send them to process # src_index is source number, starting from 0 mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Gaul2Srl") mylogger.userinfo(mylog, 'Grouping Gaussians into sources') img.aperture = img.opts.aperture if img.aperture is not None and img.aperture <= 0.0: mylog.warn('Specified aperture is <= 0. Skipping aperture fluxes.') img.aperture = None src_index = -1 dsrc_index = 0 sources = [] dsources = [] no_gaus_islands = [] for iisl, isl in enumerate(img.islands): isl_sources = [] isl_dsources = [] g_list = [] for g in isl.gaul: if g.flag == 0: g_list.append(g) if len(g_list) > 0: if len(g_list) == 1: src_index, source = self.process_single_gaussian(img, g_list, src_index, code='S') sources.append(source) isl_sources.append(source) else: src_index, source = self.process_CM( img, g_list, isl, src_index) sources.extend(source) isl_sources.extend(source) else: if not img.waveletimage: dg = isl.dgaul[0] no_gaus_islands.append( (isl.island_id, dg.centre_pix[0], dg.centre_pix[1])) # Put in the dummy Source as the source and use negative IDs g_list = isl.dgaul dsrc_index, dsource = self.process_single_gaussian( img, g_list, dsrc_index, code='S') dsources.append(dsource) isl_dsources.append(dsource) isl.sources = isl_sources isl.dsources = isl_dsources img.sources = sources img.dsources = dsources img.nsrc = src_index + 1 mylogger.userinfo(mylog, "Number of sources formed from Gaussians", str(img.nsrc)) if not img.waveletimage and not img._pi and len( no_gaus_islands) > 0 and not img.opts.quiet: message = 'All Gaussians were flagged for the following island' if len(no_gaus_islands) == 1: message += ':\n' else: message += 's:\n' for isl_id in no_gaus_islands: message += ' Island #%i (x=%i, y=%i)\n' % isl_id if len(no_gaus_islands) == 1: message += 'Please check this island. If it is a valid island and\n' else: message += 'Please check these islands. If they are valid islands and\n' if img.opts.atrous_do: message += 'should be fit, try adjusting the flagging options (use\n'\ 'show_fit with "ch0_flagged=True" to see the flagged Gaussians).' else: message += 'should be fit, try adjusting the flagging options (use\n'\ 'show_fit with "ch0_flagged=True" to see the flagged Gaussians)\n'\ 'or enabling the wavelet module (with "atrous_do=True").' message += '\nTo include empty islands in output source catalogs, set\n'\ 'incl_empty=True in the write_catalog task.' mylog.warning(message) img.completed_Ops.append('gaul2srl') ################################################################################################# def process_single_gaussian(self, img, g_list, src_index, code): """ Process single gaussian into a source, for both S and C type sources. g is just one Gaussian object (not a list).""" g = g_list[0] total_flux = [g.total_flux, g.total_fluxE] peak_flux_centroid = peak_flux_max = [g.peak_flux, g.peak_fluxE] posn_sky_centroid = posn_sky_max = [g.centre_sky, g.centre_skyE] size_sky = [g.size_sky, g.size_skyE] size_sky_uncorr = [g.size_sky_uncorr, g.size_skyE] deconv_size_sky = [g.deconv_size_sky, g.deconv_size_skyE] deconv_size_sky_uncorr = [g.deconv_size_sky_uncorr, g.deconv_size_skyE] bbox = img.islands[g.island_id].bbox ngaus = 1 island_id = g.island_id if g.gaus_num < 0: gaussians = [] else: gaussians = list([g]) aper_flux = func.ch0_aperture_flux(img, g.centre_pix, img.aperture) source_prop = list([code, total_flux, peak_flux_centroid, peak_flux_max, aper_flux, posn_sky_centroid, \ posn_sky_max, size_sky, size_sky_uncorr, deconv_size_sky, deconv_size_sky_uncorr, bbox, ngaus, island_id, gaussians]) source = Source(img, source_prop) if g.gaussian_idx == -1: src_index -= 1 else: src_index += 1 g.source_id = src_index g.code = code source.source_id = src_index return src_index, source ################################################################################################## def process_CM(self, img, g_list, isl, src_index): """ Bundle errors with the quantities. ngau = number of gaussians in island src_id = the source index array for every gaussian in island nsrc = final number of distinct sources in the island """ ngau = len(g_list) # same as cisl in callgaul2srl.f nsrc = ngau # same as islct; initially make each gaussian as a source src_id = N.arange(nsrc) # same as islnum in callgaul2srl.f boxx, boxy = isl.bbox subn = boxx.stop - boxx.start subm = boxy.stop - boxy.start delc = [boxx.start, boxy.start] subim = self.make_subim(subn, subm, g_list, delc) index = [(i, j) for i in range(ngau) for j in range(ngau) if j > i] for pair in index: same_island = self.in_same_island(pair, img, g_list, isl, subim, subn, subm, delc) if same_island: nsrc -= 1 mmax, mmin = max(src_id[pair[0]], src_id[pair[1]]), min(src_id[pair[0]], src_id[pair[1]]) arr = N.where(src_id == mmax)[0] src_id[arr] = mmin # now reorder src_id so that it is contiguous for i in range(ngau): ind1 = N.where(src_id == i)[0] if len(ind1) == 0: arr = N.where(src_id > i)[0] if len(arr) > 0: decr = N.min(src_id[arr]) - i for j in arr: src_id[j] -= decr nsrc = N.max(src_id) + 1 # now do whats in sub_calc_para_source source_list = [] for isrc in range(nsrc): posn = N.where(src_id == isrc)[0] g_sublist = [] for i in posn: g_sublist.append(g_list[i]) ngau_insrc = len(posn) # Do source type C if ngau_insrc == 1: src_index, source = self.process_single_gaussian(img, g_sublist, src_index, code='C') else: # make mask and subim. Invalid mask value is -1 since 0 is valid srcid mask = self.make_mask(isl, subn, subm, 1, isrc, g_sublist, delc) src_index, source = self.process_Multiple(img, g_sublist, mask, src_index, isrc, subim, \ isl, delc, subn, subm) source_list.append(source) return src_index, source_list ################################################################################################## def in_same_island(self, pair, img, g_list, isl, subim, subn, subm, delc): """ Whether two gaussians belong to the same source or not. """ import functions as func def same_island_min(pair, g_list, subim, delc, tol=0.5): """ If the minimum of the reconstructed fluxes along the line joining the peak positions is greater than thresh_isl times the rms_clip, they belong to different islands. """ g1 = g_list[pair[0]] g2 = g_list[pair[1]] pix1 = N.array(g1.centre_pix) pix2 = N.array(g2.centre_pix) x1, y1 = N.floor(pix1) - delc x2, y2 = N.floor(pix2) - delc pix1 = N.array( N.unravel_index(N.argmax(subim[x1:x1 + 2, y1:y1 + 2]), (2, 2))) + [x1, y1] pix2 = N.array( N.unravel_index(N.argmax(subim[x2:x2 + 2, y2:y2 + 2]), (2, 2))) + [x2, y2] if pix1[1] >= subn: pix1[1] = pix1[1] - 1 if pix2[1] >= subm: pix2[1] = pix2[1] - 1 maxline = int(round(N.max(N.abs(pix1 - pix2) + 1))) flux1 = g1.peak_flux flux2 = g2.peak_flux # get pix values of the line pixdif = pix2 - pix1 same_island_min = False same_island_cont = False if maxline == 1: same_island_min = True same_island_cont = True else: if abs(pixdif[0]) > abs(pixdif[1]): xline = N.round(min(pix1[0], pix2[0]) + N.arange(maxline)) yline = N.round((pix1[1]-pix2[1])/(pix1[0]-pix2[0])* \ (min(pix1[0],pix2[0])+N.arange(maxline)-pix1[0])+pix1[1]) else: yline = N.round(min(pix1[1], pix2[1]) + N.arange(maxline)) xline = N.round((pix1[0]-pix2[0])/(pix1[1]-pix2[1])* \ (min(pix1[1],pix2[1])+N.arange(maxline)-pix1[1])+pix1[0]) rpixval = N.zeros(maxline, dtype=N.float32) xbig = N.where(xline >= N.size(subim, 0)) xline[xbig] = N.size(subim, 0) - 1 ybig = N.where(yline >= N.size(subim, 1)) yline[ybig] = N.size(subim, 1) - 1 for i in range(maxline): pixval = subim[xline[i], yline[i]] rpixval[i] = pixval min_pixval = N.min(rpixval) minind_p = N.argmin(rpixval) maxind_p = N.argmax(rpixval) if minind_p in (0, maxline - 1) and maxind_p in (0, maxline - 1): same_island_cont = True if min_pixval >= min(flux1, flux2): same_island_min = True elif abs(min_pixval - min(flux1, flux2) ) <= tol * isl.rms * img.opts.thresh_isl: same_island_min = True return same_island_min, same_island_cont def same_island_dist(pair, g_list, tol=0.5): """ If the centres are seperated by a distance less than half the sum of their fwhms along the PA of the line joining them, they belong to the same island. """ from math import sqrt g1 = g_list[pair[0]] g2 = g_list[pair[1]] pix1 = N.array(g1.centre_pix) pix2 = N.array(g2.centre_pix) gsize1 = g1.size_pix gsize2 = g2.size_pix fwhm1 = func.gdist_pa(pix1, pix2, gsize1) fwhm2 = func.gdist_pa(pix1, pix2, gsize2) dx = pix2[0] - pix1[0] dy = pix2[1] - pix1[1] dist = sqrt(dy * dy + dx * dx) if dist <= tol * (fwhm1 + fwhm2): same_island = True else: same_island = False return same_island if img.opts.group_by_isl: same_isl1_min = True same_isl1_cont = True same_isl2 = True else: if img.opts.group_method == 'curvature': subim = -1.0 * func.make_curvature_map(subim) tol = img.opts.group_tol same_isl1_min, same_isl1_cont = same_island_min( pair, g_list, subim, delc, tol) same_isl2 = same_island_dist(pair, g_list, tol / 2.0) g1 = g_list[pair[0]] same_island = (same_isl1_min and same_isl2) or same_isl1_cont return same_island ################################################################################################## def process_Multiple(self, img, g_sublist, mask, src_index, isrc, subim, isl, delc, subn, subm): """ Same as gaul_to_source.f. isrc is same as k in the fortran version. """ from math import pi, sqrt from const import fwsig from scipy import ndimage import functions as func mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Gaul2Srl ") dum = img.beam[0] * img.beam[1] cdeltsq = img.wcs_obj.acdelt[0] * img.wcs_obj.acdelt[1] bmar_p = 2.0 * pi * dum / (cdeltsq * fwsig * fwsig) # try subim_src = self.make_subim(subn, subm, g_sublist, delc) mompara = func.momanalmask_gaus(subim_src, mask, isrc, bmar_p, True) # initial peak posn and value maxv = N.max(subim_src) maxx, maxy = N.unravel_index(N.argmax(subim_src), subim_src.shape) # fit gaussian around this posn blc = N.zeros(2) trc = N.zeros(2) n, m = subim_src.shape[0:2] bm_pix = N.array([ img.pixel_beam()[0] * fwsig, img.pixel_beam()[1] * fwsig, img.pixel_beam()[2] ]) ssubimsize = max(N.int(N.round(N.max(bm_pix[0:2]) * 2)) + 1, 5) blc[0] = max(0, maxx - (ssubimsize - 1) / 2) blc[1] = max(0, maxy - (ssubimsize - 1) / 2) trc[0] = min(n, maxx + (ssubimsize - 1) / 2) trc[1] = min(m, maxy + (ssubimsize - 1) / 2) s_imsize = trc - blc + 1 p_ini = [maxv, (s_imsize[0]-1)/2.0*1.1, (s_imsize[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, \ bm_pix[1]/fwsig*1.1, bm_pix[2]*2] data = subim_src[blc[0]:blc[0] + s_imsize[0], blc[1]:blc[1] + s_imsize[1]] smask = mask[blc[0]:blc[0] + s_imsize[0], blc[1]:blc[1] + s_imsize[1]] rmask = N.where(smask == isrc, False, True) x_ax, y_ax = N.indices(data.shape) if N.sum(~rmask) >= 6: para, ierr = func.fit_gaus2d(data, p_ini, x_ax, y_ax, rmask) if (0.0<para[1]<s_imsize[0]) and (0.0<para[2]<s_imsize[1]) and \ para[3]<s_imsize[0] and para[4]<s_imsize[1]: maxpeak = para[0] else: maxpeak = maxv posn = para[1:3] - (0.5 * N.sum(s_imsize) - 1) / 2.0 + N.array( [maxx, maxy]) - 1 + delc else: maxpeak = maxv posn = N.unravel_index(N.argmax(data * ~rmask), data.shape) + N.array(delc) + blc # calculate peak by bilinear interpolation around centroid # First check that moment analysis gave a valid position. If not, use # posn from gaussian fit instead. if N.isnan(mompara[1]): mompara[1] = posn[0] - delc[0] x1 = N.int(N.floor(mompara[1])) if N.isnan(mompara[2]): mompara[2] = posn[1] - delc[1] y1 = N.int(N.floor(mompara[2])) xind = slice(x1, x1 + 2, 1) yind = slice(y1, y1 + 2, 1) if img.opts.flag_smallsrc and ( N.sum(mask[xind, yind] == N.ones((2, 2)) * isrc) != 4): mylog.debug('Island = ' + str(isl.island_id)) mylog.debug('Mask = ' + repr(mask[xind, yind]) + 'xind, yind, x1, y1 = ' + repr(xind) + ' ' + repr(yind) + ' ' + repr(x1) + ' ' + repr(y1)) t = (mompara[1] - x1) / (x1 + 1 - x1) # in case u change it later u = (mompara[2] - y1) / (y1 + 1 - y1) s_peak=(1.0-t)*(1.0-u)*subim_src[x1,y1]+t*(1.0-u)*subim_src[x1+1,y1]+ \ t*u*subim_src[x1+1,y1+1]+(1.0-t)*u*subim_src[x1,y1+1] if (not img.opts.flag_smallsrc) and ( N.sum(mask[xind, yind] == N.ones((2, 2)) * isrc) != 4): mylog.debug('Speak ' + repr(s_peak) + 'Mompara = ' + repr(mompara)) mylog.debug('x1, y1 : ' + repr(x1) + ', ' + repr(y1)) # import pylab as pl # pl.imshow(N.transpose(subim_src), origin='lower', interpolation='nearest') # pl.suptitle('Image of bad M source '+str(isl.island_id)) # convert pixels to coords try: sra, sdec = img.pix2sky( [mompara[1] + delc[0], mompara[2] + delc[1]]) mra, mdec = img.pix2sky(posn) except RuntimeError, err: # Invalid pixel wcs coordinate sra, sdec = 0.0, 0.0 mra, mdec = 0.0, 0.0 # "deconvolve" the sizes gaus_c = [mompara[3], mompara[4], mompara[5]] gaus_bm = [bm_pix[0], bm_pix[1], bm_pix[2]] gaus_dc, err = func.deconv2(gaus_bm, gaus_c) deconv_size_sky = img.pix2gaus( gaus_dc, [mompara[1] + delc[0], mompara[2] + delc[1]]) deconv_size_sky_uncorr = img.pix2gaus( gaus_dc, [mompara[1] + delc[0], mompara[2] + delc[1]], use_wcs=False) # update all objects etc tot = 0.0 totE_sq = 0.0 for g in g_sublist: tot += g.total_flux totE_sq += g.total_fluxE**2 totE = sqrt(totE_sq) size_pix = [mompara[3], mompara[4], mompara[5]] size_sky = img.pix2gaus(size_pix, [mompara[1] + delc[0], mompara[2] + delc[1]]) size_sky_uncorr = img.pix2gaus( size_pix, [mompara[1] + delc[0], mompara[2] + delc[1]], use_wcs=False) # Estimate uncertainties in source size and position due to # errors in the constituent Gaussians using a Monte Carlo technique. # Sum with Condon (1997) errors in quadrature. plist = mompara.tolist() + [tot] plist[0] = s_peak plist[3] /= fwsig plist[4] /= fwsig errors = func.get_errors(img, plist, isl.rms) if img.opts.do_mc_errors: nMC = 20 mompara0_MC = N.zeros(nMC, dtype=N.float32) mompara1_MC = N.zeros(nMC, dtype=N.float32) mompara2_MC = N.zeros(nMC, dtype=N.float32) mompara3_MC = N.zeros(nMC, dtype=N.float32) mompara4_MC = N.zeros(nMC, dtype=N.float32) mompara5_MC = N.zeros(nMC, dtype=N.float32) for i in range(nMC): # Reconstruct source from component Gaussians. Draw the Gaussian # parameters from random distributions given by their errors. subim_src_MC = self.make_subim(subn, subm, g_sublist, delc, mc=True) try: mompara_MC = func.momanalmask_gaus(subim_src_MC, mask, isrc, bmar_p, True) mompara0_MC[i] = mompara_MC[0] mompara1_MC[i] = mompara_MC[1] mompara2_MC[i] = mompara_MC[2] mompara3_MC[i] = mompara_MC[3] mompara4_MC[i] = mompara_MC[4] mompara5_MC[i] = mompara_MC[5] except: mompara0_MC[i] = mompara[0] mompara1_MC[i] = mompara[1] mompara2_MC[i] = mompara[2] mompara3_MC[i] = mompara[3] mompara4_MC[i] = mompara[4] mompara5_MC[i] = mompara[5] mompara0E = N.std(mompara0_MC) mompara1E = N.std(mompara1_MC) if mompara1E > 2.0 * mompara[1]: mompara1E = 2.0 * mompara[1] # Don't let errors get too large mompara2E = N.std(mompara2_MC) if mompara2E > 2.0 * mompara[2]: mompara2E = 2.0 * mompara[2] # Don't let errors get too large mompara3E = N.std(mompara3_MC) if mompara3E > 2.0 * mompara[3]: mompara3E = 2.0 * mompara[3] # Don't let errors get too large mompara4E = N.std(mompara4_MC) if mompara4E > 2.0 * mompara[4]: mompara4E = 2.0 * mompara[4] # Don't let errors get too large mompara5E = N.std(mompara5_MC) if mompara5E > 2.0 * mompara[5]: mompara5E = 2.0 * mompara[5] # Don't let errors get too large else: mompara1E = 0.0 mompara2E = 0.0 mompara3E = 0.0 mompara4E = 0.0 mompara5E = 0.0 # Now add MC errors in quadrature with Condon (1997) errors size_skyE = [ sqrt(mompara3E**2 + errors[3]**2) * sqrt(cdeltsq), sqrt(mompara4E**2 + errors[4]**2) * sqrt(cdeltsq), sqrt(mompara5E**2 + errors[5]**2) ] sraE, sdecE = (sqrt(mompara1E**2 + errors[1]**2) * sqrt(cdeltsq), sqrt(mompara2E**2 + errors[2]**2) * sqrt(cdeltsq)) deconv_size_skyE = size_skyE # set deconvolved errors to non-deconvolved ones # Find aperture flux if img.opts.aperture_posn == 'centroid': aper_pos = [mompara[1] + delc[0], mompara[2] + delc[1]] else: aper_pos = posn aper_flux, aper_fluxE = func.ch0_aperture_flux(img, aper_pos, img.aperture) isl_id = isl.island_id source_prop = list([ 'M', [tot, totE], [s_peak, isl.rms], [maxpeak, isl.rms], [aper_flux, aper_fluxE], [[sra, sdec], [sraE, sdecE]], [[mra, mdec], [sraE, sdecE]], [size_sky, size_skyE], [size_sky_uncorr, size_skyE], [deconv_size_sky, deconv_size_skyE], [deconv_size_sky_uncorr, deconv_size_skyE], isl.bbox, len(g_sublist), isl_id, g_sublist ]) source = Source(img, source_prop) src_index += 1 for g in g_sublist: g.source_id = src_index g.code = 'M' source.source_id = src_index return src_index, source
def __init__(self, img, gaussian, isl_idx, g_idx, flag=0): """Initialize Gaussian object from fitting data Parameters: img: PyBDSM image object gaussian: 6-tuple of fitted numbers isl_idx: island serial number g_idx: gaussian serial number flag: flagging (if any) """ import functions as func from const import fwsig import numpy as N use_wcs = True self.gaussian_idx = g_idx self.gaus_num = 0 # stored later self.island_id = isl_idx self.jlevel = img.j self.flag = flag self.parameters = gaussian p = gaussian self.peak_flux = p[0] self.centre_pix = p[1:3] size = p[3:6] if func.approx_equal(size[0], img.pixel_beam()[0]*1.1) and \ func.approx_equal(size[1], img.pixel_beam()[1]) and \ func.approx_equal(size[2], img.pixel_beam()[2]+90.0) or \ img.opts.fix_to_beam: # Check whether fitted Gaussian is just the distorted pixel beam # given as an initial guess or if size was fixed to the beam. If so, # reset the size to the undistorted beam. # Note: these are sigma sizes, not FWHM sizes. size = img.pixel_beam() size = (size[0], size[1], size[2]+90.0) # adjust angle so that corrected_size() works correctly size = func.corrected_size(size) # gives fwhm and P.A. self.size_pix = size # FWHM in pixels and P.A. CCW from +y axis # Use img.orig_beam for flux calculation and deconvolution on wavelet # images, as img.beam has been altered to match the wavelet scale. # Note: these are all FWHM sizes. if img.waveletimage: bm_pix = N.array(img.beam2pix(img.orig_beam)) else: bm_pix = N.array(img.beam2pix(img.beam)) # Calculate fluxes, sky sizes, etc. All sizes are FWHM. tot = p[0]*size[0]*size[1]/(bm_pix[0]*bm_pix[1]) if flag == 0: # These are good Gaussians errors = func.get_errors(img, p+[tot], img.islands[isl_idx].rms) self.centre_sky = img.pix2sky(p[1:3]) self.centre_skyE = img.pix2coord(errors[1:3], self.centre_pix, use_wcs=use_wcs) self.size_sky = img.pix2gaus(size, self.centre_pix, use_wcs=use_wcs) # FWHM in degrees and P.A. east from north self.size_sky_uncorr = img.pix2gaus(size, self.centre_pix, use_wcs=False) # FWHM in degrees and P.A. east from +y axis self.size_skyE = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=use_wcs) self.size_skyE_uncorr = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=False) gaus_dc, err = func.deconv2(bm_pix, size) self.deconv_size_sky = img.pix2gaus(gaus_dc, self.centre_pix, use_wcs=use_wcs) self.deconv_size_sky_uncorr = img.pix2gaus(gaus_dc, self.centre_pix, use_wcs=False) self.deconv_size_skyE = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=use_wcs) self.deconv_size_skyE_uncorr = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=False) else: # These are flagged Gaussians, so don't calculate sky values or errors errors = [0]*7 self.centre_sky = [0., 0.] self.centre_skyE = [0., 0.] self.size_sky = [0., 0., 0.] self.size_sky_uncorr = [0., 0., 0.] self.size_skyE = [0., 0.] self.size_skyE_uncorr = [0., 0., 0.] self.deconv_size_sky = [0., 0., 0.] self.deconv_size_sky_uncorr = [0., 0., 0.] self.deconv_size_skyE = [0., 0., 0.] self.deconv_size_skyE_uncorr = [0., 0., 0.] self.total_flux = tot self.total_fluxE = errors[6] self.peak_fluxE = errors[0] self.total_fluxE = errors[6] self.centre_pixE = errors[1:3] self.size_pixE = errors[3:6] self.rms = img.islands[isl_idx].rms self.mean = img.islands[isl_idx].mean self.total_flux_isl = img.islands[isl_idx].total_flux self.total_flux_islE = img.islands[isl_idx].total_fluxE
def __call__(self, img): if img.opts.psf_vary_do: mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Psf_Vary") mylogger.userinfo(mylog, '\nEstimating PSF variations') opts = img.opts dir = img.basedir + '/misc/' plot = False # debug figures image = img.ch0_arr try: from astropy.io import fits as pyfits old_pyfits = False except ImportError, err: from distutils.version import StrictVersion import pyfits if StrictVersion(pyfits.__version__) < StrictVersion('2.2'): old_pyfits = True else: old_pyfits = False if old_pyfits: mylog.warning('PyFITS version is too old: psf_vary module skipped') return if opts.psf_fwhm is not None: # User has specified a constant PSF to use, so skip PSF fitting/etc. psf_maj = opts.psf_fwhm[0] # FWHM in deg psf_min = opts.psf_fwhm[1] # FWHM in deg psf_pa = opts.psf_fwhm[2] # PA in deg mylogger.userinfo(mylog, 'Using constant PSF (major, minor, pos angle)', '(%.5e, %.5e, %s) degrees' % (psf_maj, psf_maj, round(psf_pa, 1))) else: # Use did not specify a constant PSF to use, so estimate it over = 2 generators = opts.psf_generators; nsig = opts.psf_nsig; kappa2 = opts.psf_kappa2 snrtop = opts.psf_snrtop; snrbot = opts.psf_snrbot; snrcutstack = opts.psf_snrcutstack gencode = opts.psf_gencode; primarygen = opts.psf_primarygen; itess_method = opts.psf_itess_method tess_sc = opts.psf_tess_sc; tess_fuzzy= opts.psf_tess_fuzzy bright_snr_cut = opts.psf_high_snr s_only = opts.psf_stype_only if opts.psf_snrcut < 5.0: mylogger.userinfo(mylog, "Value of psf_snrcut too low; increasing to 5") snrcut = 5.0 else: snrcut = opts.psf_snrcut img.psf_snrcut = snrcut if opts.psf_high_snr is not None: if opts.psf_high_snr < 10.0: mylogger.userinfo(mylog, "Value of psf_high_snr too low; increasing to 10") high_snrcut = 10.0 else: high_snrcut = opts.psf_high_snr else: high_snrcut = opts.psf_high_snr img.psf_high_snr = high_snrcut wtfns=['unity', 'roundness', 'log10', 'sqrtlog10'] if 0 <= itess_method < 4: tess_method=wtfns[itess_method] else: tess_method='unity' ### now put all relevant gaussian parameters into a list ngaus = img.ngaus nsrc = img.nsrc num = N.zeros(nsrc, dtype=N.int32) peak = N.zeros(nsrc) xc = N.zeros(nsrc) yc = N.zeros(nsrc) bmaj = N.zeros(nsrc) bmin = N.zeros(nsrc) bpa = N.zeros(nsrc) code = N.array(['']*nsrc); rms = N.zeros(nsrc) src_id_list = [] for i, src in enumerate(img.sources): src_max = 0.0 for gmax in src.gaussians: # Take only brightest Gaussian per source if gmax.peak_flux > src_max: src_max = gmax.peak_flux g = gmax num[i] = i peak[i] = g.peak_flux xc[i] = g.centre_pix[0] yc[i] = g.centre_pix[1] bmaj[i] = g.size_pix[0] bmin[i] = g.size_pix[1] bpa[i] = g.size_pix[2] code[i] = img.sources[g.source_id].code rms[i] = img.islands[g.island_id].rms gauls = (num, peak, xc, yc, bmaj, bmin, bpa, code, rms) tr_gauls = self.trans_gaul(gauls) # takes gaussians with code=S and snr > snrcut. if s_only: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut and n[7] == 'S'] else: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut] g_gauls = self.trans_gaul(tr) # computes statistics of fitted sizes. Same as psfvary_fullstat.f in fBDSM. bmaj_a, bmaj_r, bmaj_ca, bmaj_cr, ni = _cbdsm.bstat(bmaj, None, nsig) bmin_a, bmin_r, bmin_ca, bmin_cr, ni = _cbdsm.bstat(bmin, None, nsig) bpa_a, bpa_r, bpa_ca, bpa_cr, ni = _cbdsm.bstat(bpa, None, nsig) # get subset of sources deemed to be unresolved. Same as size_ksclip_wenss.f in fBDSM. flag_unresolved = self.get_unresolved(g_gauls, img.beam, nsig, kappa2, over, img.psf_high_snr, plot) if len(flag_unresolved) == 0: mylog.warning('Insufficient number of sources to determine PSF variation.\nTry changing the PSF options or specify a (constant) PSF with the "psf_fwhm" option') return # see how much the SNR-weighted sizes of unresolved sources differ from the synthesized beam. wtsize_beam_snr = self.av_psf(g_gauls, img.beam, flag_unresolved) # filter out resolved sources tr_gaul = self.trans_gaul(g_gauls) tr = [n for i, n in enumerate(tr_gaul) if flag_unresolved[i]] g_gauls = self.trans_gaul(tr) mylogger.userinfo(mylog, 'Number of unresolved sources', str(len(g_gauls[0]))) # get a list of voronoi generators. vorogenS has values (and not None) if generators='field'. vorogenP, vorogenS = self.get_voronoi_generators(g_gauls, generators, gencode, snrcut, snrtop, snrbot, snrcutstack) mylogger.userinfo(mylog, 'Number of generators for PSF variation', str(len(vorogenP[0]))) if len(vorogenP[0]) < 3: mylog.warning('Insufficient number of generators') return mylogger.userinfo(mylog, 'Tesselating image') # group generators into tiles tile_prop = self.edit_vorogenlist(vorogenP, frac=0.9) # tesselate the image volrank, vorowts = self.tesselate(vorogenP, vorogenS, tile_prop, tess_method, tess_sc, tess_fuzzy, \ generators, gencode, image.shape) if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.volrank.fits', volrank, img, dir) tile_list, tile_coord, tile_snr = tile_prop ntile = len(tile_list) bar = statusbar.StatusBar('Determining PSF variation ............... : ', 0, ntile) mylogger.userinfo(mylog, 'Number of tiles for PSF variation', str(ntile)) # For each tile, calculate the weighted averaged psf image. Also for all the sources in the image. cdelt = list(img.wcs_obj.acdelt[0:2]) factor=3. psfimages, psfcoords, totpsfimage, psfratio, psfratio_aper = self.psf_in_tile(image, img.beam, g_gauls, \ cdelt, factor, snrcutstack, volrank, tile_prop, plot, img) npsf = len(psfimages) if opts.psf_use_shap: if opts.psf_fwhm is None: # use totpsfimage to get beta, centre and nmax for shapelet decomposition. Use nmax=5 or 6 mask=N.zeros(totpsfimage.shape, dtype=bool) (m1, m2, m3)=func.moment(totpsfimage, mask) betainit=sqrt(m3[0]*m3[1])*2.0 * 1.4 tshape = totpsfimage.shape cen = N.array(N.unravel_index(N.argmax(totpsfimage), tshape))+[1,1] cen = tuple(cen) nmax = 12 basis = 'cartesian' betarange = [0.5,sqrt(betainit*max(tshape))] beta, error = sh.shape_varybeta(totpsfimage, mask, basis, betainit, cen, nmax, betarange, plot) if error == 1: print ' Unable to find minimum in beta' # decompose all the psf images using the beta from above nmax=12; psf_cf=[] for i in range(npsf): psfim = psfimages[i] cf = sh.decompose_shapelets(psfim, mask, basis, beta, cen, nmax, mode='') psf_cf.append(cf) if img.opts.quiet == False: bar.increment() bar.stop() # transpose the psf image list xt, yt = N.transpose(tile_coord) tr_psf_cf = N.transpose(N.array(psf_cf)) # interpolate the coefficients across the image. Ok, interpolate in scipy for # irregular grids is crap. doesnt even pass through some of the points. # for now, fit polynomial. compress = 100.0 x, y = N.transpose(psfcoords) if len(x) < 3: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return psf_coeff_interp, xgrid, ygrid = self.interp_shapcoefs(nmax, tr_psf_cf, psfcoords, image.shape, \ compress, plot) psfshape = psfimages[0].shape skip = 5 aa = self.create_psf_grid(psf_coeff_interp, image.shape, xgrid, ygrid, skip, nmax, psfshape, \ basis, beta, cen, totpsfimage, plot) img.psf_images = aa else: if opts.psf_fwhm is None: if ntile < 4: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return else: # Fit stacked PSFs with Gaussians and measure aperture fluxes bm_pix = N.array([img.pixel_beam()[0]*fwsig, img.pixel_beam()[1]*fwsig, img.pixel_beam()[2]]) psf_maj = N.zeros(npsf) psf_min = N.zeros(npsf) psf_pa = N.zeros(npsf) if img.opts.quiet == False: bar.start() for i in range(ntile): psfim = psfimages[i] mask = N.zeros(psfim.shape, dtype=bool) x_ax, y_ax = N.indices(psfim.shape) maxv = N.max(psfim) p_ini = [maxv, (psfim.shape[0]-1)/2.0*1.1, (psfim.shape[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, bm_pix[1]/fwsig*1.1, bm_pix[2]*2] para, ierr = func.fit_gaus2d(psfim, p_ini, x_ax, y_ax, mask) ### first extent is major if para[3] < para[4]: para[3:5] = para[4:2:-1] para[5] += 90 ### clip position angle para[5] = divmod(para[5], 180)[1] psf_maj[i] = para[3] psf_min[i] = para[4] posang = para[5] while posang >= 180.0: posang -= 180.0 psf_pa[i] = posang if img.opts.quiet == False: bar.increment() bar.stop() # Interpolate Gaussian parameters if img.aperture is None: psf_maps = [psf_maj, psf_min, psf_pa, psfratio] else: psf_maps = [psf_maj, psf_min, psf_pa, psfratio, psfratio_aper] nimgs = len(psf_maps) bar = statusbar.StatusBar('Interpolating PSF images ................ : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.interp_prop), psf_maps, itertools.repeat(psfcoords), itertools.repeat(image.shape)), numcores=opts.ncores, bar=bar) if img.aperture is None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Smooth if desired if img.opts.psf_smooth is not None: sm_scale = img.opts.psf_smooth / img.pix2beam([1.0, 1.0, 0.0])[0] / 3600.0 # pixels if img.opts.aperture is None: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int] else: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int] nimgs = len(psf_maps) bar = statusbar.StatusBar('Smoothing PSF images .................... : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.blur_image), psf_maps, itertools.repeat(sm_scale)), numcores=opts.ncores, bar=bar) if img.aperture is None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Make sure all smoothed, interpolated images are ndarrays psf_maj_int = N.array(psf_maj_int) psf_min_int = N.array(psf_min_int) psf_pa_int = N.array(psf_pa_int) psf_ratio_int = N.array(psf_ratio_int) if img.aperture is None: psf_ratio_aper_int = N.zeros(psf_maj_int.shape, dtype=N.float32) else: psf_ratio_aper_int = N.array(psf_ratio_aper_int, dtype=N.float32) # Blank with NaNs if needed mask = img.mask_arr if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) psf_maj_int[pix_masked] = N.nan psf_min_int[pix_masked] = N.nan psf_pa_int[pix_masked] = N.nan psf_ratio_int[pix_masked] = N.nan psf_ratio_aper_int[pix_masked] = N.nan # Store interpolated images. The major and minor axis images are # the sigma in units of arcsec, the PA image in units of degrees east of # north, the ratio images in units of 1/beam. img.psf_vary_maj_arr = psf_maj_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_min_arr = psf_min_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_pa_arr = psf_pa_int img.psf_vary_ratio_arr = psf_ratio_int # in 1/beam img.psf_vary_ratio_aper_arr = psf_ratio_aper_int # in 1/beam if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_maj.fits', img.psf_vary_maj_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_min.fits', img.psf_vary_min_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_pa.fits', img.psf_vary_pa_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio.fits', img.psf_vary_ratio_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio_aper.fits', img.psf_vary_ratio_aper_arr, img, dir) # Loop through source and Gaussian lists and deconvolve the sizes using appropriate beam bar2 = statusbar.StatusBar('Correcting deconvolved source sizes ..... : ', 0, img.nsrc) if img.opts.quiet == False: bar2.start() for src in img.sources: src_pos = img.sky2pix(src.posn_sky_centroid) src_pos_int = (int(src_pos[0]), int(src_pos[1])) gaus_c = img.gaus2pix(src.size_sky, src.posn_sky_centroid) if opts.psf_fwhm is None: gaus_bm = [psf_maj_int[src_pos_int]*fwsig, psf_min_int[src_pos_int]*fwsig, psf_pa_int[src_pos_int]] else: # Use user-specified constant PSF instead gaus_bm = img.beam2pix(opts.psf_fwhm) gaus_dc, err = func.deconv2(gaus_bm, gaus_c) src.deconv_size_sky = img.pix2gaus(gaus_dc, src_pos) src.deconv_size_skyE = [0.0, 0.0, 0.0] for g in src.gaussians: gaus_c = img.gaus2pix(g.size_sky, src.posn_sky_centroid) gaus_dc, err = func.deconv2(gaus_bm, gaus_c) g.deconv_size_sky = img.pix2gaus(gaus_dc, g.centre_pix) g.deconv_size_skyE = [0.0, 0.0, 0.0] if img.opts.quiet == False: bar2.spin() if img.opts.quiet == False: bar2.increment() bar2.stop() img.completed_Ops.append('psf_vary')