def regridData(baseCubeFits, otherDataFits, outDir, mask=False): ''' regrids one data set to match the wcs of the base data set, which is assumed to be a cube. The regridded data set can be either 2d or 3d. ''' # open the base cube try: baseCube = SpectralCube.read(baseCubeFits) except: print("Can't read in " + baseCubeFits + ".") # determine ndim and bunit for other data sets f = fits.open(otherDataFits) ndim = f[0].header['NAXIS'] bunit = f[0].header['BUNIT'] f.close() # output image name newFits = os.path.join( outDir, os.path.basename(otherDataFits).replace('.fits', '_regrid.fits')) # now regrid images appropriately if ndim == 3: # read in cube otherCube = SpectralCube.read(otherDataFits) # interpolate velocity axis. This needs to be done first. regridCube = otherCube.spectral_interpolate(baseCube.spectral_axis) regridCube.allow_huge_operations = True newCube = regridCube.reproject(baseCube.header) if mask: # if greater than 0.0 set value to 1. otherwise 0. newdata = np.where(newCube.unitless_filled_data[:, :, :] > 0.0, 1, 0) finalCube = SpectralCube(newdata, newCube.wcs, mask=baseCube.mask) else: newdata = newCube.filled_data[:, :, :] finalCube = SpectralCube(newdata, newCube.wcs, mask=baseCube.mask) finalCube.with_spectral_unit(baseCube.spectral_axis.unit).write( newFits, overwrite=True) elif ndim == 2: ## consider using Projection.from_hdu then Projection.reproject here? # regrid image newcube = reproject_interp( otherDataFits, output_projection=baseCube.wcs.dropaxis(2), shape_out=baseCube.wcs.dropaxis(2).array_shape, order='nearest-neighbor', return_footprint=False) if mask: # set to 1 where greater than 0.0. newdata = np.where(newcube > 0.0, 1.0, 0.0) else: newdata = newcube # get mask on original data baseMask = baseCube.get_mask_array() totalBaseMask = baseMask.max(axis=0) newdata[np.invert(totalBaseMask)] = np.nan # fix up header with input image units outHdr = baseCube.wcs.dropaxis(2).to_header() outHdr.append(('BUNIT', bunit)) # write out regridded data fits.writeto(newFits, newdata, outHdr, overwrite=True) else: print("Number of dimensions of other data set is not 2 or 3.")
def rebaseline(filename, blorder=3, baselineRegion=[slice(0, 262, 1), slice(-512, 0, 1)], windowFunction=None, blankBaseline=False, flagSpike=True, v0=None, **kwargs): """ Rebaseline a data cube using robust regression of Legendre polynomials. Parameters ---------- filename : string FITS filename of the data cube blorder : int Order of the polynomial to fit to the data baselineRegion : list List of slices defining the default region of the spectrum, in channels, to be used for the baseline fitting. windowFunction : function Name of function to be used that will accept spectrum data, and velocity axis and will return a binary mask of the channels to be used in the baseline fitting. Extra **kwargs are passed to windowFunction to do with as it must. blankBaseline : boolean Blank the baseline region on a per-spectrum basis Returns ------- Nothing. A new FITS file is written out with the suffix 'rebaseN' where N is the baseline order """ cube = SpectralCube.read(filename) originalUnit = cube.spectral_axis.unit cube = cube.with_spectral_unit(u.km / u.s, velocity_convention='radio') spaxis = cube.spectral_axis.to(u.km / u.s).value goodposition = np.isfinite(cube.apply_numpy_function(np.max, axis=0)) y, x = np.where(goodposition) outcube = np.zeros(cube.shape) * np.nan RegionName = (filename.split('_'))[0] if hasattr(windowFunction, '__call__'): catalog = catalogs.GenerateRegions() nuindex = np.arange(cube.shape[0]) runmin = nuindex[-1] runmax = nuindex[0] for thisy, thisx in console.ProgressBar(zip(y, x)): spectrum = cube[:, thisy, thisx].value if v0 is not None: baselineIndex = windowFunction(spectrum, spaxis, v0=v0, **kwargs) elif hasattr(windowFunction, '__call__'): _, Dec, RA = cube.world[0, thisy, thisx] # This determines a v0 appropriate for the region v0 = VlsrByCoord(RA.value, Dec.value, RegionName, regionCatalog=catalog) baselineIndex = windowFunction(spectrum, spaxis, v0=v0, **kwargs) else: baselineIndex = np.zeros_like(spectrum,dtype=np.bool) for ss in baselineRegion: baselineIndex[ss] = True runmin = np.min([nuindex[baselineIndex].min(), runmin]) runmax = np.max([nuindex[baselineIndex].max(), runmax]) # Use channel-to-channel difference as the noise value. if flagSpike: jumps = (spectrum - np.roll(spectrum, -1)) noise = mad1d(jumps) * 2**(-0.5) baselineIndex *= (np.abs(jumps) < 5 * noise) noise = mad1d((spectrum - np.roll(spectrum, -2))[baselineIndex]) * 2**(-0.5) else: noise = mad1d((spectrum - np.roll(spectrum, -2))[baselineIndex]) * 2**(-0.5) if blankBaseline: spectrum = robustBaseline(spectrum, baselineIndex, blorder=blorder, noiserms=noise) spectrum[baselineIndex] = np.nan outcube[:, thisy, thisx] = spectrum else: outcube[:, thisy, thisx] = robustBaseline(spectrum, baselineIndex, blorder=blorder, noiserms=noise) outsc = SpectralCube(outcube, cube.wcs, header=cube.header) outsc = outsc[runmin:runmax, :, :] # cut beyond baseline edges # Return to original spectral unit outsc = outsc.with_spectral_unit(originalUnit) outsc.write(filename.replace('.fits', '_rebase{0}.fits'.format(blorder)), overwrite=True)
def rebaseline(filename, blorder=3, baselineRegion=[slice(0, 800, 1), slice(-800, 0, 1)], windowFunction=None, blankBaseline=False, flagSpike=True, v0=None, VlsrByCoord=None, verbose=False, **kwargs): """ Rebaseline a data cube using robust regression of Legendre polynomials. Parameters ---------- filename : string FITS filename of the data cube blorder : int Order of the polynomial to fit to the data baselineRegion : list List of slices defining the default region of the spectrum, in channels, to be used for the baseline fitting. windowFunction : function Name of function to be used that will accept spectrum data, and velocity axis and will return a binary mask of the channels to be used in the baseline fitting. Extra **kwargs are passed to windowFunction to do with as it must. blankBaseline : boolean Blank the baseline region on a per-spectrum basis Returns ------- Nothing. A new FITS file is written out with the suffix 'rebaseN' where N is the baseline order """ cube = SpectralCube.read(filename) originalUnit = cube.spectral_axis.unit cube = cube.with_spectral_unit(u.km / u.s, velocity_convention='radio') spaxis = cube.spectral_axis.to(u.km / u.s).value goodposition = np.isfinite(cube.apply_numpy_function(np.max, axis=0)) y, x = np.where(goodposition) outcube = np.zeros(cube.shape) * np.nan RegionName = (filename.split('/'))[-1] RegionName = (RegionName.split('_'))[0] nuindex = np.arange(cube.shape[0]) runmin = nuindex[-1] runmax = nuindex[0] if verbose: pb = console.ProgressBar(len(y)) for thisy, thisx in zip(y, x): spectrum = cube[:, thisy, thisx].value if v0 is not None: baselineIndex = windowFunction(spectrum, spaxis, v0=v0, **kwargs) elif hasattr(windowFunction, '__call__') and \ hasattr(VlsrByCoord, '__call__'): _, Dec, RA = cube.world[0, thisy, thisx] # This determines a v0 appropriate for the region v0 = VlsrByCoord(RA.value, Dec.value, RegionName, **kwargs) baselineIndex = windowFunction(spectrum, spaxis, v0=v0, **kwargs) else: baselineIndex = np.zeros_like(spectrum, dtype=np.bool) for ss in baselineRegion: baselineIndex[ss] = True runmin = np.min([nuindex[baselineIndex].min(), runmin]) runmax = np.max([nuindex[baselineIndex].max(), runmax]) # Use channel-to-channel difference as the noise value. if flagSpike: jumps = (spectrum - np.roll(spectrum, -1)) noise = mad1d(jumps) * 2**(-0.5) baselineIndex *= (np.abs(jumps) < 5 * noise) noise = mad1d( (spectrum - np.roll(spectrum, -2))[baselineIndex]) * 2**(-0.5) else: noise = mad1d( (spectrum - np.roll(spectrum, -2))[baselineIndex]) * 2**(-0.5) if blankBaseline: spectrum = robustBaseline(spectrum, baselineIndex, blorder=blorder, noiserms=noise) spectrum[baselineIndex] = np.nan outcube[:, thisy, thisx] = spectrum else: outcube[:, thisy, thisx] = robustBaseline(spectrum, baselineIndex, blorder=blorder, noiserms=noise) if verbose: pb.update() outsc = SpectralCube(outcube, cube.wcs, header=cube.header, meta={'BUNIT': cube.header['BUNIT']}) outsc = outsc[runmin:runmax, :, :] # cut beyond baseline edges # Return to original spectral unit outsc = outsc.with_spectral_unit(originalUnit) outsc.write(filename.replace('.fits', '_rebase{0}.fits'.format(blorder)), overwrite=True)
def get_best_2comp_residual(reg): modbest = get_best_2comp_model(reg) best_res = reg.ucube.cube._data - modbest res_cube = SpectralCube(best_res, reg.ucube.cube.wcs) res_cube = res_cube.with_spectral_unit(u.km / u.s, velocity_convention='radio') return res_cube
def save_best_2comp_fit(reg): # should be renamed to determine_best_2comp_fit or something along that line # currently use np.nan for pixels with no models ncomp = [1, 2] # ideally, a copy function should be in place of reloading # a new Region object is created start fresh on some of the functions (e.g., aic comparison) reg_final = Region(reg.cubePath, reg.paraNameRoot, reg.paraDir) # start out clean, especially since the deepcopy function doesn't work well for pyspeckit cubes # load the file based on the passed in reg, rather than the default for nc in ncomp: if not str(nc) in reg.ucube.pcubes: reg_final.load_fits(ncomp=[nc]) else: # load files using paths from reg if they exist print("loading model from: {}".format(reg.ucube.paraPaths[str(nc)])) reg_final.ucube.load_model_fit(filename=reg.ucube.paraPaths[str(nc)], ncomp=nc) pcube_final = reg_final.ucube.pcubes['2'].copy('deep') # make the 2-comp para maps with the best fit model lnk21 = reg_final.ucube.get_AICc_likelihood(2, 1) mask = lnk21 > 5 print("2comp pix: {}".format(np.sum(mask))) pcube_final.parcube[:4, ~mask] = reg_final.ucube.pcubes['1'].parcube[:4, ~mask].copy() pcube_final.errcube[:4, ~mask] = reg_final.ucube.pcubes['1'].errcube[:4, ~mask].copy() pcube_final.parcube[4:8, ~mask] = np.nan pcube_final.errcube[4:8, ~mask] = np.nan lnk10 = reg_final.ucube.get_AICc_likelihood(1, 0) mask = lnk10 > 5 pcube_final.parcube[:, ~mask] = np.nan pcube_final.errcube[:, ~mask] = np.nan # use the default file formate to save the finals nc = 2 if not str(nc) in reg_final.ucube.paraPaths: reg_final.ucube.paraPaths[str(nc)] = '{}/{}_{}vcomp.fits'.format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot, nc) savename = "{}_final.fits".format(os.path.splitext(reg_final.ucube.paraPaths['2'])[0]) UCube.save_fit(pcube_final, savename=savename, ncomp=2) hdr2D =reg.ucube.cube.wcs.celestial.to_header() # save the lnk21 map savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para","lnk21")) save_map(lnk21, hdr2D, savename) # save the lnk10 map savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para","lnk10")) save_map(lnk10, hdr2D, savename) # create and save the lnk20 map for reference: lnk20 = reg_final.ucube.get_AICc_likelihood(2, 0) savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para","lnk20")) save_map(lnk20, hdr2D, savename) # save the SNR map snr_map = get_best_2comp_snr_mod(reg_final) savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para","SNR")) save_map(snr_map, hdr2D, savename) # create moment0 map modbest = get_best_2comp_model(reg_final) cube_mod = SpectralCube(data=modbest, wcs=reg_final.ucube.pcubes['2'].wcs.copy(), header=reg_final.ucube.pcubes['2'].header.copy()) # make sure the spectral unit is in km/s before making moment maps cube_mod = cube_mod.with_spectral_unit('km/s', velocity_convention='radio') mom0_mod = cube_mod.moment0() savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para", "model_mom0")) mom0_mod.write(savename, overwrite=True) # created masked mom0 map with model as the mask mom0 = UCube.get_masked_moment(cube=reg_final.ucube.cube, model=modbest, order=0, expand=20, mask=None) savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para", "mom0")) mom0.write(savename, overwrite=True) # save reduced chi-squred maps # would be useful to check if 3rd component is needed savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para", "chi2red_final")) chi_map = UCube.get_chisq(cube=reg_final.ucube.cube, model=modbest, expand=20, reduced=True, usemask=True, mask=None) save_map(chi_map, hdr2D, savename) # save reduced chi-squred maps for 1 comp and 2 comp individually chiRed_1c = reg_final.ucube.get_reduced_chisq(1) chiRed_2c = reg_final.ucube.get_reduced_chisq(2) savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para", "chi2red_1c")) save_map(chiRed_1c, hdr2D, savename) savename = "{}/{}.fits".format(reg_final.ucube.paraDir, reg_final.ucube.paraNameRoot.replace("para", "chi2red_2c")) save_map(chiRed_2c, hdr2D, savename) return reg
# --------------------------------------------------- # # Find the (col,row) min/max limits of valid spaxels. # # Min/max will be used to crop the data and the WCS. # # --------------------------------------------------- # wcsMinMax = pixMinMax(validPixels) # --------------------------------------------- # # Build the spectral cube using the contSubCube # # --------------------------------------------- # # NaNs and data to be ignored are FALSE. specCube = SpectralCube(data,pacsWcs,mask=cubeMask,fill_value=1.) # For convenience, convert the X-axis to km/s # (WCSLIB automatically converts to m/s even if you give it km/s) specCube = specCube.with_spectral_unit(u.km/u.s) # --------------------------------------------- # # Build the pySpecCube using the spectral cube. # # --------------------------------------------- # # NaN values are FALSE in the mask. pyCube = pyspeckit.Cube(cube=specCube,maskmap=np.ma.getmask(falseMask[mid,:,:])) # -------------------------------- # # Set up for line profile fitting. # # -------------------------------- # # Build the guesses array gc = buildGuessCube((nCols,nRows),
def deblend(para, specCubeRef, vmin=4.0, vmax=11.0, f_spcsamp=None, tau_wgt=0.1, n_cpu=None): ''' Deblend hyperfine structures in a cube based on fitted models, i.e., reconstruct the fitted model with Gaussian lines with optical depths accounted for (e.g., similar to CO (J = 0-1)) :param para: <ndarray> The fitted parameters in the order of vel, width, tex, and tau for each velocity slab. (Note: the size of the z axis for para must thus be in the multiple of 4) :param specCubeRef: <SpectralCube.Cube> The reference cube from which the deblended cube is constructed from :param vmin: <float> The lower veloicty limit on the deblended cube in the unit of km/s :param vmax: <float> The upper veloicty limit on the deblended cube in the unit of km/s :param f_spcsamp: <int> The scaling factor for the spectral sampling relative the reference cube (e.g., f_spcsamp = 2 give you twice the spectral resolution) :param tau_wgt: The scaling factor for the input tau (e.g., tau_wgt = 0.1 better represents the true optical depth of a NH3 (1,1) hyperfine group than the "fitted tau") :param n_cpu: <int> The number of cpus to use. If None, defaults to all the cpus available minus one. :return mcube: <SpectralCube.Cube> The deblended cube ''' # open the reference cube file cube = specCubeRef cube = cube.with_spectral_unit(u.km / u.s, velocity_convention='radio') # trim the cube to the specified velocity range cube = cube.spectral_slab(vmin * u.km / u.s, vmax * u.km / u.s) # generate an empty SpectralCube to house the deblended cube if f_spcsamp is None: deblend = np.zeros(cube.shape) hdr = cube.wcs.to_header() wcs_new = cube.wcs else: deblend = np.zeros( (cube.shape[0] * int(f_spcsamp), cube.shape[1], cube.shape[2])) wcs_new = cube.wcs.deepcopy() # adjust the spectral reference value wcs_new.wcs.crpix[2] = wcs_new.wcs.crpix[2] * int(f_spcsamp) # adjust the spaxel size wcs_new.wcs.cdelt[2] = wcs_new.wcs.cdelt[2] / int(f_spcsamp) hdr = wcs_new.to_header() # retain the beam information hdr['BMAJ'] = cube.header['BMAJ'] hdr['BMIN'] = cube.header['BMIN'] hdr['BPA'] = cube.header['BPA'] mcube = SpectralCube(deblend, wcs_new, header=hdr) # convert back to an unit that the ammonia hf model can handle (i.e. Hz) without having to create a # pyspeckit.spectrum.units.SpectroscopicAxis object (which runs rather slow for model building in comparison) mcube = mcube.with_spectral_unit(u.Hz, velocity_convention='radio') xarr = mcube.spectral_axis yy, xx = np.indices(para.shape[1:]) # a pixel is valid as long as it has a single finite value isvalid = np.any(np.isfinite(para), axis=0) valid_pixels = zip(xx[isvalid], yy[isvalid]) def model_a_pixel(xy): x, y = int(xy[0]), int(xy[1]) # nh3_vtau_singlemodel_deblended takes Hz as the spectral unit models = [ nh3_deblended.nh3_vtau_singlemodel_deblended(xarr, Tex=tex, tau=tau * tau_wgt, xoff_v=vel, width=width) for vel, width, tex, tau in zip(para[::4, y, x], para[1::4, y, x], para[2::4, y, x], para[3::4, y, x]) ] mcube._data[:, y, x] = np.nansum(np.array(models), axis=0) return ((x, y), mcube._data[:, y, x]) if n_cpu is None: n_cpu = cpu_count() - 1 else: n_cpu = 1 if n_cpu > 1: print("------------------ deblending cube -----------------") print("number of cpu used: {}".format(n_cpu)) sequence = [(x, y) for x, y in valid_pixels] result = parallel_map(model_a_pixel, sequence, numcores=n_cpu) merged_result = [ core_result for core_result in result if core_result is not None ] for mr in merged_result: ((x, y), model) = mr x = int(x) y = int(y) mcube._data[:, y, x] = model else: for xy in ProgressBar(list(valid_pixels)): model_a_pixel(xy) # convert back to km/s in units before saving mcube = mcube.with_spectral_unit(u.km / u.s, velocity_convention='radio') gc.collect() print("--------------- deblending completed ---------------") return mcube