def _spectrum_from_component(self, layer, component, wcs, mask=None): data = SpectralCube(component.data, wcs) if mask is not None: data = data.with_mask(mask) if self._data_operation.currentIndex() == 1: spec_data = data.mean((1, 2)) elif self._data_operation.currentIndex() == 2: spec_data = data.median((1, 2)) else: spec_data = data.sum((1, 2)) spec_data = Spectrum1DRef(spec_data.data, unit=spec_data.unit, dispersion=data.spectral_axis.data, dispersion_unit=data.spectral_axis.unit, wcs=data.wcs, name=layer.label) # Store the relation between the component and the specviz data. If # the data exists, first remove the component from specviz and then # re-add it. if layer in self._specviz_data_cache: old_spec_data = self._specviz_data_cache[layer] dispatch.on_remove_data.emit(old_spec_data) self._specviz_data_cache[layer] = spec_data dispatch.on_add_to_window.emit(data=spec_data, style={'color': layer.style.rgba[:3]})
def _spectrum_from_component(self, layer, component, wcs, mask=None, cid=None): data = SpectralCube(component.data, wcs) if mask is not None: data = data.with_mask(mask) # Update the associated data attribute in the plugin self._spec_ops.spectral_data = data self._spec_ops.component_id = cid if self._data_operation.currentIndex() == 1: spec_data = data.mean((1, 2)) elif self._data_operation.currentIndex() == 2: spec_data = data.median((1, 2)) else: spec_data = data.sum((1, 2)) spec_data = Spectrum1DRef(spec_data.data, unit=spec_data.unit, dispersion=data.spectral_axis.data, dispersion_unit=data.spectral_axis.unit, wcs=data.wcs, name=layer.label) spec_layer = Spectrum1DRefLayer.from_parent(spec_data) # Store the relation between the component and the specviz data. If # the data exists, first remove the component from specviz and then # re-add it. old_spec_layer = self._specviz_data_cache.get(layer) self._specviz_data_cache[layer] = spec_layer if old_spec_layer is None: dispatch.on_add_to_window.emit(layer=spec_layer, style={ 'color': layer.style.rgba[:3], 'line_width': 3 }, vertical_line=True) else: dispatch.replace_layer.emit(old_layer=old_spec_layer, new_layer=spec_layer, style={ 'color': layer.style.rgba[:3], 'line_width': 3 })
def _spectrum_from_component(self, layer, component, wcs, mask=None): data = SpectralCube(component.data, wcs) if mask is not None: data = data.with_mask(mask) spec_data = data.sum((1, 2)) spec_data = Spectrum1DRef(spec_data.data, unit=spec_data.unit, dispersion=data.spectral_axis.data, dispersion_unit=data.spectral_axis.unit, wcs=data.wcs) # Store the relation between the component and the specviz data. If # the data exists, first remove the component from specviz and then # re-add it. if layer in self._specviz_data_cache: old_spec_data = self._specviz_data_cache[layer] dispatch.on_remove_data.emit(old_spec_data) self._specviz_data_cache[layer] = spec_data dispatch.on_add_to_window.emit(spec_data)
class MultiResObs(object): """ Object to hold observations of the same object at different resolutions. This is intended for matching and analyzing high-res (interferometric) and low-res (single dish) observations. """ def __init__(self, highres, lowres): super(MultiResObs, self).__init__() self.highres = SpectralCube.read(highres) self.lowres = SpectralCube.read(lowres) self.highres_convolved = None self.lowres_convolved = None self.lowbeam = self.lowres.beam self.highbeam = self.highres.beam self.combined_beam = self.lowbeam.convolve(self.highbeam) def apply_mask(self, highres_mask=None, lowres_mask=None): ''' Apply a pre-made mask to either of the data cubes. ''' if highres_mask is not None: self.highres = self.highres.with_mask(highres_mask) if lowres_mask is not None: self.lowres = self.lowres.with_mask(lowres_mask) def match_coords(self): ''' Match the spatial and spectral coordinates of the cubes. ''' # TODO: use Skycoords to convert extrema into the same frame # Are either of the cubes in the correct frame? if self.highres.header['CTYPE1'] != self.lowres.header['CTYPE1']: raise TypeError("ctypes do not match. Are observations in the " "same projection? Highres: " + self.highres.header['CTYPE1'] + " Lowres: " + self.lowres.header['CTYPE1']) elif self.highres.header['CTYPE2'] != self.lowres.header['CTYPE2']: raise TypeError("ctypes do not match. Are observations in the " "same projection? Highres: " + self.highres.header['CTYPE2'] + " Lowres: " + self.lowres.header['CTYPE2']) # Determine which cube should be slice down so both have the same # spatial coverage. limits_high = [] limits_low = [] low_long = self.lowres.longitude_extrema high_long = self.highres.longitude_extrema low_lat = self.lowres.latitude_extrema high_lat = self.highres.latitude_extrema if low_long[0] < high_long[0]: limits_low.append(high_long[0]) limits_high.append("min") else: limits_high.append(low_long[0]) limits_low.append("min") if low_long[1] > high_long[1]: limits_low.append(high_long[1]) limits_high.append("max") else: limits_high.append(low_long[0]) limits_low.append("min") if low_lat[1] > high_lat[1]: limits_low.append(high_lat[1]) limits_high.append("min") else: limits_high.append(low_lat[0]) limits_low.append("min") if low_lat[0] < high_lat[0]: limits_low.append(high_lat[0]) limits_high.append("max") else: limits_high.append(low_lat[0]) limits_low.append("max") # Apply common slicing to both self.highres = \ self.highres.subcube(xlo=limits_high[0], xhi=limits_high[1], ylo=limits_high[2], yhi=limits_high[3]) self.lowres = \ self.lowres.subcube(xlo=limits_low[0], xhi=limits_low[1], ylo=limits_low[2], yhi=limits_low[3]) # Now match the spectral extends low_spec = \ self.highres.spectral_extrema[0] if \ self.highres.spectral_extrema[0] > self.lowres.spectral_extrema[0]\ else self.lowres.spectral_extrema[0] high_spec = \ self.highres.spectral_extrema[1] if \ self.highres.spectral_extrema[1] < self.lowres.spectral_extrema[1]\ else self.lowres.spectral_extrema[1] self.highres = self.highres.spectral_slab(low_spec, high_spec) self.lowres = self.lowres.spectral_slab(low_spec, high_spec) def convert_to(self, unit=u.K, freq=1420.40575177*u.MHz): ''' Convert both sets to common brightness units. ''' convert_high = unit != self.highres.unit convert_low = unit != self.lowres.unit high_unit = self.highres.unit low_unit = self.lowres.unit if unit == u.K: if convert_high and 'Jy' in high_unit.name: self.highres = \ self.highres.to(unit, self.highbeam.jtok_equiv(freq)) if convert_low and 'Jy' in low_unit.name: self.lowres = \ self.lowres.to(unit, self.lowbeam.jtok_equiv(freq)) else: raise NotImplementedError("Only supporting Jy/beam -> K right now.") def convolve_to_common(self, verbose=False, use_dask=True, block=(256, 256)): ''' Convolve cubes to a common resolution using the combined beam. ''' # Create convolution kernels from the combined beam conv_kernel_high = \ self.combined_beam.as_kernel(wcs_to_platescale(self.highres.wcs)) # if use_dask: # assert np.alltrue([bl > kern for bl, kern in # zip(block, conv_kernel_high.shape)]) conv_kernel_low = \ self.combined_beam.as_kernel(wcs_to_platescale(self.lowres.wcs)) # if use_dask: # assert np.alltrue([bl > kern for bl, kern in # zip(block, conv_kernel_low.shape)]) high_pad = np.ceil(conv_kernel_high.shape[0] / 2).astype(int) highres_convolved = np.empty(self.highres.shape) high_chans = len(self.highres.spectral_axis) if verbose: print("Convolving high resolution cube.") if use_dask: highres_convolved = auto_dask_map(self.highres, blocks=block, args=[conv_kernel_high]) # for chan in range(high_chans): # if verbose: # print("On Channel: "+str(chan)+" of "+str(high_chans)) # if use_dask: # da_arr = \ # da.from_array(np.pad(self.highres.filled_data[chan, :, :], # high_pad, padwithnans), # chunks=block) # highres_convolved[chan, high_pad:-high_pad, # high_pad:-high_pad] = \ # da_arr.map_overlap( # lambda a: # convolve_fft(a, # conv_kernel_high, # boundary='fill', # interpolate_nan=True, # normalize_kernel=True), # depth=2*high_pad, # boundary=np.nan).compute() # else: # highres_convolved[chan, :, :] = \ # convolve_fft(self.highres.filled_data[chan, :, :], # conv_kernel_high, boundary='fill', # interpolate_nan=True, normalize_kernel=True) update_high_hdr = \ _update_beam_in_hdr(self.highres.header, self.combined_beam) self.highres_convolved = \ SpectralCube(highres_convolved*self.highres.unit, self.highres.wcs, header=update_high_hdr) # Cleanup a bit del highres_convolved # Now the low resolution data lowres_convolved = np.empty(self.lowres.shape) low_chans = len(self.lowres.spectral_axis) if verbose: print("Convolving low resolution cube.") if use_dask: lowres_convolved = auto_dask_map(self.highres, blocks=block, args=[conv_kernel_low]) # for chan in range(low_chans): # if verbose: # print("On Channel: "+str(chan)+" of "+str(low_chans)) # lowres_convolved[chan, :, :] = \ # convolve_fft(self.lowres.filled_data[chan, :, :], # conv_kernel_low, boundary='fill', # interpolate_nan=True, normalize_kernel=True) update_low_hdr = \ _update_beam_in_hdr(self.lowres.header, self.combined_beam) self.lowres_convolved = \ SpectralCube(lowres_convolved*self.lowres.unit, self.lowres.wcs, header=update_low_hdr) def flux_recovered(self, plot=True, filename=None, enable_interp=True, interp_to='lower', diff_tol=1e-4*u.m/u.s): ''' Check the amount of flux recovered in the high resolution image versus the low resolution data. If the spectral axes don't match, one is interpolated onto the other. Parameters ---------- plot : bool, optional Enable plotting. filename : str, optional Give filename for the plot save file. When specified, the plot is automatically saved. interp_to : 'lower' or 'upper', optional If the spectral axes don't match, interpolated onto the same. The default 'lower' interpolates to the spectral axis with the lowest resolution. ''' # Add up the total intensity in the cubes and compare # Assumes that there is some masking such that noise # doesn't dominate if self.highres_convolved is not None: high_channel_intensity = \ self.highres_convolved.sum(axis=(1, 2)) else: high_channel_intensity = self.highres.sum(axis=(1, 2)) Warning("Should run convolve_to_common before. Using unconvolved" " cube.") if self.lowres_convolved is not None: low_channel_intensity = self.lowres_convolved.sum(axis=(1, 2)) else: low_channel_intensity = self.lowres.sum(axis=(1, 2)) Warning("Should run convolve_to_common before. Using unconvolved" " cube.") # If the spectral axes are not the same, try interpolating to match if enable_interp: better_vres_high = \ np.abs(self.lowres.header['CDELT3']) > \ np.abs(self.highres.header['CDELT3']) spec_diff = np.abs(np.abs(self.lowres.header['CDELT3']) - np.abs(self.highres.header['CDELT3'])) spec_unit = self.highres.spectral_axis.unit if spec_diff * spec_unit < diff_tol: warnings.warn("Channels are essentially the same. " "Skipping interpolation. Lower diff_tol" " to re-enable interpolation.") else: # Invert which to interpolate to if interp_to == "higher": better_vres_high = not better_vres_high if better_vres_high: f = interp1d(np.round(self.highres.spectral_axis.value, 3), high_channel_intensity.value) high_channel_intensity = \ f(np.round(self.lowres.spectral_axis.value, 3)) *\ self.highres.unit else: f = interp1d(np.round(self.lowres.spectral_axis.value, 3), low_channel_intensity.value) low_channel_intensity = \ f(np.round(self.highres.spectral_axis.value, 3)) *\ self.lowres.unit self.fraction_flux_recovered = \ high_channel_intensity.sum()/low_channel_intensity.sum() if plot: p.plot(self.highres.spectral_axis.value, high_channel_intensity.value) p.plot(self.lowres.spectral_axis.value, low_channel_intensity.value) p.xlabel("Spectral Axis ("+self.highres.spectral_axis.unit.to_string()+")") p.ylabel("Intensity ("+self.highres.unit.to_string()+")") if filename is None: p.show() else: p.savefig(filename) def run_all(self, highres_mask=None, lowres_mask=None, plot=True, unit=u.K, freq=1420.40575177*u.MHz, filename=True, use_dask=False, verbose=True): ''' Run the complete comparisons. ''' self.apply_mask(highres_mask=highres_mask, lowres_mask=lowres_mask) self.match_coords() self.convert_to(unit=unit, freq=freq) self.convolve_to_common(verbose=verbose, use_dask=use_dask) self.flux_recovered(plot=plot, filename=filename)
denscube = SpectralCube.read(paths.dpath("H2CO_ParameterFits_{0}dens.fits".format(meastype))) denscube = denscube.with_mask(okmask) colcube = SpectralCube.read(paths.dpath("H2CO_ParameterFits_{0}col.fits".format(meastype))) colcube = colcube.with_mask(okmask) goodmask_std = stdcube < 0.5 flathead = denscube.wcs.dropaxis(2).to_header() denscube_lin = SpectralCube(10**denscube.filled_data[:].value, wcs=denscube.wcs, mask=okmask) colcube_lin = SpectralCube(10**colcube.filled_data[:].value, wcs=denscube.wcs, mask=okmask) totalcol = colcube_lin.sum(axis=0) totalcolgood = colcube_lin.with_mask(goodmask).sum(axis=0) totalcolgoodstd = colcube_lin.with_mask(goodmask_std).sum(axis=0) denscol = SpectralCube(denscube_lin.filled_data[:] * colcube_lin.filled_data[:], wcs=denscube.wcs, mask=okmask) wtdmeandens = np.log10(denscol.sum(axis=0) / totalcol) mindens_std = denscube.with_mask(goodmask_std).min(axis=0) mindens_chi2 = denscube.with_mask(goodmask).min(axis=0) hdu1 = fits.PrimaryHDU(data=wtdmeandens.value, header=flathead) hdu1.writeto(paths.dpath("H2CO_ParameterFits_weighted_mean_{0}_density.fits".format(meastype)), clobber=True) masked_wtdmeans = np.log10(denscol.with_mask(goodmask).sum(axis=0) / totalcolgood)