def photometry(data, mywcs, regs, beam, alphamap=None, alphaerrmap=None, parsuffix=''): results = {} for ii, reg in enumerate(regs): if 'text' not in reg.meta: name = str(ii) else: name = reg.meta['text'].strip("{}") # all regions are points: convert them to 0.5" circles phot_reg = regions.CircleSkyRegion(center=reg.center, radius=0.5 * u.arcsec) pixreg = phot_reg.to_pixel(mywcs) bgreg = regions.CircleSkyRegion(center=reg.center, radius=1.5 * u.arcsec).to_pixel(mywcs) log.info("Name={0} color={1}".format(name, reg.visual['color'])) mask = pixreg.to_mask() cutout = mask.cutout(data) * mask.data # how do I make an annulus? bgmask = bgreg.to_mask() # manualannulus diff = bgmask.shape[0] - mask.shape[0] bgm = bgmask.data.astype('bool') bgm[int(diff / 2):-int(diff / 2), int(diff / 2):-int(diff / 2)] ^= mask.data.astype('bool') assert bgm.sum() == bgmask.data.sum() - mask.data.sum() bgcutout = bgmask.cutout(data) * bgm results[name] = { 'peak' + parsuffix: cutout.max(), 'sum' + parsuffix: cutout.sum(), 'bgrms' + parsuffix: bgcutout.std(), 'bgmad' + parsuffix: mad_std(bgcutout), 'npix' + parsuffix: mask.data.sum(), 'beam_area' + parsuffix: beam.sr, 'RA' + parsuffix: reg.center.ra[0], 'Dec' + parsuffix: reg.center.dec[0], 'color' + parsuffix: reg.visual['color'], } if alphamap is not None and alphaerrmap is not None: alphacutout = mask.cutout(alphamap) * mask.data alphaerrcutout = mask.cutout(alphaerrmap) * mask.data argmax = np.unravel_index(cutout.argmax(), cutout.shape) results[name]['alpha'] = alphacutout[argmax] results[name]['alphaerror'] = alphaerrcutout[argmax] return results
def regions_from_sources(source_list): region_list = [] for source in source_list: l = source['Glon'] b = source['Glat'] r = Angle(source['radius'], 'arcsec') center = SkyCoord(l, b, unit='deg', frame='galactic') region_list.append(regions.CircleSkyRegion(center, r)) return region_list
def ppcat_to_regions(cat, frame): center = coordinates.SkyCoord(cat['x_cen'], cat['y_cen'], unit=(u.deg, u.deg), frame=frame) rad = cat['radius'].quantity regs = [regions.CircleSkyRegion(cen, rr) for cen, rr in zip(center, rad)] for reg, row in zip(regs, cat): if row['rejected']: reg.visual['color'] = 'red' else: reg.visual['color'] = 'green' return regs
def _parseExReg(self, filename): with open(filename, 'r') as f: lines = f.readlines() reglist = [] for line in lines: if not line.startswith('#'): if line.startswith('circle'): baseLine = line.partition('# color=green pos=') x, y, r = baseLine[0][baseLine[0].find('(') + 1:baseLine[0].find(')')].split(',') ra, dec = baseLine[-1][baseLine[-1].find('(') + 1:baseLine[-1].find(')')].split(',') circ = regions.CircleSkyRegion( center=SkyCoord(np.float(ra), np.float(dec), unit='deg'), radius=Angle(.265 * np.float(r), 'arcsec')) reglist.append(circ) elif line.startswith('box'): baseLine = line.partition('# color=green ') x, y, w, h = baseLine[0][baseLine[0].find('(') + 1:baseLine[0].find(')')].split( ',') ra, dec = baseLine[-1].rstrip().split() ra = np.float(ra[3:]) dec = np.float(dec[4:]) rec = regions.RectangleSkyRegion( center=SkyCoord(ra, dec, unit='deg'), width=Angle(.265 * np.float(w), 'arcsec'), height=Angle(.265 * np.float(h), 'arcsec'), angle=Angle(270, 'deg')) reglist.append(rec) return reglist
def measure_fluxes(data, ref_name, img, name): #This function is to add another image to an already made catalog using positions from a different image. 'data' is the catalog to add measurements to, 'ref_name' is the name of the reference data set (where the positions are from), 'img' is the new image, and 'name' is the name of 'img' cat = Table.read(data) fl = fits.open(img) header = fl[0].header img_data = fl[0].data.squeeze() mywcs = WCS(header).celestial rad = Angle(1, 'arcsecond') #radius for region list rad_deg = rad.to(u.degree) #generate region list regs = [] reg_file = '/users/jotter/summer_research_2018/final_regs/' + name + '_reg_file_circle.reg' with open(reg_file, 'w') as fh: fh.write("fk5\n") for ind in range(len(cat)): reg = regions.CircleSkyRegion(center=SkyCoord( cat['gauss_x_' + ref_name][ind] * u.degree, cat['gauss_y_' + ref_name][ind] * u.degree), radius=rad, meta={'text': str(cat['D_ID'][ind])}) reg_pix = reg.to_pixel(mywcs) if reg_pix.center.x > 0 and reg_pix.center.x < len(img_data[0]): if reg_pix.center.y > 0 and reg_pix.center.y < len(img_data): regs.append(reg) fh.write( 'circle({x_cen}, {y_cen}, {radius}) #text={{{ID}}}\n'. format(x_cen=reg.center.ra.value, y_cen=reg.center.dec.value, radius=0.1 * rad_deg.value, ID=str(cat['D_ID'][ind]))) cat_r = Angle(0.3, 'arcsecond') #radius in gaussian fitting gauss_cat = gaussfit_catalog( img, regs, cat_r, savepath='/lustre/aoc/students/jotter/gauss_diags/leaves/' + name) #output is nested dictionary structure gauss_fit_tab = Table( names=('D_ID', '_idx_' + name, 'gauss_x_' + name, 'x_err_' + name, 'gauss_y_' + name, 'y_err_' + name, 'FWHM_major_' + name, 'major_err_' + name, 'FWHM_minor_' + name, 'minor_err_' + name, 'position_angle_' + name, 'position_angle_err_' + name, 'peak_flux_' + name, 'gauss_amplitude_' + name, 'amplitude_err_' + name, 'ap_flux_' + name, 'ap_flux_err_' + name, 'fit_goodness_' + name), dtype=('i4', 'i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'U10')) #turn into astropy table for key in gauss_cat: gauss_fit_tab.add_row( (key, key, gauss_cat[key]['center_x'], gauss_cat[key]['e_center_x'], gauss_cat[key]['center_y'], gauss_cat[key]['e_center_y'], gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'], gauss_cat[key]['e_pa'], gauss_cat[key]['peak'], gauss_cat[key]['amplitude'], gauss_cat[key]['e_amplitude'], np.nan, np.nan, 'none')) #fill table beam = radio_beam.Beam.from_fits_header(header) pixel_scale = np.abs( mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg ppbeam = (beam.sr / (pixel_scale**2)).decompose().value for row in range(len(gauss_fit_tab)): pix_major_fwhm = ((gauss_fit_tab['FWHM_major_' + name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() pix_minor_fwhm = ((gauss_fit_tab['FWHM_minor_' + name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() cutout_center = SkyCoord(gauss_fit_tab['gauss_x_' + name][row], gauss_fit_tab['gauss_y_' + name][row], frame='icrs', unit=(u.deg, u.deg)) cutout_center_pix = cutout_center.to_pixel(mywcs) cutout_center_pix = regions.PixCoord(cutout_center_pix[0], cutout_center_pix[1]) position_angle = gauss_fit_tab['position_angle_' + name][row] * u.deg ellipse_reg = regions.EllipsePixelRegion(cutout_center_pix, pix_major_fwhm * 2., pix_minor_fwhm * 2., angle=position_angle) size = pix_major_fwhm * 2.1 ap_mask = ellipse_reg.to_mask() cutout_mask = ap_mask.cutout(img_data) aperture_flux = np.sum(cutout_mask[ap_mask.data == 1]) / ppbeam gauss_fit_tab['ap_flux_' + name][row] = aperture_flux gauss_fit_tab.write('/lustre/aoc/students/jotter/dendro_catalogs/' + name + '_dendro_catalog_ref_' + ref_name + '.fits', format='fits', overwrite=True)
N = len(list_zen) #print N N = 1 for i in range(N): # if i%10 ==0: # print "----running observation -- ",i obs = list_zen[i] events = datastore.obs(obs.obs_id).events counts_image = SkyImage.empty_like(ref_image) counts_image.fill_events(events) exclusion_region = regions.CircleSkyRegion(src, 0.3 * u.deg) mask = counts_image.region_mask(exclusion_region) mask1 = copy.copy(mask) mask1.data = np.invert(mask.data) image_maker = SingleObsImageMaker( obs=obs, empty_image=ref_image, energy_band=energy_band, offset_band=offset_band, exclusion_mask=mask1, ) image_maker.counts_image() counts_image = image_maker.images['counts']
nsources = len(core_phot_tbl) print("Mass fraction M>8 = {0}".format(over8fraction)) print("Mean mass Mbar(M>8) = {0}".format(over8mean)) print("Mass of observed sources, assuming all are 8 msun = {0}".format( nsources * 8)) print("Total Mass estimate if all sources are 8 msun = {0}".format( nsources * 8 / over8fraction)) print("Total Mass estimate if Mbar={1} = {0}".format( nsources * over8mean / over8fraction, over8mean)) # Comparison to Schmiedeke et al, 2016 tbl 2 clusters = regions.read_ds9(paths.rpath('schmiedeke_clusters.reg')) clusters.append( regions.CircleSkyRegion(clusters[0].center, radius=1 * u.deg, meta={'text': 'Total'})) #this can be used to determine n_cores and n_hii in a bigger region, #but is incompatible with something else below... # clusters.append(regions.CircleSkyRegion(clusters[0].center, # radius=35*u.arcsec, # meta={'text':'M Bigger'}) # ) # Cluster M Bigger: N(cores)= 52 N(HII)= 49 counted mass= 2852.28 inferred mass= 11522.81 HII-only inferred mass: 16003.53 core-inferred mass= 7042.09 # add in DePree HII regions based on whether or not their names # are already in the table, since we didn't count the larger HII regions hii_regions = regions.read_ds9( paths.rpath('SgrB2_1.3cm_hiiRegions_masked_Done.reg')) hii_regions = Table.read(paths.tpath("Schmiedeke2016_HIIregions_tableB1.txt"), format='ascii.fixed_width')
def b6b7_catalog(B6_img, B6_name, B7_img, B7_name, cat_name, nonconv_B6_img=None, nonconv_B7_img=None): #creates catalog from one image in each band #B3_names, B6_names, B7_names only used for gaussian diag directory names ref_data_name = '/home/jotter/nrao/summer_research_2018/tables/ref_catalog_may21.fits' ref_data = Table.read(ref_data_name) ref_arrs = [ref_data['B6_detect'], ref_data['B7_detect']] band_imgs = [B6_img, B7_img] band_names = ['B6', 'B7'] band_img_names = [B6_name, B7_name] band_tables = [] for b in range(len(band_imgs)): #first loop through different bands name = band_names[b] img_name = band_img_names[b] img = band_imgs[b] fl = fits.open(img) header = fl[0].header img_data = fl[0].data.squeeze() img_wcs = WCS(header).celestial beam = radio_beam.Beam.from_fits_header(header) pixel_scale = np.abs(img_wcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg ppbeam = (beam.sr/(pixel_scale**2)).decompose().value if name == 'B6' and nonconv_B6_img is not None: fl = fits.open(nonconv_B6_img) header = fl[0].header nonconv_beam = radio_beam.Beam.from_fits_header(header) ppbeam = (nonconv_beam.sr/(pixel_scale**2)).decompose().value if name == 'B7' and nonconv_B7_img is not None: fl = fits.open(nonconv_B7_img) header = fl[0].header nonconv_beam = radio_beam.Beam.from_fits_header(header) ppbeam = (nonconv_beam.sr/(pixel_scale**2)).decompose().value #now get ready to fit gaussians #start by setting up save directory for images gauss_save_dir = '/home/jotter/nrao/gauss_diags_may21/'+img_name+'/' if not os.path.exists(gauss_save_dir): os.makedirs(gauss_save_dir) #now make region list rad = Angle(1, 'arcsecond') #radius used in region list regs = [] src_inds = np.where(ref_arrs[b] == True)[0] print(len(src_inds)) for ind in src_inds: reg = regions.CircleSkyRegion(center=SkyCoord(ref_data['RA_B3'][ind]*u.degree, ref_data['DEC_B3'][ind]*u.degree), radius=rad, meta={'text':str(ref_data['B3_Seq'][ind])}) reg_pix = reg.to_pixel(img_wcs) if reg_pix.center.x > 0 and reg_pix.center.x < len(img_data[0]): if reg_pix.center.y > 0 and reg_pix.center.y < len(img_data): if np.isnan(img_data[int(reg_pix.center.x), int(reg_pix.center.y)]) == False: regs.append(reg) cat_r = Angle(0.5, 'arcsecond')/2 #radius for gaussian fitting print('ok') gauss_cat = gaussfit_catalog(img, regs, cat_r, savepath=gauss_save_dir, max_offset_in_beams = 1, max_radius_in_beams = 5) #table does not have all columns yet, add others later img_table = Table(names=('Seq_B3', 'fwhm_maj_'+name, 'fwhm_maj_err_'+name, 'fwhm_min_'+name, 'fwhm_min_err_'+name, 'pa_'+name, 'pa_err_'+name, 'gauss_amp_'+name, 'gauss_amp_err_'+name,'RA_'+name,'RA_err_'+name, 'DEC_'+name, 'DEC_err_'+name, ), dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8')) for key in gauss_cat: img_table.add_row((key, gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'], gauss_cat[key]['e_pa'], gauss_cat[key]['amplitude'], gauss_cat[key]['e_amplitude'], gauss_cat[key]['center_x'], gauss_cat[key]['e_center_x'], gauss_cat[key]['center_y'], gauss_cat[key]['e_center_y'])) #now measure deconvovled sizes and aperture flux measurements for each source ap_flux_arr = [] ap_flux_err_arr = [] fwhm_maj_deconv_arr = [] fwhm_maj_deconv_err_arr = [] fwhm_min_deconv_arr = [] fwhm_min_deconv_err_arr = [] pa_deconv_arr = [] pa_deconv_err_arr = [] snr_arr = [] rms_arr = [] for row in range(len(img_table)): #now loop through sources in reference data and make measurements ref_ind = np.where(ref_data['B3_Seq'] == img_table['Seq_B3'][row])[0] if len(ref_ind > 0): #now measuring deconvolved sizes measured_source_size = radio_beam.Beam(major=img_table['fwhm_maj_'+name][row]*u.arcsec, minor=img_table['fwhm_min_'+name][row]*u.arcsec, pa=(img_table['pa_'+name][row]-90)*u.degree) try: deconv_size = measured_source_size.deconvolve(beam) fwhm_maj_deconv_arr.append(deconv_size.major.value) fwhm_min_deconv_arr.append(deconv_size.minor.value) fwhm_maj_deconv_err_arr.append(img_table['fwhm_maj_err_'+name][row]) #same error as non deconvolved fwhm_min_deconv_err_arr.append(img_table['fwhm_min_err_'+name][row]) pa_deconv_arr.append(deconv_size.pa.to(u.deg).value) pa_deconv_err_arr.append(img_table['pa_err_'+name][row]) except ValueError: fwhm_maj_deconv_arr.append(np.nan) fwhm_min_deconv_arr.append(np.nan) fwhm_maj_deconv_err_arr.append(np.nan) fwhm_min_deconv_err_arr.append(np.nan) pa_deconv_arr.append(np.nan) pa_deconv_err_arr.append(np.nan) pix_major_fwhm = ((img_table['fwhm_maj_'+name][row]*u.arcsec).to(u.degree)/pixel_scale).decompose() pix_minor_fwhm = ((img_table['fwhm_min_'+name][row]*u.arcsec).to(u.degree)/pixel_scale).decompose() center_coord = SkyCoord(img_table['RA_'+name][row], img_table['DEC_'+name][row], frame='icrs', unit=(u.deg, u.deg)) center_coord_pix = center_coord.to_pixel(img_wcs) center_coord_pix_reg = regions.PixCoord(center_coord_pix[0], center_coord_pix[1]) pos_ang = (img_table['pa_'+name][row]-90)*u.deg #must subtract 90 to be consistent ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg, pix_major_fwhm.value*2, pix_minor_fwhm.value*2, angle=pos_ang) ap_mask = ellipse_reg.to_mask() cutout_mask = ap_mask.cutout(img_data) aperture_flux = np.nansum(cutout_mask[ap_mask.data==1])/ppbeam npix = len(cutout_mask[ap_mask.data==1]) #now make annulus for measuring background and error annulus_width = 15 #pixels annulus_radius = img_table['fwhm_maj_'+name][row]*u.arcsecond#+0.05*u.arcsecond annulus_radius_pix = (annulus_radius.to(u.degree)/pixel_scale).decompose() #cutout image cutout = Cutout2D(img_data, center_coord_pix, annulus_radius*2.5, img_wcs, mode='partial') cutout_center = regions.PixCoord(cutout.center_cutout[0], cutout.center_cutout[1]) #define aperture regions for SNR innerann_reg = regions.CirclePixelRegion(cutout_center, annulus_radius_pix.value) outerann_reg = regions.CirclePixelRegion(cutout_center, annulus_radius_pix.value+annulus_width) #Make masks from aperture regions annulus_mask = mask(outerann_reg, cutout) - mask(innerann_reg, cutout) # Calculate the SNR and aperture flux sums pixels_in_annulus = cutout.data[annulus_mask.astype('bool')] bg_rms = median_abs_deviation(pixels_in_annulus) print(img_table['Seq_B3'][row]) print('BG RMS: %f' % (bg_rms)) ap_bg_rms = bg_rms/np.sqrt(npix/ppbeam) #rms/sqrt(npix/ppbeam) - rms error per beam bg_median = np.nanmedian(pixels_in_annulus) pix_bg = bg_median*npix/ppbeam ap_flux_bgcorrect = aperture_flux - pix_bg ap_flux_correct = ap_flux_bgcorrect + ap_flux_bgcorrect*(1 - special.erf(2*np.sqrt(np.log(2)))) #flux correction for summing within 2*fwhm ap_flux_err_arr.append(ap_bg_rms) ap_flux_arr.append(ap_flux_correct) snr_arr.append(img_table['gauss_amp_'+name][row]/bg_rms) rms_arr.append(bg_rms) cols = ['ap_flux_'+name, 'ap_flux_err_'+name, 'fwhm_maj_deconv_'+name, 'fwhm_maj_deconv_err_'+name, 'fwhm_min_deconv_'+name, 'fwhm_min_deconv_err_'+name, 'pa_deconv_'+name, 'pa_deconv_err_'+name, 'SNR_'+name, 'RMS_'+name] arrs = [ap_flux_arr, ap_flux_err_arr, fwhm_maj_deconv_arr, fwhm_maj_deconv_err_arr, fwhm_min_deconv_arr, fwhm_min_deconv_err_arr, pa_deconv_arr, pa_deconv_err_arr, snr_arr, rms_arr] for c in range(len(cols)): img_table.add_column(Column(np.array(arrs[c])), name=cols[c]) img_table.add_column(Column(np.array(fwhm_maj_deconv_arr)/np.array(fwhm_min_deconv_arr)), name='ar_deconv_'+name) band_tables.append(img_table) #list of tables for each image B6B7 = join(band_tables[0], band_tables[1], keys='Seq_B3', join_type='outer') B6B7.write('/home/jotter/nrao/summer_research_2018/tables/'+cat_name+'.fits', overwrite=True)
def gauss_sizes_imgs(srcID, band, images, directory, name_start=27, name_end=-21): #This function takes a list of image names and adds them all to a catalog with gaussian sizes. 'data' is the catalog to get positions from, 'srcID' is the D_ID of the desired source, 'band' is the band of data, 'images' is the list of image names, 'directory' is the directory where these images are. data = '/lustre/aoc/students/jotter/dendro_catalogs/master_500klplus_B3_ref.fits' cat = Table.read(data) ind = np.where(cat['D_ID'] == srcID)[0][0] img_names = [img[name_start:name_end] for img in images] print(img_names) table_names = [] #because maybe not all images in img_namse get used img_fwhm_maj = [] #at the end combine these lists into a table img_fwhm_maj_err = [] img_fwhm_min = [] img_fwhm_min_err = [] img_pa = [] img_pa_err = [] for j, img in enumerate(images): #loop through images and measure fwhms name = img_names[j] if len(name) > 54: name = name[0:53] fl = fits.open(directory + img) header = fl[0].header try: img_data = fl[0].data.squeeze() except TypeError: print("error! img: " + img) continue mywcs = WCS(header).celestial beam = radio_beam.Beam.from_fits_header(header) pixel_scale = np.abs( mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg ppbeam = (beam.sr / (pixel_scale**2)).decompose().value save_dir = '/lustre/aoc/students/jotter/gauss_diags/diff_imgs/src_' + str( srcID) + '/' + band + '/' + name if not os.path.exists(save_dir): os.makedirs(save_dir) rad = Angle(1, 'arcsecond') #radius for region list regs = [] reg = regions.CircleSkyRegion(center=SkyCoord( cat['RA_B3'][ind] * u.degree, cat['DEC_B3'][ind] * u.degree), radius=rad, meta={'text': str(cat['D_ID'][ind])}) reg_pix = reg.to_pixel(mywcs) if reg_pix.center.x > 0 and reg_pix.center.x < len(img_data[0]): if reg_pix.center.y > 0 and reg_pix.center.y < len(img_data): regs.append(reg) cat_r = Angle(0.5, 'arcsecond') #radius in gaussian fitting gauss_cat = gaussfit_catalog( directory + img, regs, cat_r, savepath=save_dir) #output is nested dictionary structure table_names.append(name) img_fwhm_maj.append(gauss_cat[str(srcID)]['fwhm_major'].value) img_fwhm_maj_err.append(gauss_cat[str(srcID)]['e_fwhm_major'].value) img_fwhm_min.append(gauss_cat[str(srcID)]['fwhm_minor'].value) img_fwhm_min_err.append(gauss_cat[str(srcID)]['e_fwhm_minor'].value) img_pa.append(gauss_cat[str(srcID)]['pa'].value) img_pa_err.append(gauss_cat[str(srcID)]['e_pa'].value) table = Table((table_names, img_fwhm_maj, img_fwhm_maj_err, img_fwhm_min, img_fwhm_min_err, img_pa, img_pa_err), names=('img_name', 'fwhm_maj', 'fwhm_maj_err', 'fwhm_min', 'fwhm_min_err', 'pa', 'pa_err')) table['fwhm_maj'].unit = 'arcsec' table['fwhm_maj_err'].unit = 'arcsec' table['fwhm_min'].unit = 'arcsec' table['fwhm_min_err'].unit = 'arcsec' table.write('/lustre/aoc/students/jotter/dendro_catalogs/src' + str(srcID) + '_img_sizes' + band + '.fits', overwrite=True)
tbl.rename_column('robs', 'tmp') tbl.rename_column('ne', 'robs') tbl.rename_column('tmp', 'ne') tbl['ne'].unit = 1e4 * u.cm**-3 tbl['robs'].unit = 1e3 * u.au tbl.write(paths.tpath("Schmiedeke2016_HIIregions_tableB1.txt"), format='ascii.fixed_width', overwrite=True) reglist = [ regions.CircleSkyRegion( coordinates.SkyCoord(row['RA'], row['Dec'], frame='fk5', unit=(u.hour, u.deg)), radius=(u.Quantity(float(row['robs']) * 1000, u.au) / (8.5 * u.kpc)).to(u.arcsec, u.dimensionless_angles()), meta={'text': row['ID']}, visual={'name': row['ID']}, ) for row in tbl if row['ID'] ] regions.write_ds9(reglist, paths.rpath('Schmiedeke2016_HIIregions_tableB1.reg')) rslt = requests.get( 'http://www.aanda.org/articles/aa/full_html/2016/04/aa27311-15/T2.html') soup = BeautifulSoup(rslt.text, 'html5lib') htmltable = soup.findAll('table')[-1] allrows = (htmltable.findAll('tr'))
def fit_source(srcID, img, img_name, band, fit_bg=False, bg_stddev_x=30, bg_stddev_y=30, bg_mean_x=0, bg_mean_y=0, zoom=1, max_offset_in_beams=1, max_radius_in_beams=5, nonconv_img=None, mask_size=1.5): #this function fits a given source, and the background #srcID : int #name of source to fit in catalogs #img : fits file #fits file with source to fit #img_name : str #name of image for the directory where the fit plots will go #band : str #band of image to fit ('B3', 'B6', or 'B7') #fit_bg : bool #if False, do not fit background gaussian #bg_stddev_x : float #eyeballed estimate of stddev of the background source in pixels #bg_stddev_y : float #same as above in y direction #bg_mean_x/y : float #pixels away from center (origin) in x/y direction for background gaussian mean guess #zoom : float #amount of zoom, values greater than 1 are zoom ins #ref_data_name = '/home/jotter/nrao/summer_research_2018/tables/dendro_ref_catalog_edited.fits' ref_data_name = '/home/jotter/nrao/summer_research_2018/tables/ref_catalog_may21_b7.fits' #ref_data_name = '/lustre/cv/observers/cv-12578/orion_disks/summer_research_2018/tables/ref_catalog_may21.fits' ref_data = Table.read(ref_data_name) fl = fits.open(img) header = fl[0].header img_data = fl[0].data.squeeze() img_wcs = WCS(header).celestial beam = radio_beam.Beam.from_fits_header(header) pixel_scale = np.abs( img_wcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg ppbeam = (beam.sr / (pixel_scale**2)).decompose().value if nonconv_img is not None: flnonconv = fits.open(nonconv_img) nonconv_header = flnonconv[0].header nonconv_beam = radio_beam.Beam.from_fits_header(nonconv_header) ppbeam = (nonconv_beam.sr / (pixel_scale**2)).decompose().value #now get ready to fit gaussians #start by setting up save directory for images gauss_save_dir = '/home/jotter/nrao/gauss_diags_may21/fitbg/' + img_name + '/' #gauss_save_dir = f'/lustre/cv/observers/cv-12578/orion_disks/gauss_diags_may21/{img_name}/' print('saving plots to ' + gauss_save_dir) if not os.path.exists(gauss_save_dir): os.makedirs(gauss_save_dir) #now make region rad = Angle(1, 'arcsecond') #radius used in region list #src_ind = np.where(ref_data['D_ID']==srcID)[0] src_ind = np.where(ref_data['B3_Seq'] == srcID)[0] ra = ref_data['RA_B3'][src_ind].data[0] dec = ref_data['DEC_B3'][src_ind].data[0] center_reg = SkyCoord(ra, dec, unit='deg', frame='icrs') reg = regions.CircleSkyRegion( center=center_reg, radius=1 * u.arcsecond, meta={ 'text': str(ref_data['B3_Seq'][src_ind].data[0]) + '_xstddev_' + str(bg_stddev_x) + '_ystddev_' + str(bg_stddev_y) }) region_list = [] #valid_inds = np.where(np.isnan(ref_data[band+'_detect']) == False)[0] for ind in range(len(ref_data)): #valid_inds: if ref_data['B3_Seq'][ind] == srcID: continue ra_i = ref_data['RA_B3'][ind] dec_i = ref_data['DEC_B3'][ind] region_i = regions.CircleSkyRegion(center=SkyCoord(ra_i, dec_i, unit='deg', frame='icrs'), radius=1 * u.arcsecond) region_list.append(region_i) #print(region_list) #print(reg) cat_r = Angle(0.5, 'arcsecond') / zoom #radius for gaussian fitting if fit_bg == True: gauss_cat, fitim_bg = bg_gaussfit( img, reg, region_list, cat_r, bg_stddev_x=bg_stddev_x, bg_stddev_y=bg_stddev_y, bg_mean_x=bg_mean_x, bg_mean_y=bg_mean_y, savepath=gauss_save_dir, max_offset_in_beams=max_offset_in_beams, max_offset_in_beams_bg=10, max_radius_in_beams=max_radius_in_beams, mask_size=mask_size) #print('gauss_cat length ',len(gauss_cat)) #k = list(gauss_cat.keys())[0] #if gauss_cat[k]['success'] == False: # gauss_cat = gaussfit_cutoutim(img, fitim_bg, reg, region_list, cat_r, savepath=gauss_save_dir, max_offset_in_beams = max_offset_in_beams, max_radius_in_beams = max_radius_in_beams) # success = gauss_cat[k]['success'] # print(F'ALTERNATIVE FIT SUCCESS: {success}') else: gauss_cat = gaussfit_catalog(img, [reg], cat_r, savepath=gauss_save_dir, max_offset_in_beams=max_offset_in_beams, max_radius_in_beams=max_radius_in_beams) img_table = Table(names=('Seq', 'fwhm_maj_' + band, 'fwhm_maj_err_' + band, 'fwhm_min_' + band, 'fwhm_min_err_' + band, 'pa_' + band, 'pa_err_' + band, 'gauss_amp_' + band, 'gauss_amp_err_' + band, 'RA_' + band, 'RA_err_' + band, 'DEC_' + band, 'DEC_err_' + band), dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8')) for key in gauss_cat: img_table.add_row( (srcID, gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'], gauss_cat[key]['e_pa'], gauss_cat[key]['amplitude'], gauss_cat[key]['e_amplitude'], gauss_cat[key]['center_x'], gauss_cat[key]['e_center_x'], gauss_cat[key]['center_y'], gauss_cat[key]['e_center_y'])) #now measure deconvovled sizes and aperture flux measurements for each source ap_flux_arr = [] ap_flux_err_arr = [] fwhm_maj_deconv_arr = [] fwhm_maj_deconv_err_arr = [] fwhm_min_deconv_arr = [] fwhm_min_deconv_err_arr = [] pa_deconv_arr = [] pa_deconv_err_arr = [] snr_arr = [] for row in range( len(img_table) ): #now loop through sources in reference data and make measurements ref_ind = np.where(ref_data['B3_Seq'] == img_table['Seq'][row])[0] if True == True: #len(ref_ind > 0): measured_source_size = radio_beam.Beam( major=img_table['fwhm_maj_' + band][row] * u.arcsec, minor=img_table['fwhm_min_' + band][row] * u.arcsec, pa=(img_table['pa_' + band][row] - 90) * u.deg) try: deconv_size = measured_source_size.deconvolve(beam) fwhm_maj_deconv_arr.append(deconv_size.major.value) fwhm_min_deconv_arr.append(deconv_size.minor.value) fwhm_maj_deconv_err_arr.append(img_table['fwhm_maj_err_' + band][row]) fwhm_min_deconv_err_arr.append(img_table['fwhm_min_err_' + band][row]) pa_deconv_arr.append(deconv_size.pa.value) pa_deconv_err_arr.append(img_table['pa_err_' + band][row]) except ValueError: fwhm_maj_deconv_arr.append(np.nan) fwhm_min_deconv_arr.append(np.nan) fwhm_maj_deconv_err_arr.append(np.nan) fwhm_min_deconv_err_arr.append(np.nan) pa_deconv_arr.append(np.nan) pa_deconv_err_arr.append(np.nan) pix_major_fwhm = ( (img_table['fwhm_maj_' + band][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() pix_minor_fwhm = ( (img_table['fwhm_min_' + band][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() center_coord = SkyCoord(img_table['RA_' + band][row], img_table['DEC_' + band][row], frame='icrs', unit=(u.deg, u.deg)) center_coord_pix = center_coord.to_pixel(img_wcs) center_coord_pix_reg = regions.PixCoord(center_coord_pix[0], center_coord_pix[1]) pos_ang = (img_table['pa_' + band][row] - 90) * u.deg ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg, pix_major_fwhm.value * 2, pix_minor_fwhm.value * 2, angle=pos_ang) size = pix_major_fwhm * 2.1 ap_mask = ellipse_reg.to_mask() cutout_mask = ap_mask.cutout(img_data) aperture_flux = np.nansum(cutout_mask[ap_mask.data == 1]) / ppbeam npix = len(cutout_mask[ap_mask.data == 1]) #now make annulus for measuring background and error annulus_width = 15 #pixels annulus_radius = img_table[ 'fwhm_maj_' + band][row] * u.arcsecond #0.1*u.arcsecond annulus_radius_pix = (annulus_radius.to(u.degree) / pixel_scale).decompose() #cutout image cutout = Cutout2D(img_data, center_coord_pix, annulus_radius * 2.5, img_wcs, mode='partial') cutout_center = regions.PixCoord(cutout.center_cutout[0], cutout.center_cutout[1]) #define aperture regions for SNR innerann_reg = regions.CirclePixelRegion(cutout_center, annulus_radius_pix.value) outerann_reg = regions.CirclePixelRegion( cutout_center, annulus_radius_pix.value + annulus_width) #Make masks from aperture regions annulus_mask = mask(outerann_reg, cutout) - mask( innerann_reg, cutout) # Calculate the SNR and aperture flux sums pixels_in_annulus = cutout.data[annulus_mask.astype( 'bool')] #pixels within annulus bg_rms = median_abs_deviation(pixels_in_annulus) ap_bg_rms = bg_rms / np.sqrt( npix / ppbeam) #rms/sqrt(npix/ppbeam) - rms error per beam bg_median = np.median(pixels_in_annulus) pix_bg = bg_median * npix / ppbeam #flux corrections ap_flux_bgcorrect = aperture_flux - pix_bg ap_flux_correct = ap_flux_bgcorrect + ap_flux_bgcorrect * ( 1 - special.erf(2 * np.sqrt(np.log(2))) ) #flux correction for summing within 2*fwhm print( f'Background: {pix_bg}, Gauss amp: {img_table["gauss_amp_"+band][row]}' ) print(f'peak pixel: {np.nanmax(cutout_mask[ap_mask.data==1])}') ap_flux_err_arr.append(ap_bg_rms) ap_flux_arr.append(ap_flux_correct) snr_arr.append(img_table['gauss_amp_' + band][row] / bg_rms) cols = [ 'ap_flux_' + band, 'ap_flux_err_' + band, 'fwhm_maj_deconv_' + band, 'fwhm_maj_deconv_err_' + band, 'fwhm_min_deconv_' + band, 'fwhm_min_deconv_err_' + band, 'pa_deconv_' + band, 'pa_deconv_err_' + band, 'SNR_' + band ] arrs = [ ap_flux_arr, ap_flux_err_arr, fwhm_maj_deconv_arr, fwhm_maj_deconv_err_arr, fwhm_min_deconv_arr, fwhm_min_deconv_err_arr, pa_deconv_arr, pa_deconv_err_arr, snr_arr ] for c in range(len(cols)): img_table.add_column(Column(np.array(arrs[c])), name=cols[c]) img_table.add_column(Column(img_table['fwhm_maj_deconv_' + band] / img_table['fwhm_min_deconv_' + band]), name='ar_deconv_' + band) return img_table
objects = {'north': {'center': coordinates.SkyCoord('19:23:40.054', '14:31:05.498', unit=(u.hour, u.deg), frame='fk5'), 'radius': 0.15*u.arcsec, 'filename': 'W51n_cont_uniform.image.tt0.pbcor.fits'}, 'd2': {'center': coordinates.SkyCoord('19:23:39.820', '14:31:04.866', unit=(u.hour, u.deg), frame='fk5'), 'radius': 0.10*u.arcsec, 'filename': 'W51n_cont_uniform.image.tt0.pbcor.fits'}, 'e2e': {'center': coordinates.SkyCoord('19:23:43.969', '14:30:34.525', unit=(u.hour, u.deg), frame='fk5'), 'radius': 0.25*u.arcsec, 'filename': 'W51e2_cont_uniform.image.tt0.pbcor.fits'}, 'e8': {'center': coordinates.SkyCoord('19:23:43.906', '14:30:28.269', unit=(u.hour, u.deg), frame='fk5'), 'radius': 0.16*u.arcsec, 'filename': 'W51e2_cont_uniform.image.tt0.pbcor.fits'}, } for key in objects: obj = objects[key] objects[key]['aperture'] = regions.CircleSkyRegion(center=obj['center'], radius=obj['radius']) for objname in objects: obj = objects[objname] fh = fits.open(paths.lbpath(obj['filename']))[0] datawcs = wcs.WCS(fh.header) data = fh.data beam = radio_beam.Beam.from_fits_header(fh.header) pixreg = obj['aperture'].to_pixel(datawcs) mask = pixreg.to_mask() cutout = mask.cutout(data) * mask.data pkflux = cutout.max() * u.Unit(fh.header['BUNIT']) #pkbright = pkflux.to(u.K, beam.jtok_equiv(fh.header['CRVAL3']*u.Unit(fh.header['CUNIT3']))) pkbright = pkflux.to(u.K, beam.jtok_equiv(225*u.GHz))
def gaussfit_catalog( fitsfile, region_list, radius=1.0 * u.arcsec, max_radius_in_beams=2, max_offset_in_beams=1, background_estimator=np.nanmedian, noise_estimator=lambda x: mad_std(x, ignore_nan=True), savepath=None, prefix="", covariance='param_cov', raise_for_failure=False, ): """ Given a FITS filename and a list of regions, fit a gaussian to each region with an input guess based on the beam size. Parameters ---------- fitsfile : str Name of the FITS file region_list : list List of regions (see https://github.com/astropy/regions/) radius : angular size The radius of the region around the region center to extract and include in the fit max_radius_in_beams : float The maximum allowed source radius in units of beam major axis (this is a limit passed to the fitter) max_offset_in_beams : float The maximum allowed offset of the source center from the guessed position background_estimator : function A function to apply to the background pixels (those not within 1 beam HWHM of the center) to estimate the background level. The background will be subtracted before fitting. noise_estimator : function Function to apply to the whole data set to determine the noise level and therefore the appropriate per-pixel weight to get the correct normalization for the covariance matrix. savepath : str or None If specified, plots will be made and saved to this directory using the source name from the region metadata prefix : str The prefix to append to saved source names covariance : 'param_cov' or 'cov_x' Which covariance matrix should be used to estimate the parameter errors? ``param_cov`` uses the diagonal of the reduced-chi^2-scaled covariance matrix to compute the parameter errors, while ``cov_x`` uses the unscaled errors. See http://arxiv.org/abs/1009.2755 for a description, and criticism, of using the scaled covariance. raise_for_failure : bool If the fit was not successful, raise an exception """ # need central coordinates of each object coords = coordinates.SkyCoord([reg.center for reg in region_list]) fh = fits.open(fitsfile) data = fh[0].data.squeeze() header = fh[0].header datawcs = wcs.WCS(header).celestial beam = Beam.from_fits_header(header) pixscale = wcs.utils.proj_plane_pixel_area(datawcs)**0.5 * u.deg bmmin_px = (beam.minor.to(u.deg) / pixscale).decompose() bmmaj_px = (beam.major.to(u.deg) / pixscale).decompose() noise = noise_estimator(data) log.info("Noise estimate is {0} for file {1}".format(noise, fitsfile)) fit_data = {} pb = ProgressBar(len(region_list)) for ii, reg in enumerate(region_list): phot_reg = regions.CircleSkyRegion(center=reg.center, radius=radius) pixreg = phot_reg.to_pixel(datawcs) mask = pixreg.to_mask() mask_cutout = mask.cutout(data) if mask_cutout is None: log.warning( "Skipping region {0} because it failed to produce a cutout.". format(reg)) continue cutout = mask_cutout * mask.data cutout_mask = mask.data.astype('bool') smaller_phot_reg = regions.CircleSkyRegion(center=reg.center, radius=beam.major / 2.) #FWHM->HWHM smaller_pixreg = smaller_phot_reg.to_pixel(datawcs) smaller_mask = smaller_pixreg.to_mask() smaller_cutout = smaller_mask.cutout(data) * smaller_mask.data # mask out (as zeros) neighboring sources within the fitting area nearby_matches = phot_reg.contains(coords, datawcs) if any(nearby_matches): inds = np.where(nearby_matches)[0].tolist() inds.remove(ii) for ind in inds: maskoutreg = regions.EllipseSkyRegion( center=region_list[ind].center, width=beam.major, height=beam.minor, angle=beam.pa + 90 * u.deg, ) mpixreg = maskoutreg.to_pixel(datawcs) mmask = mpixreg.to_mask() view, mview = slice_bbox_from_bbox(mask.bbox, mmask.bbox) cutout_mask[view] &= ~mmask.data.astype('bool')[mview] cutout = cutout * cutout_mask background_mask = cutout_mask.copy().astype('bool') background_mask[sub_bbox_slice( mask.bbox, smaller_mask.bbox)] &= ~smaller_mask.data.astype('bool') background = background_estimator(cutout[background_mask]) sz = cutout.shape[0] mx = np.nanmax(smaller_cutout) ampguess = mx - background p_init = models.Gaussian2D( amplitude=ampguess, x_mean=sz / 2, y_mean=sz / 2, x_stddev=bmmaj_px / STDDEV_TO_FWHM, y_stddev=bmmin_px / STDDEV_TO_FWHM, theta=beam.pa, bounds={ 'x_stddev': (bmmin_px / STDDEV_TO_FWHM * 0.75, bmmaj_px * max_radius_in_beams / STDDEV_TO_FWHM), 'y_stddev': (bmmin_px / STDDEV_TO_FWHM * 0.75, bmmaj_px * max_radius_in_beams / STDDEV_TO_FWHM), 'x_mean': (sz / 2 - max_offset_in_beams * bmmaj_px / STDDEV_TO_FWHM, sz / 2 + max_offset_in_beams * bmmaj_px / STDDEV_TO_FWHM), 'y_mean': (sz / 2 - max_offset_in_beams * bmmaj_px / STDDEV_TO_FWHM, sz / 2 + max_offset_in_beams * bmmaj_px / STDDEV_TO_FWHM), 'amplitude': (ampguess * 0.9, ampguess * 1.1), 'theta': (0, 2 * np.pi), }) imtofit = np.nan_to_num((cutout - background) * mask.data) result, fit_info, chi2, fitter = gaussfit_image( image=imtofit, gaussian=p_init, weights=1 / noise**2, plot=savepath is not None, ) if 'text' in reg.meta: sourcename = reg.meta['text'].strip('{}') elif 'label' in reg.meta: sourcename = reg.meta['label'].strip('{}') else: raise ValueError("Regions need to have names, either as 'text' or " "'label' entries.") if savepath is not None: with warnings.catch_warnings(): warnings.simplefilter('ignore', UserWarning) bmarr = beam.as_kernel(pixscale=pixscale, x_size=sz, y_size=sz).array assert bmarr.max() > 0 bm_ellipse = beam.ellipse_to_plot(sz / 2, sz / 2., pixscale) bm_ellipse.set_facecolor('none') bm_ellipse.set_edgecolor('r') pl.gca().add_patch(bm_ellipse) #pl.contour(bmarr, levels=[0.317*bmarr.max()], colors=['r']) pl.savefig(os.path.join(savepath, '{0}{1}.png'.format(prefix, sourcename)), bbox_inches='tight') if covariance not in fit_info or fit_info[covariance] is None: fit_info[covariance] = np.zeros([6, 6]) success = False else: success = True cx, cy = pixreg.bounding_box.ixmin + result.x_mean, pixreg.bounding_box.iymin + result.y_mean clon, clat = datawcs.wcs_pix2world(cx, cy, 0) major, minor = (result.x_stddev * STDDEV_TO_FWHM * pixscale.to(u.arcsec), result.y_stddev * STDDEV_TO_FWHM * pixscale.to(u.arcsec)) majind, minind = 3, 4 pa = (result.theta * u.rad).to(u.deg) if minor > major: major, minor = minor, major majind, minind = minind, majind pa += 90 * u.deg if pa > 360 * u.deg: pa -= 360 * u.deg fitted_gaussian_as_beam = Beam(major=major, minor=minor, pa=pa) try: deconv_fit = fitted_gaussian_as_beam.deconvolve(beam) deconv_major, deconv_minor, deconv_pa = (deconv_fit.major, deconv_fit.minor, deconv_fit.pa) except ValueError: print("Could not deconvolve {0} from {1}".format( beam.__repr__(), fitted_gaussian_as_beam.__repr__())) deconv_major, deconv_minor, deconv_pa = np.nan, np.nan, np.nan if pa < -360 * u.deg or pa > 360 * u.deg: raise ValueError("PA is set incorrectly.") if deconv_pa < -360 * u.deg or deconv_pa > 360 * u.deg: raise ValueError("Deconvolved PA is set incorrectly.") fit_data[sourcename] = { 'amplitude': result.amplitude, 'center_x': float(clon) * u.deg, 'center_y': float(clat) * u.deg, 'fwhm_major': major, 'fwhm_minor': minor, 'pa': pa, 'deconv_fwhm_major': deconv_major, 'deconv_fwhm_minor': deconv_minor, 'deconv_pa': deconv_pa, 'chi2': chi2, 'chi2/n': chi2 / mask.data.sum(), 'e_amplitude': fit_info[covariance][0, 0]**0.5, 'e_center_x': fit_info[covariance][1, 1]**0.5 * pixscale, 'e_center_y': fit_info[covariance][2, 2]**0.5 * pixscale, 'e_fwhm_major': fit_info[covariance][majind, majind]**0.5 * STDDEV_TO_FWHM * pixscale.to(u.arcsec), 'e_fwhm_minor': fit_info[covariance][minind, minind]**0.5 * STDDEV_TO_FWHM * pixscale.to(u.arcsec), 'e_pa': fit_info[covariance][5, 5]**0.5 * u.deg, 'success': success, 'ampguess': ampguess, 'peak': mx, 'fit_info': fit_info, } if raise_for_failure and not success: raise ValueError("Fit failed.") pb.update(ii) signal.signal(signal.SIGINT, signal_handler) return fit_data
def create_catalog(param_file): params = ascii.read(param_file) for row in range(len(params)): name = params['name'][ row] #contains parameters for dendrogram catalog creation min_val = params['min_value'][row] min_del = params['min_delta'][row] n_pix = params['n_pix'][row] img = params['file_name'][row] reg_fname = '/home/jotter/nrao/images/dendro_regions/' + name + '_reg_file.reg' dendro, cat = compute_regions(min_val, min_del, n_pix, img, reg_fname) cat['flux_err_' + name] = np.zeros(len(cat)) if params['pbcorr'][ row] != 'True': #if the image is not pb corrected, measure flux from pb corrected image img = params['pbcorr'][row] cont_file = fits.open(img) header = cont_file[0].header mywcs = WCS(header).celestial data = cont_file[0].data.squeeze() beam = radio_beam.Beam.from_fits_header(header) pixel_scale = np.abs( mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg ppbeam = (beam.sr / (pixel_scale**2)).decompose().value cat.rename_column('_idx', '_idx_' + name) #next, measure centroids with gaussian fitting rad = Angle(1, 'arcsecond') #radius for region list regs = [] for ind, leaf in enumerate( dendro.all_structures): #.leaves #generate region list regs.append( regions.CircleSkyRegion( center=SkyCoord(cat['x_cen'][ind] * u.degree, cat['y_cen'][ind] * u.degree), radius=rad, meta={'text': str(cat['_idx_' + name][ind])})) print(len(regs)) #if name == 'B6': #regs.append(regions.CircleSkyRegion(center=SkyCoord(83.81138026377482*u.degree, -5.374951161716349*u.degree), radius=rad, meta={'text':'54'}))#manually appending a source not picked up by dendrogram cat_r = Angle(0.3, 'arcsecond') #radius in gaussian fitting gauss_cat = gaussfit_catalog( img, regs, cat_r, savepath='/home/jotter/nrao/gauss_diags/leaves/' + name) #output is nested dictionary structure gauss_fit_tab = Table( names=('_idx_' + name, 'gauss_x_' + name, 'x_err_' + name, 'gauss_y_' + name, 'y_err_' + name, 'FWHM_major_' + name, 'major_err_' + name, 'FWHM_minor_' + name, 'minor_err_' + name, 'position_angle_' + name, 'position_angle_err_' + name, 'peak_flux_' + name, 'gauss_amplitude_' + name, 'amplitude_err_' + name, 'ap_flux_' + name, 'ap_flux_err_' + name, 'fit_goodness_' + name), dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'U10')) #turn into astropy table for key in gauss_cat: gauss_fit_tab.add_row( (key, gauss_cat[key]['center_x'], gauss_cat[key]['e_center_x'], gauss_cat[key]['center_y'], gauss_cat[key]['e_center_y'], gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'], gauss_cat[key]['e_pa'], gauss_cat[key]['peak'], gauss_cat[key]['amplitude'], gauss_cat[key]['e_amplitude'], np.nan, np.nan, 'none')) #fill table for row in range(len(gauss_fit_tab)): pix_major_fwhm = ((gauss_fit_tab['FWHM_major_' + name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() pix_minor_fwhm = ((gauss_fit_tab['FWHM_minor_' + name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() cutout_center = SkyCoord(gauss_fit_tab['gauss_x_' + name][row], gauss_fit_tab['gauss_y_' + name][row], frame='icrs', unit=(u.deg, u.deg)) cutout_center_pix = cutout_center.to_pixel(mywcs) cutout_center_pix = regions.PixCoord(cutout_center_pix[0], cutout_center_pix[1]) position_angle = gauss_fit_tab['position_angle_' + name][row] * u.deg ellipse_reg = regions.EllipsePixelRegion(cutout_center_pix, pix_major_fwhm * 2., pix_minor_fwhm * 2., angle=position_angle) size = pix_major_fwhm * 2.1 ap_mask = ellipse_reg.to_mask() cutout_mask = ap_mask.cutout(data) aperture_flux = np.sum(cutout_mask[ap_mask.data == 1]) / ppbeam gauss_fit_tab['ap_flux_' + name][row] = aperture_flux #NOTE: aperture flux error and background fluxes will get filled in after running `snr_rejection.py` full_cat = join(cat, gauss_fit_tab, keys='_idx_' + name, join_type='outer' ) #joining the gaussian centroid data with the rest full_cat.write('/home/jotter/nrao/tables/dendro_catalogs/' + name + '_dendro_catalog_leaves.fits', format='fits', overwrite=True)
def bg_gaussfit( fitsfile, region, region_list, radius=1.0 * u.arcsec, max_radius_in_beams=2, max_offset_in_beams=1, max_offset_in_beams_bg=10, bg_stddev_x=40, bg_stddev_y=40, bg_mean_x=0, bg_mean_y=0, mask_size=1.5, background_estimator=np.nanmedian, noise_estimator=lambda x: mad_std(x, ignore_nan=True), savepath=None, prefix="", covariance='param_cov', raise_for_failure=False, ): """ Given a FITS filename and a region, fit a gaussian to the region with an input guess based on the beam size, and fit a gaussian to the background. Parameters ---------- fitsfile : str Name of the FITS file region : region object single region from regions (see https://github.com/astropy/regions/) region_list : list of regions list of all regions - for masking out nearby sources radius : angular size The radius of the region around the region center to extract and include in the fit max_radius_in_beams : float The maximum allowed source radius in units of beam major axis (this is a limit passed to the fitter) max_offset_in_beams : float The maximum allowed offset of the source center from the guessed position max_offset_in_beams_bg : float same as above but for background gaussian bg_stddev_x : float Guess for standard deviation in x direction for background gaussian bg_stddev_y : float Same as above, in y direction bg_mean_x/y : float pixels away for background gaussian mean guess, with origin at center mask_size : float size in beams of mask to be applied to nearby sources background_estimator : function A function to apply to the background pixels (those not within 1 beam HWHM of the center) to estimate the background level. The background will be subtracted before fitting. noise_estimator : function Function to apply to the whole data set to determine the noise level and therefore the appropriate per-pixel weight to get the correct normalization for the covariance matrix. savepath : str or None If specified, plots will be made and saved to this directory using the source name from the region metadata prefix : str The prefix to append to saved source names covariance : 'param_cov' or 'cov_x' Which covariance matrix should be used to estimate the parameter errors? ``param_cov`` uses the diagonal of the reduced-chi^2-scaled covariance matrix to compute the parameter errors, while ``cov_x`` uses the unscaled errors. See http://arxiv.org/abs/1009.2755 for a description, and criticism, of using the scaled covariance. raise_for_failure : bool If the fit was not successful, raise an exception """ # need central coordinates of each object coords = coordinates.SkyCoord([reg.center for reg in region_list]) fh = fits.open(fitsfile) data = fh[0].data.squeeze() header = fh[0].header datawcs = wcs.WCS(header).celestial beam = Beam.from_fits_header(header) pixscale = wcs.utils.proj_plane_pixel_area(datawcs)**0.5 * u.deg bmmin_px = (beam.minor.to(u.deg) / pixscale).decompose() bmmaj_px = (beam.major.to(u.deg) / pixscale).decompose() noise = noise_estimator(data) log.info("Noise estimate is {0} for file {1}".format(noise, fitsfile)) fit_data = {} phot_reg = regions.CircleSkyRegion(center=region.center, radius=radius) pixreg = phot_reg.to_pixel(datawcs) mask = pixreg.to_mask() mask_cutout = mask.cutout(data) if mask_cutout is None: log.warning("The region failed to produce a cutout.".format(reg)) return null cutout = mask_cutout * mask.data cutout_mask = mask.data.astype('bool') smaller_phot_reg = regions.CircleSkyRegion(center=region.center, radius=beam.major / 2.) #FWHM->HWHM smaller_pixreg = smaller_phot_reg.to_pixel(datawcs) smaller_mask = smaller_pixreg.to_mask() smaller_cutout = smaller_mask.cutout(data) * smaller_mask.data # mask out (as zeros) neighboring sources within the fitting area srcind = None for ii, reg in enumerate(region_list): if reg.center.ra == region.center.ra and reg.center.dec == region.center.dec: srcind = ii print(srcind) #print(region_list) #print(region) nearby_matches = phot_reg.contains(coords, datawcs) if any(nearby_matches): inds = np.where(nearby_matches)[0].tolist() print(inds) if srcind in inds: inds.remove(srcind) for ind in inds: maskoutreg = regions.EllipseSkyRegion( center=region_list[ind].center, width=mask_size * beam.major, height=mask_size * beam.minor, angle=beam.pa + 90 * u.deg, ) mpixreg = maskoutreg.to_pixel(datawcs) mmask = mpixreg.to_mask() view, mview = slice_bbox_from_bbox(mask.bbox, mmask.bbox) cutout_mask[view] &= ~mmask.data.astype('bool')[mview] cutout = cutout * cutout_mask background_mask = cutout_mask.copy().astype('bool') background_mask[sub_bbox_slice( mask.bbox, smaller_mask.bbox)] &= ~smaller_mask.data.astype('bool') background = background_estimator(cutout[background_mask]) sz = cutout.shape[0] mx = np.nanmax(smaller_cutout) ampguess = mx - background imtofit = np.nan_to_num((cutout - background) * mask.data) src_gauss = [ ampguess, sz / 2, bmmaj_px.value, bmmin_px.value, beam.pa.value ] bg_gauss = [ background, bg_mean_x + sz / 2, bg_mean_y + sz / 2, bg_stddev_x, bg_stddev_y, beam.pa.value ] bnds = [max_radius_in_beams, max_offset_in_beams, max_offset_in_beams_bg] result, fit_info, chi2, fitter, img_bgsub = gaussfit_image( image=imtofit, gauss_params=src_gauss, bg_gauss_params=bg_gauss, bound_params=bnds, weights=1 / noise**2, plot=savepath is not None, ) sourcename = region.meta['text'].strip('{}') if savepath is not None: with warnings.catch_warnings(): warnings.simplefilter('ignore', UserWarning) bmarr = beam.as_kernel(pixscale=pixscale, x_size=sz, y_size=sz).array assert bmarr.max() > 0 bm_ellipse = beam.ellipse_to_plot(sz / 2, sz / 2., pixscale) bm_ellipse.set_facecolor('none') bm_ellipse.set_edgecolor('r') pl.gca().add_patch(bm_ellipse) #pl.contour(bmarr, levels=[0.317*bmarr.max()], colors=['r']) pl.savefig(os.path.join(savepath, '{0}{1}.png'.format(prefix, sourcename)), bbox_inches='tight') if covariance not in fit_info or fit_info[covariance] is None: fit_info[covariance] = np.zeros([6, 6]) success = False else: success = True cx, cy = pixreg.bounding_box.ixmin + result.x_mean_0, pixreg.bounding_box.iymin + result.y_mean_0 clon, clat = datawcs.wcs_pix2world(cx, cy, 0) major, minor = (result.x_stddev_0 * STDDEV_TO_FWHM * pixscale.to(u.arcsec), result.y_stddev_0 * STDDEV_TO_FWHM * pixscale.to(u.arcsec)) majind, minind = 3, 4 pa = (result.theta_0 * u.rad).to(u.deg) if minor > major: major, minor = minor, major majind, minind = minind, majind pa += 90 * u.deg fitted_gaussian_as_beam = Beam(major=major, minor=minor, pa=pa) try: deconv_fit = fitted_gaussian_as_beam.deconvolve(beam) deconv_major, deconv_minor, deconv_pa = (deconv_fit.major, deconv_fit.minor, deconv_fit.pa) deconv_maj_err = fit_info[covariance][ majind, majind]**0.5 * STDDEV_TO_FWHM * pixscale.to(u.arcsec) deconv_min_err = fit_info[covariance][ minind, minind]**0.5 * STDDEV_TO_FWHM * pixscale.to(u.arcsec) deconv_pa_err = fit_info[covariance][5, 5]**0.5 * u.deg except ValueError: print("Could not deconvolve {0} from {1}".format( beam.__repr__(), fitted_gaussian_as_beam.__repr__())) deconv_major, deconv_minor, deconv_pa = np.nan, np.nan, np.nan deconv_maj_err, deconv_min_err, deconv_pa_err = np.nan, np.nan, np.nan fit_data[sourcename] = { 'amplitude': result.amplitude_0, 'center_x': float(clon) * u.deg, 'center_y': float(clat) * u.deg, 'fwhm_major': major, 'fwhm_minor': minor, 'pa': pa, 'deconv_fwhm_major': deconv_major, 'e_deconv_fwhm_major': deconv_maj_err, 'deconv_fwhm_minor': deconv_minor, 'e_deconv_fwhm_minor': deconv_min_err, 'deconv_pa': deconv_pa, 'e_deconv_pa': deconv_pa_err, 'chi2': chi2, 'chi2/n': chi2 / mask.data.sum(), 'e_amplitude': fit_info[covariance][0, 0]**0.5, 'e_center_x': fit_info[covariance][1, 1]**0.5 * pixscale, 'e_center_y': fit_info[covariance][2, 2]**0.5 * pixscale, 'e_fwhm_major': fit_info[covariance][majind, majind]**0.5 * STDDEV_TO_FWHM * pixscale.to(u.arcsec), 'e_fwhm_minor': fit_info[covariance][minind, minind]**0.5 * STDDEV_TO_FWHM * pixscale.to(u.arcsec), 'e_pa': fit_info[covariance][5, 5]**0.5 * u.deg, 'success': success, 'ampguess': ampguess, 'peak': mx, 'fit_info': fit_info, } if raise_for_failure and not success: raise ValueError("Fit failed.") signal.signal(signal.SIGINT, signal_handler) return fit_data, img_bgsub
def measure_fluxes_gaussfit(data, directory, data_name, name_start=27, name_end=-21, ref_name='B6'): #This function takes a directory full of images and adds them all to a catalog with flux measurements and fits gaussians. 'data' is the catalog to get positions from, 'ref_name' is the name of the reference data (for positions), 'directory' is the directory with all the new images. cat = Table.read(data) RA_names = ['gauss_x_B3', 'gauss_x_B6', 'gauss_x_B7_hr'] inds = [] for fn in RA_names: ind = np.where(np.isnan(cat[fn]) == False) inds.append(ind) detected = reduce( np.intersect1d, inds ) #sources detected in B3, B6, B7 - only make measurements on these sources cat = cat[detected] imgs = glob.glob(directory + '*r-2.clean0.1mJy.500klplus.deepmask*') img_names = [img[len(directory) + name_start:name_end] for img in imgs] col_names = fnmatch.filter(cat.colnames, 'ap_flux_r*') col_names = [cn[8:] for cn in col_names] for j, img in enumerate(imgs): name = img_names[j] if len(name) > 54: name = name[0:53] if name not in col_names: fl = fits.open(img) header = fl[0].header try: img_data = fl[0].data.squeeze() except TypeError: print("error! img: " + img) continue mywcs = WCS(header).celestial beam = radio_beam.Beam.from_fits_header(header) pixel_scale = np.abs( mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg ppbeam = (beam.sr / (pixel_scale**2)).decompose().value save_dir = '/lustre/aoc/students/jotter/gauss_diags/diff_imgs/' + ref_name + '/' + name + '/' if not os.path.exists(save_dir): os.makedirs(save_dir) reg_file = '/users/jotter/summer_research_2018/final_regs/' + name + '_reg_file_B6_apflux.reg' with open(reg_file, 'w') as fh: fh.write("fk5\n") for ind in range(len(cat)): fh.write( 'ellipse({x_cen}, {y_cen}, {maj}, {minr}, {ang}) #text={{{ID}}}\n' .format(x_cen=cat['gauss_x_' + ref_name][ind], y_cen=cat['gauss_y_' + ref_name][ind], maj=(cat['FWHM_major_' + ref_name][ind] * u.arcsec.to(u.degree)), minr=(cat['FWHM_minor_' + ref_name][ind] * u.arcsec.to(u.degree)), ang=cat['position_angle_' + ref_name][ind], ID=str(cat['D_ID'][ind]))) rad = Angle(1, 'arcsecond') #radius for region list regs = [] for ind in range(len(cat)): reg = regions.CircleSkyRegion(center=SkyCoord( cat['gauss_x_' + ref_name][ind] * u.degree, cat['gauss_y_' + ref_name][ind] * u.degree), radius=rad, meta={ 'text': str(cat['D_ID'][ind]) }) reg_pix = reg.to_pixel(mywcs) if reg_pix.center.x > 0 and reg_pix.center.x < len( img_data[0]): if reg_pix.center.y > 0 and reg_pix.center.y < len( img_data): regs.append(reg) cat_r = Angle(0.5, 'arcsecond') #radius in gaussian fitting gauss_cat = gaussfit_catalog( img, regs, cat_r, savepath=save_dir) #output is nested dictionary structure gauss_fit_tab = Table( names=('D_ID', 'FWHM_major_' + name, 'major_err_' + name, 'FWHM_minor_' + name, 'minor_err_' + name, 'pa_' + name, 'pa_err_' + name, 'g_amplitude_' + name, 'amp_err_' + name), dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8')) for key in gauss_cat: gauss_fit_tab.add_row( (key, gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'], gauss_cat[key]['e_pa'], gauss_cat[key]['amplitude'], gauss_cat[key]['e_amplitude'])) #fill table cat = join(cat, gauss_fit_tab, keys='D_ID', join_type='left') ap_flux_arr = [] circ_flux_arr = [] ap_flux_err_arr = [] circ_flux_err_arr = [] bg_median_arr = [] bg_ap_arr = [] bg_circ_arr = [] for row in range(len(cat)): pix_major_fwhm = ((cat['FWHM_major_' + ref_name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() pix_minor_fwhm = ((cat['FWHM_minor_' + ref_name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() center_coord = SkyCoord(cat['gauss_x_' + ref_name][row], cat['gauss_y_' + ref_name][row], frame='icrs', unit=(u.deg, u.deg)) center_coord_pix = center_coord.to_pixel(mywcs) center_coord_pix_reg = regions.PixCoord( center_coord_pix[0], center_coord_pix[1]) position_angle = cat['position_angle_' + ref_name][row] * u.deg print(center_coord_pix_reg, pix_major_fwhm, pix_minor_fwhm) ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg, pix_major_fwhm * 2., pix_minor_fwhm * 2., angle=position_angle) size = pix_major_fwhm * 2.1 ap_mask = ellipse_reg.to_mask() cutout_mask = ap_mask.cutout(img_data) aperture_flux = np.sum(cutout_mask[ap_mask.data == 1]) / ppbeam npix = len(cutout_mask[ap_mask.data == 1]) ap_flux_arr.append(aperture_flux) #now creating annulus around source to measure background and ap flux error annulus_width = 15 annulus_radius = 0.1 * u.arcsecond annulus_radius_pix = (annulus_radius.to(u.degree) / pixel_scale).decompose() # Cutout section of the image we care about, to speed up computation time size = 2.5 * annulus_radius cutout = Cutout2D(img_data, center_coord_pix, size, mywcs, mode='partial') #cutout of outer circle cutout_center = regions.PixCoord(cutout.center_cutout[0], cutout.center_cutout[1]) # Define the aperture regions needed for SNR innerann_reg = regions.CirclePixelRegion( cutout_center, annulus_radius_pix) outerann_reg = regions.CirclePixelRegion( cutout_center, annulus_radius_pix + annulus_width) # Make masks from aperture regions annulus_mask = mask(outerann_reg, cutout) - mask( innerann_reg, cutout) # Calculate the SNR and aperture flux sums pixels_in_annulus = cutout.data[annulus_mask.astype( 'bool')] #pixels within annulus bg_rms = rms(pixels_in_annulus) ap_bg_rms = bg_rms / np.sqrt( npix / ppbeam) #rms/sqrt(npix/ppbeam) - rms error per beam bg_median = np.median(pixels_in_annulus) pix_bg = bg_median * npix / ppbeam ap_flux_err_arr.append(ap_bg_rms) bg_median_arr.append(bg_median) bg_ap_arr.append(pix_bg) #now measure circle flux: radius = 0.1 * u.arcsecond radius_pix = annulus_radius_pix circle_reg = regions.CirclePixelRegion(center_coord_pix_reg, radius_pix) circ_ap_mask = circle_reg.to_mask() circ_cutout_mask = circ_ap_mask.cutout(img_data) cutout_mask = ap_mask.cutout(img_data) circ_aperture_flux = np.sum( circ_cutout_mask[circ_ap_mask.data == 1]) / ppbeam circ_npix = len(circ_cutout_mask[circ_ap_mask.data == 1]) circ_bg_rms = bg_rms / np.sqrt(circ_npix / ppbeam) circ_flux_arr.append(circ_aperture_flux) circ_flux_err_arr.append(circ_bg_rms) bg_circ_arr.append(bg_median * circ_npix / ppbeam) cols = [ 'ap_flux_', 'ap_flux_err_', 'bg_median_', 'bg_ap_', 'circ_flux_', 'circ_flux_err_', 'bg_circ_' ] arrs = [ ap_flux_arr, ap_flux_err_arr, bg_median_arr, bg_ap_arr, circ_flux_arr, circ_flux_err_arr, bg_circ_arr ] for j in range(len(cols)): cat[cols[j] + name] = arrs[j] cat.write('/lustre/aoc/students/jotter/dendro_catalogs/' + data_name + '_500klplus_allsrcs_catalog_' + ref_name + '_ref.txt', format='ascii', overwrite=True)
new_fl = orig_fl new_fl[0].data = fake_img wcs = WCS(new_fl[0].header).celestial B3beam = Beam.from_fits_header(new_fl[0].header) B3_pixel_scale = np.abs(wcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg B3_kernel = B3beam.as_kernel(B3_pixel_scale) convolved_img = convolve_fft(fake_img, B3_kernel) new_fl[0].data = convolved_img new_fl.writeto('/users/jotter/summer_research_2018/fake_conv_img.fits', overwrite=True) loc = wcs.wcs_pix2world(500, 500, 1) reg = regions.CircleSkyRegion(center=SkyCoord(loc[0], loc[1], unit='deg'), radius=0.5 * u.arcsec, meta={'text': 'test'}) reg_pix = reg.to_pixel(wcs) gaussfit = gaussfit_catalog( '/users/jotter/summer_research_2018/fake_conv_img.fits', [reg], Angle(0.5, 'arcsecond'), savepath='/users/jotter/summer_research_2018/') source_size = Beam(major=gaussfit['test']['fwhm_major'], minor=gaussfit['test']['fwhm_minor'], pa=(gaussfit['test']['pa'].value - 90) * u.degree) try: deconv_size = source_size.deconvolve(B3beam) print('gaussfit deconv major: ' + str(gaussfit['test']['deconv_fwjm_major']) + ' minor: ' +
def measure_fluxes_gaussfit_allsrcs(data, img, data_name, name_start=27, name_end=-21, ref_name='B3'): cat = Table.read(data) RA_name = 'gauss_x_' + ref_name ind = np.where(np.isnan(cat[RA_name]) == False) cat = cat[ind] col_names = fnmatch.filter(cat.colnames, 'ap_flux_r*') col_names = [cn[8:] for cn in col_names] fl = fits.open(img) header = fl[0].header img_data = fl[0].data.squeeze() mywcs = WCS(header).celestial beam = radio_beam.Beam.from_fits_header(header) pixel_scale = np.abs( mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg ppbeam = (beam.sr / (pixel_scale**2)).decompose().value save_dir = '/lustre/aoc/students/jotter/gauss_diags/diff_imgs/' + ref_name + '/' + data_name + '/' if not os.path.exists(save_dir): os.makedirs(save_dir) reg_file = '/users/jotter/summer_research_2018/final_regs/misc_regs/' + data_name + '_reg_file_' + ref_name + '_apflux.reg' with open(reg_file, 'w') as fh: fh.write("fk5\n") for ind in range(len(cat)): fh.write( 'ellipse({x_cen}, {y_cen}, {maj}, {minr}, {ang}) #text={{{ID}}}\n' .format(x_cen=cat['gauss_x_' + ref_name][ind], y_cen=cat['gauss_y_' + ref_name][ind], maj=(cat['FWHM_major_' + ref_name][ind] * u.arcsec.to(u.degree)), minr=(cat['FWHM_minor_' + ref_name][ind] * u.arcsec.to(u.degree)), ang=cat['position_angle_' + ref_name][ind], ID=str(cat['D_ID'][ind]))) rad = Angle(1, 'arcsecond') #radius for region list regs = [] for ind in range(len(cat)): reg = regions.CircleSkyRegion(center=SkyCoord( cat['gauss_x_' + ref_name][ind] * u.degree, cat['gauss_y_' + ref_name][ind] * u.degree), radius=rad, meta={'text': str(cat['D_ID'][ind])}) reg_pix = reg.to_pixel(mywcs) if reg_pix.center.x > 0 and reg_pix.center.x < len(img_data[0]): if reg_pix.center.y > 0 and reg_pix.center.y < len(img_data): if np.isnan(img_data[int(reg_pix.center.x), int(reg_pix.center.y)]) == False: regs.append(reg) cat_r = Angle(0.5, 'arcsecond') #radius in gaussian fitting gauss_cat = gaussfit_catalog( img, regs, cat_r, savepath=save_dir) #output is nested dictionary structure gauss_fit_tab = Table( names=('D_ID', 'FWHM_major_' + data_name, 'major_err_' + data_name, 'FWHM_minor_' + data_name, 'minor_err_' + data_name, 'pa_' + data_name, 'pa_err_' + data_name, 'g_amplitude_' + data_name, 'amp_err_' + data_name), dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8')) for key in gauss_cat: gauss_fit_tab.add_row( (key, gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'], gauss_cat[key]['e_pa'], gauss_cat[key]['amplitude'], gauss_cat[key]['e_amplitude'])) #fill table ap_flux_arr = [] circ_flux_arr = [] ap_flux_err_arr = [] circ_flux_err_arr = [] bg_median_arr = [] bg_ap_arr = [] bg_circ_arr = [] RA = [] DEC = [] RA_err = [] DEC_err = [] for row in range(len(cat)): gauss_ind = np.where(gauss_fit_tab['D_ID'] == cat['D_ID'][row])[0] if len(gauss_ind) > 0: pix_major_fwhm = ( (cat['FWHM_major_' + ref_name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() pix_minor_fwhm = ( (cat['FWHM_minor_' + ref_name][row] * u.arcsec).to(u.degree) / pixel_scale).decompose() center_coord = SkyCoord(cat['gauss_x_' + ref_name][row], cat['gauss_y_' + ref_name][row], frame='icrs', unit=(u.deg, u.deg)) center_coord_pix = center_coord.to_pixel(mywcs) center_coord_pix_reg = regions.PixCoord(center_coord_pix[0], center_coord_pix[1]) position_angle = cat['position_angle_' + ref_name][row] * u.deg ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg, pix_major_fwhm * 2., pix_minor_fwhm * 2., angle=position_angle) size = pix_major_fwhm * 2.1 ap_mask = ellipse_reg.to_mask() cutout_mask = ap_mask.cutout(img_data) aperture_flux = np.sum(cutout_mask[ap_mask.data == 1]) / ppbeam npix = len(cutout_mask[ap_mask.data == 1]) ap_flux_arr.append(aperture_flux) #now creating annulus around source to measure background and ap flux error annulus_width = 15 annulus_radius = 0.1 * u.arcsecond annulus_radius_pix = (annulus_radius.to(u.degree) / pixel_scale).decompose() # Cutout section of the image we care about, to speed up computation time size = 2.5 * annulus_radius cutout = Cutout2D(img_data, center_coord_pix, size, mywcs, mode='partial') #cutout of outer circle cutout_center = regions.PixCoord(cutout.center_cutout[0], cutout.center_cutout[1]) # Define the aperture regions needed for SNR innerann_reg = regions.CirclePixelRegion(cutout_center, annulus_radius_pix) outerann_reg = regions.CirclePixelRegion( cutout_center, annulus_radius_pix + annulus_width) # Make masks from aperture regions annulus_mask = mask(outerann_reg, cutout) - mask( innerann_reg, cutout) # Calculate the SNR and aperture flux sums pixels_in_annulus = cutout.data[annulus_mask.astype( 'bool')] #pixels within annulus bg_rms = rms(pixels_in_annulus) ap_bg_rms = bg_rms / np.sqrt( npix / ppbeam) #rms/sqrt(npix/ppbeam) - rms error per beam bg_median = np.median(pixels_in_annulus) pix_bg = bg_median * npix / ppbeam ap_flux_err_arr.append(ap_bg_rms) bg_median_arr.append(bg_median) bg_ap_arr.append(pix_bg) #now measure circle flux: radius = 0.1 * u.arcsecond radius_pix = annulus_radius_pix circle_reg = regions.CirclePixelRegion(center_coord_pix_reg, radius_pix) circ_ap_mask = circle_reg.to_mask() circ_cutout_mask = circ_ap_mask.cutout(img_data) cutout_mask = ap_mask.cutout(img_data) circ_aperture_flux = np.sum( circ_cutout_mask[circ_ap_mask.data == 1]) / ppbeam circ_npix = len(circ_cutout_mask[circ_ap_mask.data == 1]) circ_bg_rms = bg_rms / np.sqrt(circ_npix / ppbeam) circ_flux_arr.append(circ_aperture_flux) circ_flux_err_arr.append(circ_bg_rms) bg_circ_arr.append(bg_median * circ_npix / ppbeam) RA.append(cat['gauss_x_' + ref_name][row]) DEC.append(cat['gauss_y_' + ref_name][row]) RA_err.append(cat['x_err_' + ref_name][row]) DEC_err.append(cat['y_err_' + ref_name][row]) cols = [ 'ap_flux_', 'ap_flux_err_', 'bg_median_', 'bg_ap_', 'circ_flux_', 'circ_flux_err_', 'bg_circ_' ] arrs = [ ap_flux_arr, ap_flux_err_arr, bg_median_arr, bg_ap_arr, circ_flux_arr, circ_flux_err_arr, bg_circ_arr ] cols2 = ['RA_', 'DEC_', 'RA_err_', 'DEC_err_'] #seperate bc different naming arrs2 = [RA, DEC, RA_err, DEC_err] for j in range(len(cols)): gauss_fit_tab[cols[j] + data_name] = arrs[j] for c in range(len(cols2)): gauss_fit_tab[cols2[c] + ref_name] = arrs2[c] gauss_fit_tab.write('/lustre/aoc/students/jotter/dendro_catalogs/' + data_name + '_500klplus_allsrcs_catalog_' + ref_name + '_ref.txt', format='ascii', overwrite=True)
contfile_north = fits.open( paths.dpath('longbaseline/W51n_cont_briggsSC_tclean.image.fits')) datanorth = u.Quantity(contfile_north[0].data.squeeze(), unit=contfile_north[0].header['BUNIT']) beam_north = radio_beam.Beam.from_fits_header(contfile_north[0].header) wcs_north = wcs.WCS(contfile_north[0].header) # this section repeats the analysis of dust_proerties, but it serves as a nice # independent check since I did it 6 months later.... give or take... beam_radius_north = ((beam_north.sr / (2 * np.pi))**0.5 * masscalc.distance).to(u.pc, u.dimensionless_angles()) e2 = regions.CircleSkyRegion(coordinates.SkyCoord('19:23:43.969 +14:30:34.518', frame='icrs', unit=(u.hour, u.deg)), radius=0.616 * u.arcsec) e2pix = e2.to_pixel(wcs_e2e8) e2mask = e2pix.to_mask() e8 = regions.CircleSkyRegion(coordinates.SkyCoord('19:23:43.907 +14:30:28.267', frame='icrs', unit=(u.hour, u.deg)), radius=0.464 * u.arcsec) e8pix = e8.to_pixel(wcs_e2e8) e8mask = e8pix.to_mask() north = regions.CircleSkyRegion(coordinates.SkyCoord( '19:23:40.054 +14:31:05.513', frame='icrs', unit=(u.hour, u.deg)), radius=0.412 * u.arcsec) northpix = north.to_pixel(wcs_north)