def fit_channels(self, img, chan_images, clip_rms, src, beamlist): """Fits normalizations of Gaussians in source to multiple channels If unresolved, the size of the Gaussians are adjusted to match the channel's beam size (given by beamlist) before fitting. Returns array of total fluxes (N_channels x N_Gaussians) and array of errors (N_channels x N_Gaussians). """ import functions as func from const import fwsig isl = img.islands[src.island_id] isl_bbox = isl.bbox nchan = chan_images.shape[0] x, y = N.mgrid[isl_bbox] gg = src.gaussians fitfix = N.ones(len(gg)) # fit only normalization srcmask = isl.mask_active total_flux = N.zeros( (nchan, len(fitfix))) # array of fluxes: N_channels x N_Gaussians errors = N.zeros( (nchan, len(fitfix))) # array of fluxes: N_channels x N_Gaussians for cind in range(nchan): image = chan_images[cind] gg_adj = self.adjust_size_by_freq(img.beam, beamlist[cind], gg) p, ep = func.fit_mulgaus2d(image, gg_adj, x, y, srcmask, fitfix, adj=True) pbeam = img.beam2pix(beamlist[cind]) bm_pix = (pbeam[0] / fwsig, pbeam[1] / fwsig, pbeam[2] ) # IN SIGMA UNITS for ig in range(len(fitfix)): total_flux[cind, ig] = p[ig * 6] * p[ig * 6 + 3] * p[ig * 6 + 4] / ( bm_pix[0] * bm_pix[1]) p = N.insert(p, N.arange(len(fitfix)) * 6 + 6, total_flux[cind]) rms_isl = N.mean(clip_rms[cind]) errors[cind] = func.get_errors(img, p, rms_isl, bm_pix=(bm_pix[0] * fwsig, bm_pix[1] * fwsig, bm_pix[2]))[6] self.reset_size(gg) return total_flux, errors
def fit_channels(self, img, chan_images, clip_rms, src, beamlist): """Fits normalizations of Gaussians in source to multiple channels If unresolved, the size of the Gaussians are adjusted to match the channel's beam size (given by beamlist) before fitting. Returns array of total fluxes (N_channels x N_Gaussians) and array of errors (N_channels x N_Gaussians). """ import functions as func from const import fwsig isl = img.islands[src.island_id] isl_bbox = isl.bbox nchan = chan_images.shape[0] x, y = N.mgrid[isl_bbox] gg = src.gaussians fitfix = N.ones(len(gg)) # fit only normalization srcmask = isl.mask_active total_flux = N.zeros((nchan, len(fitfix))) # array of fluxes: N_channels x N_Gaussians errors = N.zeros((nchan, len(fitfix))) # array of fluxes: N_channels x N_Gaussians for cind in range(nchan): image = chan_images[cind] gg_adj = self.adjust_size_by_freq(img.beam, beamlist[cind], gg) p, ep = func.fit_mulgaus2d(image, gg_adj, x, y, srcmask, fitfix, adj=True) pbeam = img.beam2pix(beamlist[cind]) bm_pix = (pbeam[0]/fwsig, pbeam[1]/fwsig, pbeam[2]) # IN SIGMA UNITS for ig in range(len(fitfix)): total_flux[cind, ig] = p[ig*6]*p[ig*6+3]*p[ig*6+4]/(bm_pix[0]*bm_pix[1]) p = N.insert(p, N.arange(len(fitfix))*6+6, total_flux[cind]) rms_isl = N.mean(clip_rms[cind]) errors[cind] = func.get_errors(img, p, rms_isl, bm_pix=(bm_pix[0]*fwsig, bm_pix[1]*fwsig, bm_pix[2]))[6] self.reset_size(gg) return total_flux, errors
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Polarisatn") if img.opts.polarisation_do: mylog.info('Extracting polarisation properties for all sources') pols = ['I', 'Q', 'U', 'V'] # Run gausfit and gual2srl on PI image to look for polarized sources # undetected in I fit_PI = img.opts.pi_fit n_new = 0 ch0_pi = N.sqrt(img.ch0_Q_arr**2 + img.ch0_U_arr**2) img.ch0_pi_arr = ch0_pi if fit_PI: from . import _run_op_list mylogger.userinfo(mylog, "\nChecking PI image for new sources") mask = img.mask_arr minsize = img.opts.minpix_isl # Set up image object for PI image. pi_chain, pi_opts = self.setpara_bdsm(img) pimg = Image(pi_opts) pimg.beam = img.beam pimg.pixel_beam = img.pixel_beam pimg.pixel_beamarea = img.pixel_beamarea pimg.log = 'PI.' pimg.pix2beam = img.pix2beam pimg.beam2pix = img.beam2pix pimg.pix2gaus = img.pix2gaus pimg.gaus2pix = img.gaus2pix pimg.pix2sky = img.pix2sky pimg.sky2pix = img.sky2pix pimg.pix2coord = img.pix2coord pimg.wcs_obj = img.wcs_obj pimg.mask_arr = mask pimg.masked = img.masked pimg.ch0_arr = ch0_pi pimg._pi = True success = _run_op_list(pimg, pi_chain) if not success: return img.pi_islands = pimg.islands img.pi_gaussians = pimg.gaussians img.pi_sources = pimg.sources # Now check for new sources in the PI image that are not # found in the Stokes I image. If any new sources are found, # adjust their IDs to follow after those found in I. new_isl = [] new_src = [] new_gaus = [] n_new_src = 0 isl_id = img.islands[-1].island_id src_id = img.sources[-1].source_id gaus_id = img.gaussians[-1].gaus_num for pi_isl in pimg.islands: new_sources = [] for pi_src in pi_isl.sources: if img.pyrank[int(img.sky2pix(pi_src.posn_sky_max)[0]), int(img.sky2pix(pi_src.posn_sky_max)[1] )] == -1: src_id += 1 pi_src._pi = True pi_src.island_id = isl_id pi_src.source_id = src_id pi_src.spec_indx = N.NaN pi_src.e_spec_indx = N.NaN pi_src.spec_norm = N.NaN pi_src.specin_flux = [N.NaN] pi_src.specin_fluxE = [N.NaN] pi_src.specin_freq = [N.NaN] pi_src.specin_freq0 = N.NaN new_sources.append(pi_src) new_src.append(pi_src) n_new_src += 1 for g in pi_src.gaussians: gaus_id += 1 new_gaus.append(g) g.gaus_num = gaus_id if len(new_sources) > 0: isl_id += 1 pi_isl.sources = new_sources pi_isl.island_id = isl_id pi_isl._pi = True new_isl.append(pi_isl) n_new = len(new_isl) mylogger.userinfo(mylog, "New sources found in PI image", '%i (%i total)' % (n_new, img.nsrc + n_new)) if n_new > 0: img.islands += new_isl img.sources += new_src img.gaussians += new_gaus img.nsrc += n_new_src bar = statusbar.StatusBar( 'Calculating polarisation properties .... : ', 0, img.nsrc) if img.opts.quiet == False: bar.start() for isl in img.islands: isl_bbox = isl.bbox ch0_I = img.ch0_arr[isl_bbox] ch0_Q = img.ch0_Q_arr[isl_bbox] ch0_U = img.ch0_U_arr[isl_bbox] ch0_V = img.ch0_V_arr[isl_bbox] ch0_images = [ch0_I, ch0_Q, ch0_U, ch0_V] for i, src in enumerate(isl.sources): # For each source, assume the morphology does not change # across the Stokes cube. This assumption allows us to fit # the Gaussians of each source to each Stokes image by # simply fitting only the overall normalizations of the # individual Gaussians. # # First, fit all source Gaussians to each Stokes image: x, y = N.mgrid[isl_bbox] gg = src.gaussians fitfix = N.ones(len(gg)) # fit only normalization srcmask = isl.mask_active total_flux = N.zeros( (4, len(fitfix)), dtype=N.float32 ) # array of fluxes: N_Stokes x N_Gaussians errors = N.zeros( (4, len(fitfix)), dtype=N.float32 ) # array of fluxes: N_Stokes x N_Gaussians for sind, image in enumerate(ch0_images): if (sind == 0 and hasattr(src, '_pi') ) or sind > 0: # Fit I only for PI sources p, ep = func.fit_mulgaus2d(image, gg, x, y, srcmask, fitfix) for ig in range(len(fitfix)): center_pix = (p[ig * 6 + 1], p[ig * 6 + 2]) bm_pix = N.array([ img.pixel_beam()[0], img.pixel_beam()[1], img.pixel_beam()[2] ]) total_flux[sind, ig] = p[ig * 6] * p[ ig * 6 + 3] * p[ig * 6 + 4] / (bm_pix[0] * bm_pix[1]) p = N.insert(p, N.arange(len(fitfix)) * 6 + 6, total_flux[sind]) if sind > 0: rms_img = img.__getattribute__('rms_' + pols[sind] + '_arr') else: rms_img = img.rms_arr if len(rms_img.shape) > 1: rms_isl = rms_img[isl.bbox].mean() else: rms_isl = rms_img errors[sind] = func.get_errors(img, p, rms_isl)[6] # Now, assign fluxes to each Gaussian. src_flux_I = 0.0 src_flux_Q = 0.0 src_flux_U = 0.0 src_flux_V = 0.0 src_flux_I_err_sq = 0.0 src_flux_Q_err_sq = 0.0 src_flux_U_err_sq = 0.0 src_flux_V_err_sq = 0.0 for ig, gaussian in enumerate(src.gaussians): flux_I = total_flux[0, ig] flux_I_err = abs(errors[0, ig]) flux_Q = total_flux[1, ig] flux_Q_err = abs(errors[1, ig]) flux_U = total_flux[2, ig] flux_U_err = abs(errors[2, ig]) flux_V = total_flux[3, ig] flux_V_err = abs(errors[3, ig]) if hasattr(src, '_pi'): gaussian.total_flux = flux_I gaussian.total_fluxE = flux_I_err gaussian.total_flux_Q = flux_Q gaussian.total_flux_U = flux_U gaussian.total_flux_V = flux_V gaussian.total_fluxE_Q = flux_Q_err gaussian.total_fluxE_U = flux_U_err gaussian.total_fluxE_V = flux_V_err if hasattr(src, '_pi'): src_flux_I += flux_I src_flux_I_err_sq += flux_I_err**2 src_flux_Q += flux_Q src_flux_U += flux_U src_flux_V += flux_V src_flux_Q_err_sq += flux_Q_err**2 src_flux_U_err_sq += flux_U_err**2 src_flux_V_err_sq += flux_V_err**2 # Calculate and store polarisation fractions and angle for each Gaussian in the island # For this we need the I flux, which we can just take from g.total_flux and src.total_flux flux_I = gaussian.total_flux flux_I_err = gaussian.total_fluxE stokes = [flux_I, flux_Q, flux_U, flux_V] stokes_err = [ flux_I_err, flux_Q_err, flux_U_err, flux_V_err ] lpol_frac, lpol_frac_loerr, lpol_frac_hierr = self.calc_lpol_fraction( stokes, stokes_err) # linear pol fraction lpol_ang, lpol_ang_err = self.calc_lpol_angle( stokes, stokes_err) # linear pol angle cpol_frac, cpol_frac_loerr, cpol_frac_hierr = self.calc_cpol_fraction( stokes, stokes_err) # circular pol fraction tpol_frac, tpol_frac_loerr, tpol_frac_hierr = self.calc_tpol_fraction( stokes, stokes_err) # total pol fraction gaussian.lpol_fraction = lpol_frac gaussian.lpol_fraction_loerr = lpol_frac_loerr gaussian.lpol_fraction_hierr = lpol_frac_hierr gaussian.cpol_fraction = cpol_frac gaussian.cpol_fraction_loerr = cpol_frac_loerr gaussian.cpol_fraction_hierr = cpol_frac_hierr gaussian.tpol_fraction = tpol_frac gaussian.tpol_fraction_loerr = tpol_frac_loerr gaussian.tpol_fraction_hierr = tpol_frac_hierr gaussian.lpol_angle = lpol_ang gaussian.lpol_angle_err = lpol_ang_err # Store fluxes for each source in the island if hasattr(src, '_pi'): src.total_flux = src_flux_I src.total_fluxE = N.sqrt(src_flux_I_err_sq) src.total_flux_Q = src_flux_Q src.total_flux_U = src_flux_U src.total_flux_V = src_flux_V src.total_fluxE_Q = N.sqrt(src_flux_Q_err_sq) src.total_fluxE_U = N.sqrt(src_flux_U_err_sq) src.total_fluxE_V = N.sqrt(src_flux_V_err_sq) # Calculate and store polarisation fractions and angle for each source in the island # For this we need the I flux, which we can just take from g.total_flux and src.total_flux src_flux_I = src.total_flux src_flux_I_err = src.total_fluxE stokes = [src_flux_I, src_flux_Q, src_flux_U, src_flux_V] stokes_err = [ src_flux_I_err, N.sqrt(src_flux_Q_err_sq), N.sqrt(src_flux_U_err_sq), N.sqrt(src_flux_V_err_sq) ] lpol_frac, lpol_frac_loerr, lpol_frac_hierr = self.calc_lpol_fraction( stokes, stokes_err) # linear pol fraction lpol_ang, lpol_ang_err = self.calc_lpol_angle( stokes, stokes_err) # linear pol angle cpol_frac, cpol_frac_loerr, cpol_frac_hierr = self.calc_cpol_fraction( stokes, stokes_err) # circular pol fraction tpol_frac, tpol_frac_loerr, tpol_frac_hierr = self.calc_tpol_fraction( stokes, stokes_err) # total pol fraction src.lpol_fraction = lpol_frac src.lpol_fraction_loerr = lpol_frac_loerr src.lpol_fraction_hierr = lpol_frac_hierr src.cpol_fraction = cpol_frac src.cpol_fraction_loerr = cpol_frac_loerr src.cpol_fraction_hierr = cpol_frac_hierr src.tpol_fraction = tpol_frac src.tpol_fraction_loerr = tpol_frac_loerr src.tpol_fraction_hierr = tpol_frac_hierr src.lpol_angle = lpol_ang src.lpol_angle_err = lpol_ang_err if bar.started: bar.increment() bar.stop() img.completed_Ops.append('polarisation')
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
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 __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Polarisatn") if img.opts.polarisation_do: mylog.info('Extracting polarisation properties for all sources') pols = ['I', 'Q', 'U', 'V'] # Run gausfit and gual2srl on PI image to look for polarized sources # undetected in I fit_PI = img.opts.pi_fit n_new = 0 ch0_pi = N.sqrt(img.ch0_Q_arr**2 + img.ch0_U_arr**2) img.ch0_pi_arr = ch0_pi if fit_PI: from . import _run_op_list mylogger.userinfo(mylog, "\nChecking PI image for new sources") mask = img.mask_arr minsize = img.opts.minpix_isl # Set up image object for PI image. pi_chain, pi_opts = self.setpara_bdsm(img) pimg = Image(pi_opts) pimg.beam = img.beam pimg.pixel_beam = img.pixel_beam pimg.pixel_beamarea = img.pixel_beamarea pimg.log = 'PI.' pimg.pix2beam = img.pix2beam pimg.beam2pix = img.beam2pix pimg.pix2gaus = img.pix2gaus pimg.gaus2pix = img.gaus2pix pimg.pix2sky = img.pix2sky pimg.sky2pix = img.sky2pix pimg.pix2coord = img.pix2coord pimg.wcs_obj = img.wcs_obj pimg.mask_arr = mask pimg.masked = img.masked pimg.ch0_arr = ch0_pi pimg._pi = True success = _run_op_list(pimg, pi_chain) if not success: return img.pi_islands = pimg.islands img.pi_gaussians = pimg.gaussians img.pi_sources = pimg.sources # Now check for new sources in the PI image that are not # found in the Stokes I image. If any new sources are found, # adjust their IDs to follow after those found in I. new_isl = [] new_src = [] new_gaus = [] n_new_src = 0 isl_id = img.islands[-1].island_id src_id = img.sources[-1].source_id gaus_id = img.gaussians[-1].gaus_num for pi_isl in pimg.islands: new_sources = [] for pi_src in pi_isl.sources: if img.pyrank[int(img.sky2pix(pi_src.posn_sky_max)[0]), int(img.sky2pix(pi_src.posn_sky_max)[1])] == -1: src_id += 1 pi_src._pi = True pi_src.island_id = isl_id pi_src.source_id = src_id pi_src.spec_indx = N.NaN pi_src.e_spec_indx = N.NaN pi_src.spec_norm = N.NaN pi_src.specin_flux = [N.NaN] pi_src.specin_fluxE = [N.NaN] pi_src.specin_freq = [N.NaN] pi_src.specin_freq0 = N.NaN new_sources.append(pi_src) new_src.append(pi_src) n_new_src += 1 for g in pi_src.gaussians: gaus_id += 1 new_gaus.append(g) g.gaus_num = gaus_id if len(new_sources) > 0: isl_id += 1 pi_isl.sources = new_sources pi_isl.island_id = isl_id pi_isl._pi = True new_isl.append(pi_isl) n_new = len(new_isl) mylogger.userinfo(mylog, "New sources found in PI image", '%i (%i total)' % (n_new, img.nsrc+n_new)) if n_new > 0: img.islands += new_isl img.sources += new_src img.gaussians += new_gaus img.nsrc += n_new_src bar = statusbar.StatusBar('Calculating polarisation properties .... : ', 0, img.nsrc) if img.opts.quiet == False: bar.start() for isl in img.islands: isl_bbox = isl.bbox ch0_I = img.ch0_arr[isl_bbox] ch0_Q = img.ch0_Q_arr[isl_bbox] ch0_U = img.ch0_U_arr[isl_bbox] ch0_V = img.ch0_V_arr[isl_bbox] ch0_images = [ch0_I, ch0_Q, ch0_U, ch0_V] for i, src in enumerate(isl.sources): # For each source, assume the morphology does not change # across the Stokes cube. This assumption allows us to fit # the Gaussians of each source to each Stokes image by # simply fitting only the overall normalizations of the # individual Gaussians. # # First, fit all source Gaussians to each Stokes image: x, y = N.mgrid[isl_bbox] gg = src.gaussians fitfix = N.ones(len(gg)) # fit only normalization srcmask = isl.mask_active total_flux = N.zeros((4, len(fitfix)), dtype=N.float32) # array of fluxes: N_Stokes x N_Gaussians errors = N.zeros((4, len(fitfix)), dtype=N.float32) # array of fluxes: N_Stokes x N_Gaussians for sind, image in enumerate(ch0_images): if (sind==0 and hasattr(src, '_pi')) or sind > 0: # Fit I only for PI sources p, ep = func.fit_mulgaus2d(image, gg, x, y, srcmask, fitfix) for ig in range(len(fitfix)): center_pix = (p[ig*6 + 1], p[ig*6 + 2]) bm_pix = N.array([img.pixel_beam()[0], img.pixel_beam()[1], img.pixel_beam()[2]]) total_flux[sind, ig] = p[ig*6]*p[ig*6+3]*p[ig*6+4]/(bm_pix[0]*bm_pix[1]) p = N.insert(p, N.arange(len(fitfix))*6+6, total_flux[sind]) if sind > 0: rms_img = img.__getattribute__('rms_'+pols[sind]+'_arr') else: rms_img = img.rms_arr if len(rms_img.shape) > 1: rms_isl = rms_img[isl.bbox].mean() else: rms_isl = rms_img errors[sind] = func.get_errors(img, p, rms_isl)[6] # Now, assign fluxes to each Gaussian. src_flux_I = 0.0 src_flux_Q = 0.0 src_flux_U = 0.0 src_flux_V = 0.0 src_flux_I_err_sq = 0.0 src_flux_Q_err_sq = 0.0 src_flux_U_err_sq = 0.0 src_flux_V_err_sq = 0.0 for ig, gaussian in enumerate(src.gaussians): flux_I = total_flux[0, ig] flux_I_err = abs(errors[0, ig]) flux_Q = total_flux[1, ig] flux_Q_err = abs(errors[1, ig]) flux_U = total_flux[2, ig] flux_U_err = abs(errors[2, ig]) flux_V = total_flux[3, ig] flux_V_err = abs(errors[3, ig]) if hasattr(src, '_pi'): gaussian.total_flux = flux_I gaussian.total_fluxE = flux_I_err gaussian.total_flux_Q = flux_Q gaussian.total_flux_U = flux_U gaussian.total_flux_V = flux_V gaussian.total_fluxE_Q = flux_Q_err gaussian.total_fluxE_U = flux_U_err gaussian.total_fluxE_V = flux_V_err if hasattr(src, '_pi'): src_flux_I += flux_I src_flux_I_err_sq += flux_I_err**2 src_flux_Q += flux_Q src_flux_U += flux_U src_flux_V += flux_V src_flux_Q_err_sq += flux_Q_err**2 src_flux_U_err_sq += flux_U_err**2 src_flux_V_err_sq += flux_V_err**2 # Calculate and store polarisation fractions and angle for each Gaussian in the island # For this we need the I flux, which we can just take from g.total_flux and src.total_flux flux_I = gaussian.total_flux flux_I_err = gaussian.total_fluxE stokes = [flux_I, flux_Q, flux_U, flux_V] stokes_err = [flux_I_err, flux_Q_err, flux_U_err, flux_V_err] lpol_frac, lpol_frac_loerr, lpol_frac_hierr = self.calc_lpol_fraction(stokes, stokes_err) # linear pol fraction lpol_ang, lpol_ang_err = self.calc_lpol_angle(stokes, stokes_err) # linear pol angle cpol_frac, cpol_frac_loerr, cpol_frac_hierr = self.calc_cpol_fraction(stokes, stokes_err) # circular pol fraction tpol_frac, tpol_frac_loerr, tpol_frac_hierr = self.calc_tpol_fraction(stokes, stokes_err) # total pol fraction gaussian.lpol_fraction = lpol_frac gaussian.lpol_fraction_loerr = lpol_frac_loerr gaussian.lpol_fraction_hierr = lpol_frac_hierr gaussian.cpol_fraction = cpol_frac gaussian.cpol_fraction_loerr = cpol_frac_loerr gaussian.cpol_fraction_hierr = cpol_frac_hierr gaussian.tpol_fraction = tpol_frac gaussian.tpol_fraction_loerr = tpol_frac_loerr gaussian.tpol_fraction_hierr = tpol_frac_hierr gaussian.lpol_angle = lpol_ang gaussian.lpol_angle_err = lpol_ang_err # Store fluxes for each source in the island if hasattr(src, '_pi'): src.total_flux = src_flux_I src.total_fluxE = N.sqrt(src_flux_I_err_sq) src.total_flux_Q = src_flux_Q src.total_flux_U = src_flux_U src.total_flux_V = src_flux_V src.total_fluxE_Q = N.sqrt(src_flux_Q_err_sq) src.total_fluxE_U = N.sqrt(src_flux_U_err_sq) src.total_fluxE_V = N.sqrt(src_flux_V_err_sq) # Calculate and store polarisation fractions and angle for each source in the island # For this we need the I flux, which we can just take from g.total_flux and src.total_flux src_flux_I = src.total_flux src_flux_I_err = src.total_fluxE stokes = [src_flux_I, src_flux_Q, src_flux_U, src_flux_V] stokes_err = [src_flux_I_err, N.sqrt(src_flux_Q_err_sq), N.sqrt(src_flux_U_err_sq), N.sqrt(src_flux_V_err_sq)] lpol_frac, lpol_frac_loerr, lpol_frac_hierr = self.calc_lpol_fraction(stokes, stokes_err) # linear pol fraction lpol_ang, lpol_ang_err = self.calc_lpol_angle(stokes, stokes_err) # linear pol angle cpol_frac, cpol_frac_loerr, cpol_frac_hierr = self.calc_cpol_fraction(stokes, stokes_err) # circular pol fraction tpol_frac, tpol_frac_loerr, tpol_frac_hierr = self.calc_tpol_fraction(stokes, stokes_err) # total pol fraction src.lpol_fraction = lpol_frac src.lpol_fraction_loerr = lpol_frac_loerr src.lpol_fraction_hierr = lpol_frac_hierr src.cpol_fraction = cpol_frac src.cpol_fraction_loerr = cpol_frac_loerr src.cpol_fraction_hierr = cpol_frac_hierr src.tpol_fraction = tpol_frac src.tpol_fraction_loerr = tpol_frac_loerr src.tpol_fraction_hierr = tpol_frac_hierr src.lpol_angle = lpol_ang src.lpol_angle_err = lpol_ang_err if bar.started: bar.increment() bar.stop() img.completed_Ops.append('polarisation')