def update_rest_moment0(region_name='L1688', file_extension='DR1_rebase3', v_mean=2.5, sigma_v=0.3): """ Update moment calculation for non-NH3 lines. Don't base on NH3 fit; these lines may be offset in velocity. Right now including velocity ranges by hand. """ for line in ['HC5N','HC7N_21_20','HC7N_22_21','C2S','NH3_33']: file_in ='{0}/{0}_{2}_{1}.fits'.format(region_name,file_extension,line) file_out='{0}/{0}_{2}_{1}_mom0_QA.fits'.format(region_name,file_extension,line) file_rms='{0}/{0}_{2}_{1}_rms_QA.fits'.format(region_name,file_extension,line) # Load pyspeckit cube pycube = pyspeckit.Cube(file_in) # Use spectral cube to calculate integrated intensity maps cube_raw = SpectralCube.read(file_in) # in km/s not Hz cube = cube_raw.with_spectral_unit(u.km / u.s,velocity_convention='radio') vaxis=cube.spectral_axis dv=np.abs(vaxis[1]-vaxis[0]) vmean = v_mean*u.km/u.s sigmav = sigma_v*u.km/u.s total_spc=np.sqrt( (vaxis-vmean)**2)/sigmav < 3 mask3d = np.ones(cube.shape,dtype=bool) for ii in np.arange(cube.shape[2]): for jj in np.arange(cube.shape[1]): mask3d[:,jj,ii] = total_spc # create masked cube cube2 = cube.with_mask(mask3d) cube3 = cube.with_mask(~mask3d) # calculate moment map moment_0 = cube2.moment(axis=0) moment_0.write( file_out, overwrite=True) rms=cube3.std(axis=0) rms.write( file_rms, overwrite=True)
def __init__(self, *args): """ A collection of Specfit objects mapped to SubCubes by the mapper method. Includes* methods to fit multiple guess grids for different models, and the means to decide between the results of those fits. *In a distant[citation needed] future. Input parameters: see ~pyspeckit.Cube """ # parent cube, used for attribute propagation self.supercube = pyspeckit.Cube(*args) # making a bunch of references to make our life easier self.cube = self.SuperCube.cube self.xarr = self.SuperCube.xarr self.header = self.SuperCube.header # FIXME: rewrite mapplot to include the mapper/judge methods! # doesn't work in its current implementation, will need # to rewire different params and errors, for variable # number of parameters across different models self.multiplot = self.SuperCube.mapplot # MultiCube's own instances: self.multigrid = {} self.tesseract = {}
def test_load_cube_from_spectralcube(): sc = make_fake_cube() pcube = pyspeckit.Cube(cube=sc) assert pcube.unit == 'K' assert pcube.xarr.unit == 'km/s' return pcube
def make_cube(files=(file_nh311_dr1, file_nh322_dr1), rms_files=(file_rms_nh311_dr1, file_rms_nh322_dr1)): """ Opens the cube and calculates all the pre-fitting attributes of interest. """ # make sure we're working on arrays (why?) files = np.atleast_1d([f for f in files]) rms_files = np.atleast_1d([f for f in rms_files]) if files.size > 1: spc_dict = {f: pyspeckit.Cube(f) for f in files} rmsmaps = {f: fits.getdata(ef) for f, ef in zip(files, rms_files)} for f in files: spc_dict[f].errorcube = np.repeat([rmsmaps[f]], spc_dict[f].xarr.size, axis=0) # now the errorcubes should merge automatically spc = pyspeckit.CubeStack([spc_dict[f] for f in files]) spc.xarr.refX = spc.cubelist[0].xarr.refX spc.xarr.refX_unit = spc.cubelist[0].xarr.refX_unit else: spc = pyspeckit.Cube(files[0]) rms = fits.getdata(rms_files[0]) # easier to handle everything get_spectrum-related spc.errorcube = np.repeat([rms], spc.xarr.size, axis=0) # I don't see a reason why errorcube should be a masked array if type(spc.errorcube) == np.ma.MaskedArray: spc.errorcube = np.array(spc.errorcube) spc.xarr.velocity_convention = 'radio' spc.xarr.convert_to_unit('km/s') snr = (spc.cube / spc.errorcube).max(axis=0) # TODO: fix multinest-pipeline.py and run_multicube.py #spc.errmap = rms spc.snrmap = snr return spc
def velocity(cube, wavelength, velocity_range, multicore, outfile): ''' cube: str The cube path and filename, e.g. path_to_cube/cube_filename.fits wavelength: str The line one wishes to compute the velocity map for, e.g. Ha, SII6717, or stacked for the stacked version. Default is stacked. Available lines are SII6717, SII6731, Ha, NII6548, NII6584, Hb, OIII5007, SIII9068, OI6300. velocity_range: array The velocity range, e.g. [-200,200]. Default is [-300,300]. multicore: int The number of cores for the parallelization. Default is 3. outfile: str The name of the output file. Default is wavelength+'_velocity.fits'. ''' cube = spectral_cube.SpectralCube.read(str(cube), hdu=1) hd = cube.header ## NEED TO IMPLEMENT CONTINUUM SUBTRACTION BASED ON SPECIFIED WAVELENGTH ## slabs = [ cube.with_spectral_unit(u.km / u.s, 'optical', wl).spectral_slab( velocity_range[0] * u.km / u.s, velocity_range[1] * u.km / u.s) for wl in wavelength ] newcube_shape = (sum(s.shape[0] for s in slabs), ) + slabs[0].shape[1:] newcube_spaxis = np.concatenate([s.spectral_axis for s in slabs]).value * u.km / u.s sortvect = newcube_spaxis.argsort() sortspaxis = newcube_spaxis[sortvect] newcube = np.empty(newcube_shape) ind = 0 for ii, slab in enumerate(slabs): data = slab.filled_data[:] / slab.sum(axis=0) newcube[ind:ind + data.shape[0], :, :] = data ind += data.shape[0] supercube = newcube[sortvect, :, :] pxarr = pyspeckit.units.SpectroscopicAxis(sortspaxis.value, unit='km/s') print pxarr pcube = pyspeckit.Cube(cube=supercube, xarr=pxarr, header=hd) pcube.fiteach(fittype='gaussian', guesses=[1 / np.sqrt(np.pi), 5, 20.0], errmap=np.ones(supercube.shape[1:]) / 100., multicore=3) ff = fits.PrimaryHDU(data=pcube.parcube[1], header=hd) ff.writeto(outfile, clobber=True)
def load_model_fit(cube, filename, ncomp): # currently only loads ammonia multi-component model pcube = pyspeckit.Cube(cube=cube) # reigster fitter linename = 'oneone' import ammonia_multiv as ammv fitter = ammv.nh3_multi_v_model_generator(n_comp=ncomp, linenames=[linename]) pcube.specfit.Registry.add_fitter('nh3_multi_v', fitter, fitter.npars) pcube.load_model_fit(filename, npars=fitter.npars, fittype='nh3_multi_v') gc.collect() return pcube
def get_subregion_pcube(cube303m, cube303, cube321, region): #scube = cube_merge_high.subcube_from_ds9region(pyregion.ShapeList([region])) scube303m = cube303m.subcube_from_ds9region(pyregion.ShapeList([region])) scube303 = cube303.subcube_from_ds9region(pyregion.ShapeList([region])) scube321 = cube321.subcube_from_ds9region(pyregion.ShapeList([region])) # TODO: get error map #pcube = pyspeckit.Cube(cube=scube) pcube303 = pyspeckit.Cube(cube=scube303) pcube303.xarr.refX = cube303.wcs.wcs.restfrq pcube303.xarr.refX_unit = 'Hz' pcube321 = pyspeckit.Cube(cube=scube321) pcube321.xarr.refX = cube321.wcs.wcs.restfrq pcube321.xarr.refX_unit = 'Hz' pcube = pyspeckit.CubeStack([ pcube303, pcube321, ]) pcube.specfit.Registry.add_fitter('h2co_simple', simple_fitter3, 4, multisingle='multi') pcube.xarr.refX = cube303m.wcs.wcs.restfrq pcube.xarr.refX_unit = 'Hz' return pcube, scube303m
def multiscale_fit_clouddn(center=(643 - 1, 164 - 1)): clouddn_slice = np.s_[:, center[1] - 16:center[1] + 16, center[0] - 16:center[0] + 16] clouddn_cube = cube_merge_high[clouddn_slice] clouddn_pcube = pyspeckit.Cube(cube=clouddn_cube) clouddn_pcube.xarr.refX = clouddn_cube.wcs.wcs.restfrq return multiscale_fit(clouddn_pcube, 16, 16, savedir='clouddn', savepre='clouddn', guesses=[0.1, 19, 8, 0.3, 0.9, 0.2], offset_scale=0.4)
def multiscale_fit_g1pt2_cool(center=(236, 95)): # G1.23-0.08box g1pt2_cool_slice = np.s_[:, center[1] - 16:center[1] + 16, center[0] - 16:center[0] + 16] g1pt2_cool_cube = cube_merge_high[g1pt2_cool_slice] g1pt2_cool_pcube = pyspeckit.Cube(cube=g1pt2_cool_cube) g1pt2_cool_pcube.xarr.refX = g1pt2_cool_cube.wcs.wcs.restfrq return multiscale_fit(g1pt2_cool_pcube, 16, 16, savedir='g1.2', savepre='g1.2_coolspot', guesses=[0.1, 91, 13.6, 0.3, 0.9, 0.2], offset_scale=0.4)
def multiscale_fit_g08south_hot(center=(434, 63)): g08south_slice = np.s_[:, center[1] - 16:center[1] + 16, center[0] - 16:center[0] + 16] g08south_cube = cube_merge_high[g08south_slice] g08south_pcube = pyspeckit.Cube(cube=g08south_cube) g08south_pcube.xarr.refX = g08south_cube.wcs.wcs.restfrq multiscale_fit(g08south_pcube, 16, 16, savedir='g0.8_south', savepre='g0.8_south_hotspot', guesses=[0.5, 40.3, 7.9, 0.57, 0.5, 0.3], offset_scale=0.4)
def multiscale_fit_g08south_cool(center=(426, 36)): g08south_cool_slice = np.s_[:, center[1] - 16:center[1] + 16, center[0] - 16:center[0] + 16] g08south_cool_cube = cube_merge_high[g08south_cool_slice] g08south_cool_pcube = pyspeckit.Cube(cube=g08south_cool_cube) g08south_cool_pcube.xarr.refX = g08south_cool_cube.wcs.wcs.restfrq multiscale_fit(g08south_cool_pcube, 16, 16, savedir='g0.8_south', savepre='g0.8_south_coolspot', guesses=[0.5, 50.3, 10.6, 0.3, 0.9, 0.2] + [0.3, 96.3, 3.6, 0.3, 0.9, 0.2], offset_scale=0.4)
def multiscale_fit_g1pt6(center=(53, 143)): g1pt6_slice = np.s_[:, center[1] - 16:center[1] + 16, center[0] - 16:center[0] + 16] g1pt6_cube = cube_merge_high[g1pt6_slice] g1pt6_pcube = pyspeckit.Cube(cube=g1pt6_cube) g1pt6_pcube.xarr.refX = g1pt6_cube.wcs.wcs.restfrq return multiscale_fit(g1pt6_pcube, 16, 16, savedir='g1.6', savepre='g1.6_spot', guesses=[0.1, 157, 8, 0.3, 0.9, 0.2] + [0.1, 60, 9, 0.3, 0.9, 0.2], offset_scale=0.4)
def pyspeckit_fit_cube(cube, max_map, centroid_map, width_map, noisemap, lines, vz): """ This is experimental and doesn't really work: the idea here is to fit all lines in the cube simultaneously. """ import pyspeckit vz = u.Quantity(vz, u.km / u.s) fcube = cube.with_spectral_unit(u.GHz) def inrange(x): return (x < fcube.spectral_extrema[1] and x > fcube.spectral_extrema[0]) lines_in_cube = { linename: linedata for linename, linedata in lines.items() if inrange(linedata['frequency'] * (1 - vz / constants.c)) } frequencies = sorted(linedata['frequency'] for linedata in lines_in_cube.values()) line_guesses = [[ max_map.value, ((1 - centroid_map / constants.c) * frq).to(u.GHz).value, ((width_map / constants.c) * frq).to(u.GHz).value ] for frq in frequencies] line_guesses = np.array([x for y in line_guesses for x in y]) guesses = np.array([max_map.value, centroid_map.value, width_map.value]) #vcube = cube.with_spectral_unit(u.km/u.s, velocity_convention='optical') pcube = pyspeckit.Cube(cube=fcube) pcube.mapplot.plane = max_map.value pcube.fiteach(guesses=guesses, start_from_point=(150, 150), errmap=noisemap.value)
# it's barely worth it; cuts off 10% of pixels f1 = all_lines['H2CO_303_202']*u.GHz f2 = all_lines['H2CO_321_220']*u.GHz h2co_cube_merge_high = cube_merge_high.spectral_slab(f1*(1-(150*u.km/u.s/constants.c)), f2*(1+(100*u.km/u.s/constants.c))) h2co_noise_cube = noise_spcube.spectral_slab(f1*(1-(150*u.km/u.s/constants.c)), f2*(1+(100*u.km/u.s/constants.c))) h2co_cube_merge_high_sm = cube_merge_high_sm.spectral_slab(f1*(1-(150*u.km/u.s/constants.c)), f2*(1+(100*u.km/u.s/constants.c))) h2co_noise_cube_sm = noise_spcube_sm.spectral_slab(f1*(1-(150*u.km/u.s/constants.c)), f2*(1+(100*u.km/u.s/constants.c))) # Pyspeckit cube made from spectralcube pcube_merge_high = pyspeckit.Cube(cube=h2co_cube_merge_high._data, errorcube=h2co_noise_cube._data, header=h2co_cube_merge_high.header, xarr=h2co_cube_merge_high.spectral_axis, ) pcube_merge_high.xarr.refX = 218.22219 pcube_merge_high.xarr.refX_unit = 'GHz' pcube_merge_high_sm = pyspeckit.Cube(cube=h2co_cube_merge_high_sm._data, errorcube=h2co_noise_cube_sm._data, header=h2co_cube_merge_high_sm.header, xarr=h2co_cube_merge_high_sm.spectral_axis, ) pcube_merge_high_sm.xarr.refX = 218.22219 pcube_merge_high_sm.xarr.refX_unit = 'GHz'
import os import astropy.units as u if not os.path.exists('n2hp_cube.fit'): import astropy.utils.data as aud from astropy.io import fits f = aud.download_file( 'ftp://cdsarc.u-strasbg.fr/pub/cats/J/A%2BA/472/519/fits/opha_n2h.fit') with fits.open(f) as ff: ff[0].header['CUNIT3'] = 'm/s' for kw in ['CTYPE4', 'CRVAL4', 'CDELT4', 'CRPIX4']: del ff[0].header[kw] ff.writeto('n2hp_cube.fit') # Load the spectral cube spc = pyspeckit.Cube('n2hp_cube.fit') # Register the fitter # The N2H+ fitter is 'built-in' but is not registered by default; this example # shows how to register a fitting procedure # 'multi' indicates that it is possible to fit multiple components and a # background will not automatically be fit 4 is the number of parameters in the # model (excitation temperature, optical depth, line center, and line width) spc.Registry.add_fitter('n2hp_vtau', pyspeckit.models.n2hp.n2hp_vtau_fitter, 4) # Get a measurement of the error per pixel errmap = spc.slice(20, 28, unit='km/s').cube.std(axis=0) # A good way to write a cube fitter is to have it load from disk if the cube # fit was completed successfully in the past if os.path.exists('n2hp_fitted_parameters.fits'):
fit_dir = 'fit/' # primary beam corrected file and in Kelvin units file_in_K = data_dir + 'pNH2D.fits' snr_min = 5 file_thick = fit_dir + 'pNH2D_fit_thick_par_snr{0}.fits'.format(snr_min) file_thin = fit_dir + 'pNH2D_fit_thin_par_snr{0}.fits'.format(snr_min) rms_file = data_dir + 'pNH2D_rms.fits' SNR_file = data_dir + 'pNH2D_SNR.fits' Tpeak_file = data_dir + 'pNH2D_Tpeak.fits' mask_file = data_dir + 'pNH2D_mask.fits' # rest-freq used in Harju et al. (2020) for ortho-NH2D freq_line = 110.153599 * u.GHz cube = pyspeckit.Cube(file_in_K) cube.xarr.refX = freq_line cube.xarr.velocity_convention = 'radio' cube.xarr.convert_to_unit('km/s') # # source dependent parameters # # pixel to start fit and to make sample plot of the line fit xmax = 269 ymax = 240 # velocity range with signal (of the main hf-component) to # calculate moment maps vmin = 3.4 vmax = 5.0 # default velocity used as guess in fit
simple_fitter2) from full_cubes import cube_merge_high from masked_cubes import (cube303, cube303sm, cube303m, cube321m, cube303msm, cube321msm, cube321, cube321sm) from noise import (noise, noise_cube, sm_noise, cube303nm, cube303nmsm, cube321nm, cube321nmsm) import pyspeckit from astrodendro import Dendrogram from astropy.utils.console import ProgressBar import paths import pylab as pl brick_slice = np.s_[:, 131:154, 710:740] brick_cube = cube_merge_high[brick_slice] brick_pcube = pyspeckit.Cube(cube=brick_cube) brick_pcube.xarr.refX = brick_cube.wcs.wcs.restfrq brick_pcube.specfit.Registry.add_fitter('h2co_simple', simple_fitter2, 6, multisingle='multi') b303m = cube303m[brick_slice] m1 = b303m.moment1(axis=0).to(u.km / u.s) # For radial multiscale fitting, see multiscale_fit.py def do_pyspeck_fits_1comp(): #guesses_simple = [1,25,5,0.5,0.7,1]
def hmm1_cubefit(vmin=3.4, vmax=5.0, tk_ave=10., do_plot=False, snr_min=5.0, multicore=1, do_thin=False): """ Fit NH3(1,1) and (2,2) cubes for H-MM1. It fits all pixels with SNR larger than requested. Initial guess is based on moment maps and neighboring pixels. The fitting can be done in parallel mode using several cores, however, this is dangerous for large regions, where using a good initial guess is important. It stores the result in a FITS cube. TODO: -convert FITS cube into several FITS files -Improve initial guess Parameters ---------- vmin : numpy.float Minimum centroid velocity to plot, in km/s. vmax : numpy.float Maximum centroid velocity to plot, in km/s. tk_ave : numpy.float Mean kinetic temperature of the region, in K. do_plot : bool If True, then a map of the region to map is shown. snr_min : numpy.float Minimum signal to noise ratio of the spectrum to be fitted. multicore : int Numbers of cores to use for parallel processing. """ cube11sc = SpectralCube.read(OneOneFile) cube22sc = SpectralCube.read(TwoTwoFile) cube11_v = cube11sc.with_spectral_unit(u.km / u.s, velocity_convention='radio', rest_value=freq11) cube22_v = cube22sc.with_spectral_unit(u.km / u.s, velocity_convention='radio', rest_value=freq22) from pyspeckit.spectrum.units import SpectroscopicAxis spec11 = SpectroscopicAxis(cube11_v.spectral_axis, refX=freq11, velocity_convention='radio') spec22 = SpectroscopicAxis(cube22_v.spectral_axis, refX=freq22, velocity_convention='radio') errmap11 = fits.getdata(RMSFile_11) errmap22 = fits.getdata(RMSFile_22) errmap_K = errmap11 #[errmap11, errmap22] Tpeak11 = fits.getdata(OneOnePeak) moment1 = fits.getdata(OneOneMom1) moment2 = (fits.getdata(OneOneMom2))**0.5 snr = cube11sc.filled_data[:].value / errmap11 peaksnr = Tpeak11 / errmap11 planemask = (peaksnr > snr_min) # *(errmap11 < 0.15) planemask = remove_small_objects(planemask, min_size=40) planemask = opening(planemask, disk(1)) #planemask = (peaksnr>20) * (errmap11 < 0.2) mask = (snr > 3) * planemask maskcube = cube11sc.with_mask(mask.astype(bool)) maskcube = maskcube.with_spectral_unit(u.km / u.s, velocity_convention='radio') slab = maskcube.spectral_slab(vmax * u.km / u.s, vmin * u.km / u.s) w11 = slab.moment(order=0, axis=0).value peakloc = np.nanargmax(w11) ymax, xmax = np.unravel_index(peakloc, w11.shape) moment2[np.isnan(moment2)] = 0.2 moment2[moment2 < 0.2] = 0.2 ## Load FITS files cube11 = pyspeckit.Cube(OneOneFile, maskmap=planemask) cube22 = pyspeckit.Cube(TwoTwoFile, maskmap=planemask) # Stack files cubes = pyspeckit.CubeStack([cube11, cube22], maskmap=planemask) cubes.unit = "K" # Define initial guess guesses = np.zeros((6, ) + cubes.cube.shape[1:]) moment1[moment1 < vmin] = vmin + 0.2 moment1[moment1 > vmax] = vmax - 0.2 guesses[0, :, :] = tk_ave # Kinetic temperature guesses[1, :, :] = 7 # Excitation Temp guesses[2, :, :] = 14.5 # log(column) guesses[ 3, :, :] = moment2 # Line width / 5 (the NH3 moment overestimates linewidth) guesses[4, :, :] = moment1 # Line centroid guesses[5, :, :] = 0.5 # F(ortho) - ortho NH3 fraction (fixed) if do_plot: import matplotlib.pyplot as plt plt.imshow(w11 * planemask, origin='lower') plt.show() print('start fit') cubes.specfit.Registry.add_fitter('cold_ammonia', ammonia.cold_ammonia_model(), 6) if do_thin: file_out = "{0}H-MM1_cold_parameter_maps_snr{1}_thin_v1.fits".format( fit_dir, snr_min) else: file_out = "{0}H-MM1_cold_parameter_maps_snr{1}_thick_v1.fits".format( fit_dir, snr_min) cubes.fiteach(fittype='cold_ammonia', guesses=guesses, integral=False, verbose_level=3, fixed=[do_thin, False, False, False, False, True], signal_cut=2, limitedmax=[True, False, False, False, True, True], maxpars=[20, 15, 20, 0.4, vmax, 1], limitedmin=[True, True, True, True, True, True], minpars=[5, 2.8, 12.0, 0.05, vmin, 0], start_from_point=(xmax, ymax), use_neighbor_as_guess=True, position_order=1 / peaksnr, errmap=errmap_K, multicore=multicore) # Store fits into FITS cube fitcubefile = fits.PrimaryHDU(data=np.concatenate( [cubes.parcube, cubes.errcube]), header=cubes.header) fitcubefile.header.set('PLANE1', 'TKIN') fitcubefile.header.set('PLANE2', 'TEX') fitcubefile.header.set('PLANE3', 'COLUMN') fitcubefile.header.set('PLANE4', 'SIGMA') fitcubefile.header.set('PLANE5', 'VELOCITY') fitcubefile.header.set('PLANE6', 'FORTHO') fitcubefile.header.set('PLANE7', 'eTKIN') fitcubefile.header.set('PLANE8', 'eTEX') fitcubefile.header.set('PLANE9', 'eCOLUMN') fitcubefile.header.set('PLANE10', 'eSIGMA') fitcubefile.header.set('PLANE11', 'eVELOCITY') fitcubefile.header.set('PLANE12', 'eFORTHO') fitcubefile.header.set('CDELT3', 1) fitcubefile.header.set('CTYPE3', 'FITPAR') fitcubefile.header.set('CRVAL3', 0) fitcubefile.header.set('CRPIX3', 1) fitcubefile.writeto(file_out, overwrite=True)
def cube_fit(cubefilename, outfilename, errfilename=None, scale_keyword=None, vheight=False, verbose=False, signal_cut=3, verbose_level=2, clobber=True, **kwargs): """ Light-weight wrapper for cube fitting Takes a cube and error map (error will be computed naively if not given) and computes moments then fits for each spectrum in the cube. It then saves the fitted parameters to a reasonably descriptive output file whose header will look like :: PLANE1 = 'amplitude' PLANE2 = 'velocity' PLANE3 = 'sigma' PLANE4 = 'err_amplitude' PLANE5 = 'err_velocity' PLANE6 = 'err_sigma' PLANE7 = 'integral' PLANE8 = 'integral_error' CDELT3 = 1 CTYPE3 = 'FITPAR' CRVAL3 = 0 CRPIX3 = 1 Parameters ---------- errfilename: [ None | string name of .fits file ] A two-dimensional error map to use for computing signal-to-noise cuts scale_keyword: [ None | Char ] Keyword to pass to the data cube loader - multiplies cube by the number indexed by this header kwarg if it exists. e.g., if your cube is in T_A units and you want T_A* vheight: [ bool ] Is there a background to be fit? Used in moment computation verbose: [ bool ] verbose_level: [ int ] How loud will the fitting procedure be? Passed to momenteach and fiteach signal_cut: [ float ] Signal-to-Noise ratio minimum. Spectra with a peak below this S/N ratio will not be fit and will be left blank in the output fit parameter cube clobber: [ bool ] Overwrite parameter .fits cube if it exists? `kwargs` are passed to :class:`pyspeckit.Spectrum.specfit` """ # Load the spectrum sp = pyspeckit.Cube(cubefilename, scale_keyword=scale_keyword) if os.path.exists(errfilename): errmap = pyfits.getdata(errfilename) else: # very simple error calculation... biased and bad, needs improvement # try using something from the cubes package errmap = sp.cube.std(axis=0) # Run the fitter sp.mapplot() # Compute the moments at each position to come up with reasonable guesses. # This speeds up the process enormously, but can easily mess up the fits if # there are bad pixels sp.momenteach(vheight=vheight, verbose=verbose) sp.fiteach(errmap=errmap, multifit=None, verbose_level=verbose_level, signal_cut=signal_cut, usemomentcube=True, blank_value=np.nan, verbose=verbose, **kwargs) # steal the header from the error map f = pyfits.open(cubefilename) # start replacing components of the pyfits object f[0].data = np.concatenate([sp.parcube, sp.errcube, sp.integralmap]) f[0].header['PLANE1'] = 'amplitude' f[0].header['PLANE2'] = 'velocity' f[0].header['PLANE3'] = 'sigma' f[0].header['PLANE4'] = 'err_amplitude' f[0].header['PLANE5'] = 'err_velocity' f[0].header['PLANE6'] = 'err_sigma' f[0].header['PLANE7'] = 'integral' f[0].header['PLANE8'] = 'integral_error' f[0].header['CDELT3'] = 1 f[0].header['CTYPE3'] = 'FITPAR' f[0].header['CRVAL3'] = 0 f[0].header['CRPIX3'] = 1 # save your work f.writeto(outfilename, clobber=clobber) return sp
import astropy import pyspeckit try: from astropy.io import fits as pyfits except ImportError: import pyfits import numpy as np # Load the spectrum sp = pyspeckit.Cube('region5_hcn_crop.fits') errmap = pyfits.getdata('region5.hcn.errmap.fits') # Register the fitter # The N2H+ fitter is 'built-in' but is not registered by default; this example # shows how to register a fitting procedure # 'multi' indicates that it is possible to fit multiple components and a background will not automatically be fit # 4 is the number of parameters in the model (excitation temperature, optical depth, line center, and line width) sp.Registry.add_fitter('hcn_amp', pyspeckit.models.hcn.hcn_amp, 3) # Run the fitter sp.mapplot() # use an individual spectrum selected semi-arbitrarily from the map to get an estimate of the error # this method has been rendered obsolete - use the error map instead #s = sp.get_spectrum(20,20) #s.plotter() #s.Registry = sp.Registry #s.specfit.Registry = sp.Registry #s.specfit(fittype='hcn_amp',guesses=[2.5,-5.6,1.5],show_components=True,debug=True,quiet=False) #s.specfit(fittype='hcn_amp',guesses=[2.5,-5.6,1.5],show_components=True,debug=True,quiet=False) #sp.error = s.specfit.errspec
# set up CASA-like shortcuts F=False; T=True # Some optional parameters for the script # (if False, it will try to load an already-stored version # of the file) fitcube = True # Mask out low S/N pixels (to speed things up) mask = pyfits.getdata('hotclump_11_mask.fits') mask = np.isfinite(mask) * (mask > 0) # Load the data using a mask # Then calibrate the data (the data we're loading in this case are in Janskys, # but we want surface brightness in Kelvin for the fitting process) cube11 = pyspeckit.Cube('hotclump_11.cube_r0.5.image.fits', maskmap=mask) cube11.cube *= (13.6 * (300.0 / (pyspeckit.spectrum.models.ammonia.freq_dict['oneone']/1e9))**2 * 1./cube11.header.get('BMAJ')/3600. * 1./cube11.header.get('BMIN')/3600. ) cube11.unit = "K" cube22 = pyspeckit.Cube('hotclump_22.cube_r0.5_contsub.image.fits', maskmap=mask) cube22.cube *= (13.6 * (300.0 / (pyspeckit.spectrum.models.ammonia.freq_dict['twotwo']/1e9))**2 * 1./cube22.header.get('BMAJ')/3600. * 1./cube22.header.get('BMIN')/3600. ) cube22.unit = "K" cube44 = pyspeckit.Cube('hotclump_44.cube_r0.5_contsub.image.fits', maskmap=mask) cube44.cube *= (13.6 * (300.0 / (pyspeckit.spectrum.models.ammonia.freq_dict['fourfour']/1e9))**2 * 1./cube44.header.get('BMAJ')/3600. * 1./cube44.header.get('BMIN')/3600. ) cube44.unit = "K"
def cubefit_gen(cube, ncomp=2, paraname=None, modname=None, chisqname=None, guesses=None, errmap11name=None, multicore=None, mask_function=None, snr_min=3.0, linename="oneone", momedgetrim=True, saveguess=False, **kwargs): ''' Perform n velocity component fit on the GAS ammonia 1-1 data. (This should be the function to call for all future codes if it has been proven to be reliable) # note: the method can probably be renamed to cubefit() Parameters ---------- cube : str The file name of the ammonia 1-1 cube or a SpectralCube object ncomp : int The number of components one wish to fit. Default is 2 paraname: str The output file name of the Returns ------- pcube : 'pyspeckit.cubes.SpectralCube.Cube' Pyspeckit cube object containing both the fit and the original data cube ''' if hasattr(cube, 'spectral_axis'): pcube = pyspeckit.Cube(cube=cube) else: cubename = cube cube = SpectralCube.read(cubename) pcube = pyspeckit.Cube(filename=cubename) pcube.unit = "K" # the following check on rest-frequency may not be necessarily for GAS, but better be safe than sorry # note: this assume the data cube has the right units if cube._wcs.wcs.restfrq == np.nan: # Specify the rest frequency not present cube = cube.with_spectral_unit(u.Hz, rest_value=freq_dict[linename] * u.Hz) cube = cube.with_spectral_unit(u.km / u.s, velocity_convention='radio') if pcube.wcs.wcs.restfrq == np.nan: # Specify the rest frequency not present pcube.xarr.refX = freq_dict[linename] * u.Hz pcube.xarr.velocity_convention = 'radio' # always register the fitter just in case different lines are used fitter = ammv.nh3_multi_v_model_generator(n_comp=ncomp, linenames=[linename]) pcube.specfit.Registry.add_fitter('nh3_multi_v', fitter, fitter.npars) print "number of parameters is {0}".format(fitter.npars) print "the line to fit is {0}".format(linename) # Specify a width for the expected velocity range in the data #v_peak_hwidth = 3.0 # km/s (should be sufficient for GAS Orion, but may not be enough for KEYSTONE) v_peak_hwidth = 4.0 # km/s (should be sufficient for GAS Orion, but may not be enough for KEYSTONE) if errmap11name is not None: errmap11 = fits.getdata(errmap11name) else: # a quick way to estimate RMS as long as the noise dominates the spectrum by channels mask_finite = np.isfinite(cube._data) errmap11 = mad_std(cube._data[mask_finite], axis=0) print "median rms: {0}".format(np.nanmedian(errmap11)) snr = cube.filled_data[:].value / errmap11 peaksnr = np.nanmax(snr, axis=0) #the snr map will inetiabley be noisy, so a little smoothing kernel = Gaussian2DKernel(1) peaksnr = convolve(peaksnr, kernel) # trim the edges by 3 pixels to guess the location of the peak emission footprint_mask = np.any(np.isfinite(cube._data), axis=0) if np.logical_and(footprint_mask.size > 1000, momedgetrim): print "triming the edges to make moment maps" footprint_mask = binary_erosion(footprint_mask, disk(3)) # the following function is copied directly from GAS def default_masking(snr, snr_min=5.0): planemask = (snr > snr_min) if planemask.size > 100: planemask = remove_small_objects(planemask, min_size=40) planemask = opening(planemask, disk(1)) return (planemask) if 'maskmap' in kwargs: planemask = kwargs['maskmap'] elif mask_function is None: planemask = default_masking(peaksnr, snr_min=snr_min) else: planemask = mask_function(peaksnr, snr_min=snr_min) print "planemask size: {0}, shape: {1}".format(planemask[planemask].size, planemask.shape) # masking mask = np.isfinite(cube._data) * planemask * footprint_mask print "mask size: {0}, shape: {1}".format(mask[mask].size, mask.shape) maskcube = cube.with_mask(mask.astype(bool)) maskcube = maskcube.with_spectral_unit(u.km / u.s, velocity_convention='radio') if guesses is not None: v_guess = guesses[::4] v_guess[v_guess == 0] = np.nan else: v_guess = np.nan if np.isfinite(v_guess).sum() > 0: v_guess = v_guess[np.isfinite(v_guess)] v_median = np.median(v_guess) print "The median of the user provided velocities is: {0}".format( v_median) m0, m1, m2 = main_hf_moments(maskcube, window_hwidth=v_peak_hwidth, v_atpeak=v_median) else: m0, m1, m2 = main_hf_moments(maskcube, window_hwidth=v_peak_hwidth) v_median = np.median(m1[np.isfinite(m1)]) print "median velocity: {0}".format(v_median) if False: # save the moment maps for diagnostic purposes hdr_new = copy.deepcopy(pcube.header) hdr_new['CDELT3'] = 1 hdr_new['CTYPE3'] = 'FITPAR' hdr_new['CRVAL3'] = 0 hdr_new['CRPIX3'] = 1 savename = "{0}_moments.fits".format( os.path.splitext(paraname)[0], "parameter_maps") fitcubefile = fits.PrimaryHDU(data=np.array([m0, m1, m2]), header=hdr_new) fitcubefile.writeto(savename, overwrite=True) # remove the nana values to allow np.nanargmax(m0) to operate smoothly m0[np.isnan( m0 )] = 0.0 # I'm not sure if this is a good way to get around the sum vs nansum issue # define acceptable v range based on the provided or determined median velocity vmax = v_median + v_peak_hwidth vmin = v_median - v_peak_hwidth # find the location of the peak signal (to determine the first pixel to fit if nearest neighbour method is used) peakloc = np.nanargmax(m0) ymax, xmax = np.unravel_index(peakloc, m0.shape) # set the fit parameter limits (consistent with GAS DR1) Texmin = 3.0 # K; a more reasonable lower limit (5 K T_kin, 1e3 cm^-3 density, 1e13 cm^-2 column, 3km/s sigma) Texmax = 40 # K; DR1 T_k for Orion A is < 35 K. T_k = 40 at 1e5 cm^-3, 1e15 cm^-2, and 0.1 km/s yields Tex = 37K sigmin = 0.07 # km/s sigmax = 2.5 # km/s; for Larson's law, a 10pc cloud has sigma = 2.6 km/s taumax = 100.0 # a reasonable upper limit for GAS data. At 10K and 1e5 cm^-3 & 3e15 cm^-2 -> 70 taumin = 0.2 # note: at 1e3 cm^-3, 1e13 cm^-2, 1 km/s linewidth, 40 K -> 0.15 eps = 0.001 # a small perturbation that can be used in guesses # get the guesses based on moment maps # tex and tau guesses are chosen to reflect low density, diffusive gas that are likley to have low SNR gg = moment_guesses(m1, m2, ncomp, sigmin=sigmin, moment0=m0) if guesses is None: guesses = gg else: # fill in the blanks with moment guesses guesses[guesses == 0] = np.nan gmask = np.isfinite(guesses) guesses[~gmask] = gg[~gmask] # fill in the failed sigma guesses with moment guesses gmask = guesses[1::4] < sigmin guesses[1::4][gmask] = gg[1::4][gmask] print "user provided guesses accepted" # The guesses should be fine in the first case, but just in case, make sure the guesses are confined within the # appropriate limits guesses[::4][guesses[::4] > vmax] = vmax guesses[::4][guesses[::4] < vmin] = vmin guesses[1::4][guesses[1::4] > sigmax] = sigmax guesses[1::4][guesses[1::4] < sigmin] = sigmin + eps guesses[2::4][guesses[2::4] > Texmax] = Texmax guesses[2::4][guesses[2::4] < Texmin] = Texmin guesses[3::4][guesses[3::4] > taumax] = taumax guesses[3::4][guesses[3::4] < taumin] = taumin if saveguess: # save the guesses for diagnostic purposes hdr_new = copy.deepcopy(pcube.header) hdr_new['CDELT3'] = 1 hdr_new['CTYPE3'] = 'FITPAR' hdr_new['CRVAL3'] = 0 hdr_new['CRPIX3'] = 1 savedir = "{0}/{1}".format(path.dirname(paraname), "guesses") try: os.makedirs(savedir) except OSError as e: if e.errno != errno.EEXIST: raise savename = "{0}_guesses.fits".format( path.splitext(paraname)[0], "parameter_maps") savename = "{0}/{1}".format(savedir, path.basename(savename)) fitcubefile = fits.PrimaryHDU(data=guesses, header=hdr_new) fitcubefile.writeto(savename, overwrite=True) # set some of the fiteach() inputs to that used in GAS DR1 reduction if not 'integral' in kwargs: kwargs['integral'] = False if not 'verbose_level' in kwargs: kwargs['verbose_level'] = 3 if not 'signal_cut' in kwargs: kwargs['signal_cut'] = 2 # Now fit the cube. (Note: the function inputs are consistent with GAS DR1 whenever possible) print('start fit') # use SNR masking if not provided if not 'maskmap' in kwargs: print "mask mask!" kwargs['maskmap'] = planemask * footprint_mask if np.sum(kwargs['maskmap']) < 1: print("[WARNING]: maskmap has no pixel, no fitting will be performed") return pcube elif np.sum(np.isfinite(guesses)) < 1: print("[WARNING]: guesses has no pixel, no fitting will be performed") return pcube pcube.fiteach(fittype='nh3_multi_v', guesses=guesses, start_from_point=(xmax, ymax), use_neighbor_as_guess=False, limitedmax=[True, True, True, True] * ncomp, maxpars=[vmax, sigmax, Texmax, taumax] * ncomp, limitedmin=[True, True, True, True] * ncomp, minpars=[vmin, sigmin, Texmin, taumin] * ncomp, multicore=multicore, **kwargs) if paraname != None: save_pcube(pcube, paraname, ncomp=ncomp) if modname != None: model = SpectralCube(pcube.get_modelcube(), pcube.wcs, header=cube.header) model.write(modname, overwrite=True) if chisqname != None: chisq = get_chisq(cube, pcube.get_modelcube(), expand=20) chisqfile = fits.PrimaryHDU(data=chisq, header=cube.wcs.celestial.to_header()) chisqfile.writeto(chisqname, overwrite=True) return pcube
def get_multiV_models(paraname, refcubename, n_comp=2, savename=None, snrname=None, rms=0.15, rmspath=None, linename="oneone"): ''' Creates a fits file containing the model cubes of individual components stacked into a hypercube :param paraname: :param refcubename: :param n_comp: :param savename: :param snrname: :param rms: :param rmspath: :return: ''' para, hdr = fits.getdata(paraname, header=True) pcube = pyspeckit.Cube(refcubename) xarr = pcube.xarr cubes = [pcube.cube.copy() for i in np.arange(n_comp)] cubes = np.array(cubes) cubes[:] = np.nan # remove the error components n_para = n_comp * 4 para = para[:n_para] assert para.shape[0] == n_para yy, xx = np.indices(para.shape[1:]) nanvals = np.any(~np.isfinite(para), axis=0) isvalid = np.any(para, axis=0) & ~nanvals valid_pixels = zip(xx[isvalid], yy[isvalid]) def model_a_pixel(xy): x, y = int(xy[0]), int(xy[1]) models = [ ammonia._ammonia_spectrum(xarr.as_unit('GHz'), tex=tex, tau_dict={linename: tau}, width=width, xoff_v=vel, fortho=0.0, line_names=[linename]) 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]) ] cubes[:, :, y, x] = models for xy in ProgressBar(list(valid_pixels)): print int(xy[0]), int(xy[1]) model_a_pixel(xy) if savename != None: f_name, f_extension = path.splitext(savename) for i, data in enumerate(cubes): fname = "{0}_v{1}_{2}".format(f_name, i, f_extension) model = SpectralCube(data, pcube.wcs, header=pcube.header) model.write(fname, overwrite=True) if snrname != None: # calculate the peak temperature Tpeak = np.array([np.nanmax(cube, axis=0) for cube in cubes]) if rmspath is not None: rmsdata = fits.getdata(rmspath) if rmsdata.shape == Tpeak[0].shape: rms = rmsdata else: print "[WARNING]: The shape of the rms map ({0}) does not match the shape of the emission map {1}." \ " An uniform rms value of: {2} has been adopted instead".format(rmsdata.shape, Tpeak[0].shape, rms) snr = Tpeak / rms snrfile = fits.PrimaryHDU(data=snr, header=pcube.header) for i in np.arange(n_comp * 8) + 1: key = 'PLANE{0}'.format(i) if key in hdr: hdr.remove(key) snrfile.header.set('CDELT3', 1) snrfile.header.set('CTYPE3', 'FITPAR') snrfile.header.set('PLANE1', 'SNR_0') snrfile.header.set('PLANE2', 'SNR_1') snrfile.header.set('NAXIS3', n_comp * 8) snrfile.writeto(snrname, overwrite=True) return cubes
import pyspeckit from spectral_cube import SpectralCube from astropy import units as u import pylab as pl cube11 = SpectralCube.read(filename='unpb_mosaic_11_trim.fits', allow_huge_operations=True) cube11.allow_huge_operations=True cube11 = cube11.to(u.K) cube11 = cube11.with_spectral_unit(u.km/u.s, velocity_convention='radio') n11cube = pyspeckit.Cube(cube = cube11) cube22 = SpectralCube.read('unpb_mosaic_22_trim.fits', allow_huge_operations=True) cube22.allow_huge_operations=True cube22 = cube22.to(u.K) cube22 = cube22.with_spectral_unit(u.km/u.s, velocity_convention='radio') n22cube = pyspeckit.Cube(cube = cube22) cube44 = SpectralCube.read('unpb_mosaic_44_trim.fits', allow_huge_operations=True) cube44.allow_huge_operations=True cube44 = cube44.to(u.K) cube44 = cube44.with_spectral_unit(u.km/u.s, velocity_convention='radio') n44cube = pyspeckit.Cube(cube = cube44) cube55 = SpectralCube.read('unpb_mosaic_55_trim.fits', allow_huge_operations=True) cube55.allow_huge_operations=True cube55 = cube55.to(u.K) cube55 = cube55.with_spectral_unit(u.km/u.s, velocity_convention='radio')
def update_rest_moment0_2(region_name='L1688', file_extension='base_all_rebase3', threshold=0.0125, save_masked=False): """ Function to update moment calculation based on centroid velocity from line fit. For a given line cube, we check which channels have flux in the model cube, and then use those channels as the appropiate channels for integration. Based on code provided by Vlas Sokolov Parameters ---------- region : str Name of region to re-calculate moment map file_extension : str filename extension threshold : float minimum threshold in model cube used to identify channels with emission Default is down to the machine precision, a better result could be obtained with 0.0125 save_masked : Boolean Keyword to store the masked cube used in the integrated intensity calculation. This is useful to Usage: import GAS GAS.PropertyMaps.update_rest_moment0_2(region_name='NGC1333', file_extension='DR1_rebase3', threshold=0.0125, save_masked=True) """ # Add NH3 (3,3) to this list once change Gaussian fits to fit this line as well for line in ['HC5N','HC7N_21_20','HC7N_22_21','C2S']: fit_file='{0}/{0}_{1}_{2}_param_cube.fits'.format(region_name,line,file_extension) fit_model_file='{0}/{0}_{1}_{2}_gauss_cube.fits'.format(region_name,line,file_extension) file_in ='{0}/{0}_{1}_{2}.fits'.format(region_name,line,file_extension) file_out='{0}/{0}_{1}_{2}_mom0_QA.fits'.format(region_name,line,file_extension) file_rms='{0}/{0}_{1}_{2}_rms_QA.fits'.format(region_name,line,file_extension) file_rms_mom='{0}/{0}_{1}_{2}_mom0_sigma_QA.fits'.format(region_name,line,file_extension) file_temp='{0}/{0}_{1}_{2}_masked_temp.fits'.format(region_name,line,file_extension) # Load pyspeckit cube # Might be able to just load the Gaussian cube here.. pycube = pyspeckit.Cube(file_in) pycube.load_model_fit( fit_file, npars=3, npeaks=1, fittype='gaussian') # If threshold is not defined, then use the machine accuracy if threshold == None: threshold=np.finfo(pycube.data.dtype).eps # Get model cube from pyspeckit. Is completely zero for Gaussian fits. Why? #modelcube = pycube.get_modelcube() # Using output model file from the Gaussian fitter instead model = pyspeckit.Cube(fit_model_file) modelcube = model.cube # Use spectral cube to calculate integrated intensity maps cube_raw = SpectralCube.read(file_in) # in km/s not Hz cube = cube_raw.with_spectral_unit(u.km / u.s,velocity_convention='radio') vaxis=cube.spectral_axis dv=np.abs(vaxis[1]-vaxis[0]) # define mask mask3d = modelcube > threshold # What to do with pixels without signal # Calculate mean velocity and velocity dispersion vmap=pycube.parcube[1,:,:] sigma_map=pycube.parcube[2,:,:] vmean=np.nanmean(vmap[vmap != 0])*u.km/u.s sigma_v=( np.nanmedian(sigma_map[vmap != 0]))*u.km/u.s total_spc=np.sqrt( (vaxis-vmean)**2)/sigma_v < 3.0 # im_mask=np.sum(mask3d, axis=0) # Here checking for bad fits. Places where parameter uncertainties are zero or unreasonably low # Probably a more elegant way to do this! # For Gaussian fits: parameters are [amp, vlsr, sigma] for ii in np.arange( im_mask.shape[1]): for jj in np.arange( im_mask.shape[0]): if ((im_mask[jj,ii] == 0) or (pycube.parcube[2,jj,ii] < 3*pycube.errcube[2,jj,ii]) or (pycube.errcube[1,jj,ii] == 0) or (pycube.errcube[1,jj,ii] > 0.2)): mask3d[:,jj,ii] = total_spc n_chan=np.sum(mask3d, axis=0) # create masked cube cube2 = cube.with_mask(mask3d) cube3 = cube.with_mask(~mask3d) # if save_masked: cube2.write( file_temp, overwrite=True) # calculate moment map moment_0 = cube2.moment(axis=0) moment_0.write( file_out, overwrite=True) rms=cube3.std(axis=0) rms.write( file_rms, overwrite=True)
F = False T = True # Some optional parameters for the script # (if False, it will try to load an already-stored version # of the file) fitcube = True # Mask out low S/N pixels (to speed things up) mask = pyfits.getdata('badpix_mask.fits') mask = np.isfinite(mask) * (mask > 0) # Load the data using a mask # Then calibrate the data (the data we're loading in this case are in Janskys, # but we want surface brightness in Kelvin for the fitting process) cube11 = pyspeckit.Cube('unpb_mosaic_11.fits', maskmap=mask) cube11.cube *= ( 13.6 * (300.0 / (pyspeckit.spectrum.models.ammonia.freq_dict['oneone'] / 1e9))**2 * 1. / cube11.header.get('BMAJ') / 3600. * 1. / cube11.header.get('BMIN') / 3600.) cube11.unit = "K" cube22 = pyspeckit.Cube('unpb_mosaic_22.fits', maskmap=mask) cube22.cube *= ( 13.6 * (300.0 / (pyspeckit.spectrum.models.ammonia.freq_dict['twotwo'] / 1e9))**2 * 1. / cube22.header.get('BMAJ') / 3600. * 1. / cube22.header.get('BMIN') / 3600.) cube22.unit = "K" cube44 = pyspeckit.Cube('unpb_mosaic_44.fits', maskmap=mask) cube44.cube *= (
def update_NH3_moment0(region_name='L1688', file_extension='DR1_rebase3', threshold=0.0125, trim_edge=True, save_masked=False): """ Function to update moment calculation based on centroid velocity from line fit. For a given NH3(1,1) cube, we check which channels have flux in the model cube, and then use those channels as the appropiate channels for integration. Based on code provided by Vlas Sokolov Parameters ---------- region : str Name of region to re-calculate moment map file_extension : str filename extension threshold : float minimum threshold in model cube used to identify channels with emission Default is down to the machine precision, a better result could be obtained with 0.0125 trim_edge : Boolean Keyword to trim noisy edges of output maps using disk erode save_masked : Boolean Keyword to store the masked cube used in the integrated intensity calculation. This is useful to Usage: import GAS GAS.PropertyMaps.update_NH3_moment0(region_name='NGC1333', file_extension='DR1_rebase3', threshold=0.0125, save_masked=True) """ fit_file='{0}/{0}_parameter_maps_{1}.fits'.format(region_name,file_extension) for line_i in ['11','22']: file_in ='{0}/{0}_NH3_{2}_{1}.fits'.format(region_name,file_extension,line_i) file_out='{0}/{0}_NH3_{2}_{1}_mom0_QA.fits'.format(region_name,file_extension,line_i) file_rms='{0}/{0}_NH3_{2}_{1}_rms_QA.fits'.format(region_name,file_extension,line_i) file_rms_mom='{0}/{0}_NH3_{2}_{1}_mom0_sigma_QA.fits'.format(region_name,file_extension,line_i) file_temp='{0}/{0}_NH3_{2}_{1}_masked_temp.fits'.format(region_name,file_extension,line_i) # Load pyspeckit cube pycube = pyspeckit.Cube(file_in) if 'FITTYPE' in fits.getheader(fit_file): # 'FITTYPE' is not present in old versions of the parameter files pycube.load_model_fit( fit_file, npars=6, npeaks=1) else: if not 'cold_ammonia' in pycube.specfit.Registry.multifitters: pycube.specfit.Registry.add_fitter('cold_ammonia',ammonia.cold_ammonia_model(),6) pycube.load_model_fit( fit_file, npars=6, npeaks=1, fittype='cold_ammonia') # If threshold is not defined, then use the machine accuracy if threshold == None: threshold=np.finfo(pycube.data.dtype).eps # Get model cube from pyspeckit, this take some time modelcube = pycube.get_modelcube() # Use spectral cube to calculate integrated intensity maps cube_raw = SpectralCube.read(file_in) # in km/s not Hz cube = cube_raw.with_spectral_unit(u.km / u.s,velocity_convention='radio') if trim_edge: cube = trim_edge_spectral_cube(cube) vaxis=cube.spectral_axis dv=np.abs(vaxis[1]-vaxis[0]) # define mask mask3d = modelcube > threshold # What to do with pixels without signal # Calculate mean velocity and velocity dispersion vmap=pycube.parcube[4,:,:] sigma_map=pycube.parcube[3,:,:] vmean=np.mean(vmap[vmap != 0])*u.km/u.s if line_i == '11': sigma_v=( np.median(sigma_map[vmap != 0]) + 0.15)*u.km/u.s else: sigma_v=( np.median(sigma_map[vmap != 0]))*u.km/u.s total_spc=np.sqrt( (vaxis-vmean)**2)/sigma_v < 3.0 # im_mask=np.sum(mask3d, axis=0) # Here checking for bad fits. Places where parameter uncertainties are zero or unreasonably low # Probably a more elegant way to do this! for ii in np.arange( im_mask.shape[1]): for jj in np.arange( im_mask.shape[0]): if ((im_mask[jj,ii] == 0) or (pycube.parcube[3,jj,ii] < 3*pycube.errcube[3,jj,ii]) or (pycube.errcube[4,jj,ii] == 0) or (pycube.errcube[1,jj,ii] < 0.01) or (pycube.parcube[0,jj,ii] == 5)): mask3d[:,jj,ii] = total_spc n_chan=np.sum(mask3d, axis=0) # create masked cube cube2 = cube.with_mask(mask3d) cube3 = cube.with_mask(~mask3d) # if save_masked: cube2.write( file_temp, overwrite=True) # calculate moment map moment_0 = cube2.moment(axis=0) moment_0.write( file_out, overwrite=True) rms=cube3.std(axis=0) rms.write( file_rms, overwrite=True)
import os import astropy.units as u if not os.path.exists('n2hp_cube.fit'): import astropy.utils.data as aud from astropy.io import fits f = aud.download_file( 'ftp://cdsarc.u-strasbg.fr/pub/cats/J/A%2BA/472/519/fits/opha_n2h.fit') with fits.open(f) as ff: ff[0].header['CUNIT3'] = 'm/s' for kw in ['CTYPE4', 'CRVAL4', 'CDELT4', 'CRPIX4']: del ff[0].header[kw] ff.writeto('n2hp_cube.fit') # Load the spectral cube cropped in the middle for efficiency spc = pyspeckit.Cube('n2hp_cube.fit')[:, 25:28, 12:15] # Set the velocity convention: in the future, this may be read directly from # the file, but for now it cannot be. spc.xarr.refX = 93176265000.0 * u.Hz spc.xarr.velocity_convention = 'radio' spc.xarr.convert_to_unit('km/s') # Register the fitter # The N2H+ fitter is 'built-in' but is not registered by default; this example # shows how to register a fitting procedure # 'multi' indicates that it is possible to fit multiple components and a # background will not automatically be fit 4 is the number of parameters in the # model (excitation temperature, optical depth, line center, and line width) spc.Registry.add_fitter('n2hp_vtau', pyspeckit.models.n2hp.n2hp_vtau_fitter, 4) # Get a measurement of the error per pixel
guesses[0, :, :] = 58 guesses[1, :, :] = 26.9 guesses[2, :, :] = 0.010 guesses[3, :, :] = 1.5 guesses[4, :, :] = 31.4 guesses[5, :, :] = 0.010 guesses[6, :, :] = 1.5 negmask = mn < -0.005 guesses[2, negmask] = -0.1 guesses[5, negmask] = -0.1 #peak = scube.max(axis=0) #mask = peak>0.005*peak.unit pcube = pyspeckit.Cube(cube=scube) parfilename = 'fitted_nh366_hf_emission.fits' if os.path.exists(parfilename): pcube.load_model_fit(parfilename, 7, 1, 'hfonly') else: pcube.fiteach( fittype='hfonly', guesses=guesses, limits=[(50, 70), (24, 29), (-10, 5), (0, 4), (29, 34), (-10, 5), (0, 4)], limitedmin=[True] * 7, limitedmax=[True] * 7, integral=False, errmap=errmap.value, signal_cut=0, #maskmap=mask,
def cubefit(region='NGC1333', blorder=1, vmin=5, vmax=15, do_plot=False, snr_min=5.0, multicore=1, file_extension=None, mask_function = None, gauss_fit=False): """ Fit NH3(1,1) and (2,2) cubes for the requested region. It fits all pixels with SNR larger than requested. Initial guess is based on moment maps and neighboring pixels. The fitting can be done in parallel mode using several cores, however, this is dangerous for large regions, where using a good initial guess is important. It stores the result in a FITS cube. TODO: -Improve initial guess Parameters ---------- region : str Name of region to reduce blorder : int order of baseline removed vmin : numpy.float Minimum centroid velocity to plot, in km/s. vmax : numpy.float Maximum centroid velocity to plot, in km/s. do_plot : bool If True, then a map of the region to map is shown. snr_min : numpy.float Minimum signal to noise ratio of the spectrum to be fitted. multicore : int Numbers of cores to use for parallel processing. file_extension : str File extension of the input maps. Default is 'base#' where # is the blorder parameter above. mask_function : fun function to create a custom made mask for analysis. Defaults to using `default_masking` """ if file_extension: root = file_extension else: # root = 'base{0}'.format(blorder) root = 'all' OneOneIntegrated = '{0}/{0}_NH3_11_{1}_mom0.fits'.format(region,root) OneOneFile = '{0}/{0}_NH3_11_{1}.fits'.format(region,root) RMSFile = '{0}/{0}_NH3_11_{1}_rms.fits'.format(region,root) TwoTwoFile = '{0}/{0}_NH3_22_{1}.fits'.format(region,root) ThreeThreeFile = '{0}/{0}_NH3_33_{1}.fits'.format(region,root) cube11sc = SpectralCube.read(OneOneFile) cube22sc = SpectralCube.read(TwoTwoFile) errmap11 = fits.getdata(RMSFile) rms = np.nanmedian(errmap11) snr = cube11sc.filled_data[:].value/errmap11 peaksnr = np.max(snr,axis=0) if mask_function is None: planemask = default_masking(peaksnr,snr_min = snr_min) else: planemask = mask_function(peaksnr,snr_min = snr_min) #planemask = (peaksnr>20) * (errmap11 < 0.2) mask = (snr>3)*planemask maskcube = cube11sc.with_mask(mask.astype(bool)) maskcube = maskcube.with_spectral_unit(u.km/u.s,velocity_convention='radio') slab = maskcube.spectral_slab( vmax*u.km/u.s, vmin*u.km/u.s) w11=slab.moment( order=0, axis=0).value peakloc = np.nanargmax(w11) ymax,xmax = np.unravel_index(peakloc,w11.shape) moment1 = slab.moment( order=1, axis=0).value moment2 = (slab.moment( order=2, axis=0).value)**0.5 moment2[np.isnan(moment2)]=0.2 moment2[moment2<0.2]=0.2 cube11 = pyspeckit.Cube(OneOneFile,maskmap=planemask) cube11.unit="K" cube22 = pyspeckit.Cube(TwoTwoFile,maskmap=planemask) cube22.unit="K" #cube33 = pyspeckit.Cube(ThreeThreeFile,maskmap=planemask) #cube33.unit="K" # removed as long as we're not modeling OPR cubes = pyspeckit.CubeStack([cube11,cube22],maskmap=planemask) cubes.unit="K" guesses = np.zeros((6,)+cubes.cube.shape[1:]) moment1[moment1<vmin] = vmin+0.2 moment1[moment1>vmax] = vmax-0.2 guesses[0,:,:] = 12 # Kinetic temperature guesses[1,:,:] = 3 # Excitation Temp guesses[2,:,:] = 14.5 # log(column) guesses[3,:,:] = moment2 # Line width / 5 (the NH3 moment overestimates linewidth) guesses[4,:,:] = moment1 # Line centroid guesses[5,:,:] = 0.0 # F(ortho) - ortho NH3 fraction (fixed) if do_plot: import matplotlib.pyplot as plt plt.imshow( w11, origin='lower',interpolation='nearest') plt.show() F=False T=True if not 'cold_ammonia' in cubes.specfit.Registry.multifitters: cubes.specfit.Registry.add_fitter('cold_ammonia',ammonia.cold_ammonia_model(),6) print('start fit') cubes.fiteach(fittype='cold_ammonia', guesses=guesses, integral=False, verbose_level=3, fixed=[F,F,F,F,F,T], signal_cut=2, limitedmax=[F,F,T,F,T,T], maxpars=[0,0,17.0,0,vmax,1], limitedmin=[T,T,T,T,T,T], minpars=[5,2.8,12.0,0.04,vmin,0], start_from_point=(xmax,ymax), use_neighbor_as_guess=True, position_order = 1/peaksnr, errmap=errmap11, multicore=multicore) fitcubefile = fits.PrimaryHDU(data=np.concatenate([cubes.parcube,cubes.errcube]), header=cubes.header) fitcubefile.header.set('PLANE1','TKIN') fitcubefile.header.set('PLANE2','TEX') fitcubefile.header.set('PLANE3','COLUMN') fitcubefile.header.set('PLANE4','SIGMA') fitcubefile.header.set('PLANE5','VELOCITY') fitcubefile.header.set('PLANE6','FORTHO') fitcubefile.header.set('PLANE7','eTKIN') fitcubefile.header.set('PLANE8','eTEX') fitcubefile.header.set('PLANE9','eCOLUMN') fitcubefile.header.set('PLANE10','eSIGMA') fitcubefile.header.set('PLANE11','eVELOCITY') fitcubefile.header.set('PLANE12','eFORTHO') fitcubefile.header.set('CDELT3',1) fitcubefile.header.set('CTYPE3','FITPAR') fitcubefile.header.set('CRVAL3',0) fitcubefile.header.set('CRPIX3',1) fitcubefile.writeto("{0}/{0}_parameter_maps_{1}.fits".format(region,root),clobber=True) if gauss_fit==True: molecules = ['C2S', 'HC7N_22_21', 'HC7N_21_20', 'HC5N'] for i in molecules: gauss_fitter(region=region, mol=i, vmin=vmin, vmax=vmax, snr_min=snr_min, multicore=multicore, file_extension=file_extension)