def import_fits(filename): """Imports a FITS radio image from CASA pipeline""" fits_image_filename = os.getcwd()+filename with fits.open(fits_image_filename) as hdul: #print(hdul.info()) data=hdul[0].data #now convert to Jy/str surface brigthness header = fits.getheader(fits_image_filename) if header['BUNIT'] == 'Jy/beam': beam = Beam.from_fits_header(header) SB=((data*u.Jy/u.beam).to(u.MJy/u.sr, equivalencies=u.beam_angular_area(beam)))[0,0,:,:].value elif header['BUNIT'] == 'JY/BEAM': beam = Beam.from_fits_header(header) SB=((data*u.Jy/u.beam).to(u.MJy/u.sr, equivalencies=u.beam_angular_area(beam)))[0,0,:,:].value elif header['BUNIT'] == 'MJy/sr': SB=data """SB is a 2-d array with Surface_brigthness values in MJy/sr. For masked arrays, some values are nan, need to create a mask to these values. Before that, need to set all values not within data range to NAN. Using SB.data can access the original array.""" sb_maskedNAN = np.ma.array(SB, mask=np.isnan(SB)) array = np.ma.array(sb_maskedNAN, mask=np.isnan(sb_maskedNAN)) print('Max value in image is :', np.max(sb_maskedNAN), ' MJy/sr') print('Min value in image is :', np.min(sb_maskedNAN), ' MJy/sr')
def jy_beam_MJy_sr(data, header): """Funtion which converts fits data into units of Jy/sr""" if header['BUNIT'] == 'Jy/beam': beam = Beam.from_fits_header(header) SB = ((data * u.Jy / u.beam).to( u.MJy / u.sr, equivalencies=u.beam_angular_area(beam))).value elif header['BUNIT'] == 'JY/BEAM': beam = Beam.from_fits_header(header) SB = ((data * u.Jy / u.beam).to( u.MJy / u.sr, equivalencies=u.beam_angular_area(beam))).value elif header['BUNIT'] == 'MJy/sr': SB = data return SB
def column_density(flux, beam, wave, t, kappa, mu=2.8): """ Computes column density from continuum data Parameters ---------- flux : float flux in Jy/beam beam : array like beam size in arcseconds wave : float wavelength (m) t: float (optional) temperature (K) kappa: float dust opacity per unit mass (cm^2g^-1) mu : float (optional) molecular weight (default = 2.8; i.e. assumes density is given as density of hydrogen molecules) """ beam = beam * u.arcsec omega_beam = beam_solid_angle(beam) flux = (flux * u.Jy / u.beam).to( u.Jy / u.sr, equivalencies=u.beam_angular_area(omega_beam)) wave = wave * u.m t = t * u.K kappa = kappa * (u.cm**2 / u.g) B = blackbody_nu(wave.to(u.Hz, equivalencies=u.spectral()), t) N = flux / (mu * ap.M_p * kappa * B) N = N.to(1. / u.cm**2) return N
def contour_plot(ax, contour_file, contour_type, image_beam): """Plots contours over the main image, can be either from the same image or from a different one, fits file.""" cblock = import_contours(contour_file) cdata = cblock[0] cheader = cblock[1] cwcs = WCS(cheader) contour_type = str(contour_type) ax.set_autoscale_on(False) norm = ImageNormalize(cdata, interval=MinMaxInterval(), stretch=SqrtStretch()) if contour_type == "automatic": spacing = contour_bias n = 6 #number of contours ub = np.max(cdata) lb = np.min(cdata) def level_func(lb, ub, n, spacing=1.1): span = (ub - lb) dx = 1.0 / (n - 1) return [lb + (i * dx)**spacing * span for i in range(n)] levels = level_func(lb, ub, n, spacing=float(spacing)) levels = np.array(levels)[np.array(levels) > 0.] print('Generated Levels for contour are: ', levels, 'MJy/sr') CS = ax.contour(cdata, levels=levels, colors='black', transform=ax.get_transform(cwcs.celestial), alpha=1.0, linewidths=1) if contour_type == "sigma": contour_levels = list(map(float, args.contour_levels)) sigma_levels = np.array(contour_levels) rms = (0.0004 * u.Jy / u.beam).to( u.MJy / u.sr, equivalencies=u.beam_angular_area(image_beam)) levels = rms * sigma_levels print('Sigma Levels for contour are: ', sigma_levels, 'sigma') CS = ax.contour(cdata, levels=levels, norm=norm, transform=ax.get_transform(cwcs.celestial), colors='white', alpha=0.5) #plt.title(label='Contours at %s sigma' % contour_levels) if contour_type == "manual": levels = list(map(float, args.contour_levels)) print('Contour Levels are: ', levels, 'MJy/sr') CS = ax.contour(cdata, levels=levels, norm=norm, transform=ax.get_transform(cwcs.celestial), colors='white', alpha=0.5, linewidths=1.0)
def mass_jyperbeam(flux, beam, pixel_size, wave, t, kappa, distance): """ Takes flux in Jy/beam summed over some area and computes the mass using beam and pixel information Parameters: flux : float flux in Jy/beam beam : array like beam size in arcseconds pixel_size : number pixel size in arcseconds wave : float wavelength (m) t: float (optional) temperature (K) kappa: float dust opacity per unit mass (cm^2g^-1) distance: float distance to the object (pc) """ beam = beam * u.arcsec pixel_size = pixel_size * u.arcsec omega_beam = beam_solid_angle(beam) flux = (flux * u.Jy / u.beam).to( u.Jy / u.sr, equivalencies=u.beam_angular_area(omega_beam)) integrated_flux = flux * pixel_size**2 integrated_flux = integrated_flux.to(u.Jy) wave = wave * u.m t = t * u.K kappa = kappa * (u.cm**2 / u.g) distance = distance * (u.pc) B = blackbody_nu(wave.to(u.Hz, equivalencies=u.spectral()), t) B = B * u.sr m = (distance**2. * integrated_flux) / (kappa * B) m = m.to(ap.solMass) return m
def test_beamarea_equiv(): major = 0.1 * u.rad beam = Beam(major, major, 30 * u.deg) conv_factor = u.beam_angular_area(beam.sr) assert_quantity_allclose( (1 * u.Jy / u.beam).to(u.Jy / u.sr, equivalencies=conv_factor), (1 * u.Jy / u.beam).to(u.Jy / u.sr, equivalencies=beam.beamarea_equiv)) assert_quantity_allclose( (1 * u.Jy / u.sr).to(u.Jy / u.beam, equivalencies=conv_factor), (1 * u.Jy / u.sr).to(u.Jy / u.beam, equivalencies=beam.beamarea_equiv)) # Add a by-hand check value = (1 * u.Jy / u.sr).to(u.Jy / u.beam, equivalencies=conv_factor).value byhand_value = 1 * beam.sr.value npt.assert_allclose(value, byhand_value)
arrays = [] i = 0 freq_ar = np.array([4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5]) for im in images: with fits.open(im) as hdul: data = hdul[0].data[0, 0, :, :] header = fits.getheader(im) wcs = WCS(header) print(wcs) beam = Beam.from_fits_header(header) SB = ((data * u.Jy / u.beam).to( u.MJy / u.sr, equivalencies=u.beam_angular_area(beam))).value where_are_NaNs = np.isnan(SB) SB[where_are_NaNs] = 0.0 #SB_masked = np.ma.masked_invalid(SB) where_are_neg = np.where(SB < 0) SB[where_are_neg] = 0.0 result = ndimage.zoom(SB, 1 / 4.0) print(np.shape(result)) i += 1 arrays.append(result) spectral_ar = np.ones_like(arrays[0]) * 10 for freq in freq_ar: for i in range(np.shape(spectral_ar)[0] - 1): for j in range(np.shape(spectral_ar)[1] - 1):
def regrid_3d_sb( *, sim_data, sb, pixel_size=1.8 * _u.arcsec, beam_fwhm=5 * _u.arcsec, z=0.05, ): """ Grids & smooths the surface brightness, for a given redshift, pixel size, and observing beam information (currently assumes observing beam is a circular 2D Gaussian with constant & FWHM) Parameters --------- sim_data : HDF5 File The simulation data file sb : u.mJy / u.beam Surface brightness, in units of mJy per beam pixel_size : u.arcsec The gridded pixel size, in arcseconds beam_fwhm : u.arcsec The 2D observing beam Gaussian fwhm, in arcseconds z : float The redshift this sourced is observed at Returns ------- grid_x : u.arcsec X grid in arcseconds grid_y : u.arsec Y grid in arcseconds sb : u.mJy / u.beam Gridded and smoothed surface brightness, in units of mJy per beam """ fwhm_to_sigma = 1 / (8 * _np.log(2))**0.5 beam_sigma = beam_fwhm * fwhm_to_sigma omega_beam = 2 * _np.pi * beam_sigma**2 # Area for a circular 2D gaussian z = 0.05 kpc_per_arcsec = _cosmo.kpc_proper_per_arcmin(z) # Create our grid grid_res = pixel_size.to(_u.arcsec) x_min = (sim_data.mx[0] * sim_data.unit_length / kpc_per_arcsec).to( _u.arcsec) x_max = (sim_data.mx[-1] * sim_data.unit_length / kpc_per_arcsec).to( _u.arcsec) y_min = (sim_data.mz[0] * sim_data.unit_length / kpc_per_arcsec).to( _u.arcsec) y_max = (sim_data.mz[-1] * sim_data.unit_length / kpc_per_arcsec).to( _u.arcsec) new_x = _np.arange(x_min.value, x_max.value, grid_res.value) # in arcsec now new_y = _np.arange(y_min.value, y_max.value, grid_res.value) # in arcsec now grid_x, grid_y = _np.meshgrid(new_x, new_y, indexing="xy") # in arcsec old_x = ((sim_data.mx * sim_data.unit_length / kpc_per_arcsec).to( _u.arcsec).value) # in arcsec old_z = ((sim_data.mz * sim_data.unit_length / kpc_per_arcsec).to( _u.arcsec).value) # in arcsec # Regrid data # everything is in arcsec # save our units first, to add them back after sb_units = sb.unit sb_gridded = (_scipy.interpolate.interpn( points=(old_z, old_x), values=sb.value, xi=(grid_y, grid_x), method="linear", bounds_error=False, fill_value=0, ) * sb_units) # Smooth data stddev = beam_sigma / pixel_size kernel = _Gaussian2DKernel(x_stddev=stddev.value) sb_units = sb_gridded.unit sb_smoothed = _convolve(sb_gridded.value, kernel) * sb_units # Convert from per sr to per beam sb_final = sb_smoothed.to(_u.mJy / _u.beam, equivalencies=_u.beam_angular_area(omega_beam)) return (grid_x * _u.arcsec, grid_y * _u.arcsec, sb_final)
matplotlib.rcParams['figure.dpi'] = 120 matplotlib.rcParams['font.sans-serif'] = "Nimbus Roman" #coordinates of BD+60 2522 star_coord = SkyCoord("23h20m44.5s +61d11m40.5s", frame='icrs') #fits_image_filename = 'fits/ngc7635_g0.5_briggs1.0_nsig5.image.5sig_masked.fits' fits_image_filename = 'fits/ngc7635_g0.5_briggs1.0_nsig5.image.tt0.fits' with fits.open(fits_image_filename) as hdul: data = hdul[0].data[0, 0, :, :] header = fits.getheader(fits_image_filename) beam = Beam.from_fits_header(header) SB = ((data * u.Jy / u.beam).to(u.MJy / u.sr, equivalencies=u.beam_angular_area(beam))).value SB_masked = np.ma.masked_invalid(SB) def optical_depth(flux): SB_erg = flux * 1e-17 T = 8000 #K nu = 8e9 # central frequency of our broadband observations c = 3e10 #speed of light in cgs k_b = 1.38e-16 #boltzmann constant in cgs tau = -np.log(1 - (c**2 * SB_erg) / (2 * k_b * T * (nu**2))) return tau # Position of NGC 7635: 23 20 48.3 +61 12 06
def beamarea_equiv(self): return u.beam_angular_area(self.sr)