def smooth_cube( incube=None, outfile=None, angular_resolution=None, linear_resolution=None, distance=None, velocity_resolution=None, nan_treatment='interpolate', # can also be 'fill' tol=None, make_coverage_cube=False, collapse_coverage=False, coveragefile=None, coverage2dfile=None, dtype=np.float32, overwrite=True ): """ Smooth an input cube to coarser angular or spectral resolution. This lightly wraps spectral cube and some of the error checking is left to that. tol is a fraction. When the target beam is within tol of the original beam, we just copy. Optionally, also calculate a coverage footprint in which original (finite) cube coverage starts at 1.0 and the output cube shows the fraction of finite pixels. """ # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% # Error checking # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% # Require a valid cube or map input if type(incube) is SpectralCube: cube = incube elif type(incube) == type("hello"): cube = SpectralCube.read(incube) else: logger.error("Input must be a SpectralCube object or a filename.") # Allow huge operations. If the speed or segfaults become a huge # problem, we will adjust our strategy here. cube.allow_huge_operations = True # Check that only one target scale is set if (angular_resolution is not None) and (linear_resolution is not None): logger.error('Only one of angular_resolution or ', 'linear_resolution can be set') return(None) # Work out the target angular resolution if angular_resolution is not None: if type(angular_resolution) is str: angular_resolution = u.Quantity(angular_resolution) if linear_resolution is not None: if distance is None: logger.error('Convolution to linear resolution requires a distance.') return(None) if type(distance) is str: distance = u.Quantity(distance) if type(linear_resolution) is str: linear_resolution = u.Quantity(linear_resolution) angular_resolution = (linear_resolution / distance * u.rad).to(u.arcsec) dist_mpc_val = float(distance.to(u.pc).value) / 1e6 cube._header.append(('DIST_MPC',dist_mpc_val,'Used in convolution')) if tol is None: tol = 0.0 # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% # Convolution to coarser beam # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% if angular_resolution is not None: logger.info("... convolving from beam: "+str(cube.beam)) target_beam = Beam(major=angular_resolution, minor=angular_resolution, pa=0 * u.deg) logger.info("... convolving to beam: "+str(target_beam)) new_major = float(target_beam.major.to(u.arcsec).value) old_major = float(cube.beam.major.to(u.arcsec).value) delta = (new_major-old_major)/old_major logger.info("... fractional change: "+str(delta)) if make_coverage_cube: coverage = SpectralCube(np.isfinite(cube.unmasked_data[:])*1.0, wcs=cube.wcs, header=cube.header, meta={'BUNIT': ' ', 'BTYPE': 'Coverage'}) coverage = coverage.with_mask(LazyMask(np.isfinite,cube=coverage)) # Allow huge operations. If the speed or segfaults become a huge # problem, we will adjust our strategy here. coverage.allow_huge_operations = True if delta > tol: logger.info("... proceeding with convolution.") cube = cube.convolve_to(target_beam, nan_treatment=nan_treatment) if make_coverage_cube: coverage = coverage.convolve_to(target_beam, nan_treatment=nan_treatment) if np.abs(delta) < tol: logger.info("... current resolution meets tolerance.") if delta < -1.0*tol: logger.info("... resolution cannot be matched. Returning") return(None) # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% # Spectral convolution # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% # This is only a boxcar smooth right now and does not downsample # or update the header. if velocity_resolution is not None: if type(velocity_resolution) is str: velocity_resolution = u.Quantity(velocity_resolution) dv = scdr.channel_width(cube) nChan = (velocity_resolution / dv).to(u.dimensionless_unscaled).value if nChan > 1: cube = cube.spectral_smooth(Box1DKernel(nChan)) if make_coverage_cube: coverage = coverage.spectral_smooth(Box1DKernel(nChan)) # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% # Write or return as requested # &%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&%&% if outfile is not None: # cube.write(outfile, overwrite=overwrite) hdu = fits.PrimaryHDU(np.array(cube.filled_data[:], dtype=dtype), header=cube.header) hdu.writeto(outfile, overwrite=overwrite) if make_coverage_cube: if coveragefile is not None: hdu = fits.PrimaryHDU(np.array(coverage.filled_data[:], dtype=dtype), header=coverage.header) hdu.writeto(coveragefile, overwrite=overwrite) if collapse_coverage: if coveragefile and not coverage2dfile: coverage2dfile = coveragefile.replace('.fits','2d.fits') coverage_collapser(coverage, coverage2dfile=coverage2dfile, overwrite=overwrite) # coverage.write(coveragefile, overwrite=overwrite) return(cube)
def cleansplit(filename, galaxy=None, Vwindow=650 * u.km / u.s, Vgalaxy=300 * u.km / u.s, blorder=3, HanningLoops=0, maskfile=None, circleMask=True, edgeMask=False, weightCut=0.2, spectralSetup=None, spatialSmooth=1.0): """ Takes a raw DEGAS cube and produces individual cubes for each spectral line. Paramters --------- filename : str The file to split. Keywords -------- galaxy : Galaxy object Currently unused Vwindow : astropy.Quantity Width of the window in velocity units Vgalaxy : astropy.Quantity Line of sight velocity of the galaxy centre blorder : int Baseline order HanningLoops : int Number of times to smooth and resample the data edgeMask : bool Determine whether to apply an edgeMask weightCut : float Minimum weight value to include in the data spatialSmooth : float Factor to increase the (linear) beam size by in a convolution. spectralSetup : str String to determine how we set up the spectrum 'hcn_hcop' -- split based on HCN/HCO+ setup '13co_c18o' -- split based on 13CO/C18O setup '12co' -- don't split; assume single line """ Cube = SpectralCube.read(filename) CatalogFile = get_pkg_data_filename('./data/dense_survey.cat', package='degas') Catalog = Table.read(CatalogFile, format='ascii') # Find which galaxy in our catalog corresponds to the object we # are mapping if galaxy is None: RABound, DecBound = Cube.world_extrema match = np.zeros_like(Catalog, dtype=np.bool) for index, row in enumerate(Catalog): galcoord = SkyCoord(row['RA'], row['DEC'], unit=(u.hourangle, u.deg)) if (galcoord.ra < RABound[1] and galcoord.ra > RABound[0] and galcoord.dec < DecBound[1] and galcoord.dec > DecBound[0]): match[index] = True MatchRow = Catalog[match] galcoord = SkyCoord(MatchRow['RA'], MatchRow['DEC'], unit=(u.hourangle, u.deg)) Galaxy = MatchRow['NAME'].data[0] print("Catalog Match with " + Galaxy) V0 = MatchRow['CATVEL'].data[0] * u.km / u.s # Check spectral setups. Use the max frequencies present to # determine which spectral setup we used if not specifed. if spectralSetup is None: if (Cube.spectral_axis.max() > 105 * u.GHz and Cube.spectral_axis.max() < 113 * u.GHz): warnings.warn("assuming 13CO/C18O spectral setup") spectralSetup = '13CO_C18O' filestr = '13co_c18o' if (Cube.spectral_axis.max() > 82 * u.GHz and Cube.spectral_axis.max() < 90 * u.GHz): warnings.warn("assuming HCN/HCO+ spectral setup") spectralSetup = 'HCN_HCO+' filestr = 'hcn_hcop' if (Cube.spectral_axis.max() > 113 * u.GHz): warnings.warn("assuming 12CO spectral setup") spectralSetup = '12CO' filestr = '12co' if spectralSetup == '13CO_C18O': CEighteenO = Cube.with_spectral_unit(u.km / u.s, velocity_convention='radio', rest_value=109.78217 * u.GHz) ThirteenCO = Cube.with_spectral_unit(u.km / u.s, velocity_convention='radio', rest_value=110.20135 * u.GHz) CubeList = (CEighteenO, ThirteenCO) LineList = ('C18O', '13CO') elif spectralSetup == 'HCN_HCO+': HCN = Cube.with_spectral_unit(u.km / u.s, velocity_convention='radio', rest_value=88.631847 * u.GHz) HCOp = Cube.with_spectral_unit(u.km / u.s, velocity_convention='radio', rest_value=89.188518 * u.GHz) CubeList = (HCN, HCOp) LineList = ('HCN', 'HCOp') elif spectralSetup == '12CO': TwelveCO = Cube.with_spectral_unit(u.km / u.s, velocity_convention='radio', rest_value=115.27120180 * u.GHz) CubeList = (TwelveCO, ) LineList = ('12CO', ) for ThisCube, ThisLine in zip(CubeList, LineList): if circleMask: x0, y0, _ = ThisCube.wcs.wcs_world2pix(galcoord.ra, galcoord.dec, 0, 0) ThisCube = circletrim(ThisCube, filename.replace('.fits', '_wts.fits'), x0, y0, weightCut=weightCut) if edgeMask: ThisCube = edgetrim(ThisCube, filename.replace('.fits', '_wts.fits'), weightCut=weightCut) # Trim each cube to the specified velocity range ThisCube = ThisCube.spectral_slab(V0 - Vwindow, V0 + Vwindow) ThisCube.write(Galaxy + '_' + ThisLine + '.fits', overwrite=True) StartChan = ThisCube.closest_spectral_channel(V0 - Vgalaxy) EndChan = ThisCube.closest_spectral_channel(V0 + Vgalaxy) if maskfile is not None: maskLookup = buildMaskLookup(maskfile) shp = ThisCube.shape TmpCube = ThisCube.with_spectral_unit(u.Hz) spaxis = TmpCube.spectral_axis spaxis = spaxis.value data = ThisCube.filled_data[:].value for y in np.arange(shp[1]): for x in np.arange(shp[2]): spectrum = data[:, y, x] if np.any(np.isnan(spectrum)): continue coords = ThisCube.world[:, y, x] mask = maskLookup(coords[2].value, coords[1].value, spaxis) spectrum = robustBaseline(spectrum, blorder=blorder, baselineIndex=~mask) data[:, y, x] = spectrum ThisCube = SpectralCube(data * ThisCube.unit, ThisCube.wcs, header=ThisCube.header, meta={'BUNIT': ThisCube.header['BUNIT']}) ThisCube.write(Galaxy + '_' + ThisLine + '_rebase{0}.fits'.format(blorder), overwrite=True) else: gbtpipe.Baseline.rebaseline(Galaxy + '_' + ThisLine + '.fits', baselineRegion=[ slice(0, StartChan, 1), slice(EndChan, ThisCube.shape[0], 1) ], blorder=blorder) ThisCube = SpectralCube.read(Galaxy + '_' + ThisLine + '_rebase{0}'.format(blorder) + '.fits') # Smooth Kern = Kernel1D(array=np.array([0.5, 1.0, 0.5])) for i in range(HanningLoops): ThisCube.spectral_smooth(Kern) ThisCube = ThisCube[::2, :, :] # Spatial Smooth if spatialSmooth > 1.0: newBeam = Beam(major=ThisCube.beam.major * spatialSmooth, minor=ThisCube.beam.minor * spatialSmooth) ThisCube.convolve_to(newBeam) smoothstr = '_smooth{0}'.format(spatialSmooth) else: smoothstr = '' # Final Writeout ThisCube.write(Galaxy + '_' + ThisLine + '_rebase{0}'.format(blorder) + smoothstr + '_hanning{0}.fits'.format(HanningLoops), overwrite=True)
mask = mask.reproject(cube_degas.header) fl = glob.glob(empire_data + 'EMPIRE_{0}_{1}_*.fits'.format(gal.lower(),molecule.lower())) hdulist = fits.open(fl[0]) hdu = sanitize(hdulist[0]) cube_empire = SpectralCube(data=hdu.data, header=hdu.header, wcs=wcs.WCS(hdu.header)) empire_mask = np.isfinite(hdu.data) cube_empire = cube_empire.with_mask(empire_mask) channel_ratio = ((cube_degas.spectral_axis[1] - cube_degas.spectral_axis[0]) / (cube_empire.spectral_axis[1] - cube_empire.spectral_axis[0])).to(u.dimensionless_unscaled).value kernel = Box1DKernel(channel_ratio) cube_empire = cube_empire.spectral_smooth(kernel) cube_empire = cube_empire.spectral_interpolate(cube_degas.spectral_axis) cube_empire = cube_empire.reproject(cube_degas.header) cube_degas = cube_degas.convolve_to(cube_empire.beam) noise_empire = mad(cube_empire.filled_data[:].value, ignore_nan=True) noise_degas = mad(cube_degas.filled_data[:].value, ignore_nan=True) bools = mask.filled_data[:] > 1 degas_vals = cube_degas.filled_data[bools].value empire_vals = cube_empire.filled_data[bools].value idx = np.isfinite(degas_vals) * np.isfinite(empire_vals) ax.plot(empire_vals * sf, degas_vals * sf,'ro', alpha=0.5, markeredgecolor='k', label=molecule) xlims = ax.get_xlim()