def GaussMoffatFit(pixels, projected_footprint, amplitude, x_0, gamma, alpha, A, mu, sigma, residuals, i, plot): g_init = models.Moffat1D(amplitude = amplitude, x_0 = x_0, gamma = gamma, alpha=alpha)\ + models.Gaussian1D(amplitude=A, mean=mu, stddev=sigma) g_init.stddev_1.min = 0.5 g_init.stddev_1.max = 3. g_init.amplitude_0.min = 1. g_init.amplitude_0.max = A / 10. g_init.gamma_0.min = 1. g_init.gamma_0.max = 2. g_init.alpha_0.min = 1. g_init.alpha_0.max = 2. fit_g = fitting.LevMarLSQFitter() psf = fit_g(g_init, pixels, projected_footprint) start = psf.x_0_0 - 10 * psf.stddev_1 end = psf.x_0_0 + 10 * psf.stddev_1 integralGM = (integrate.quad(lambda pixels: psf(pixels), start, end))[0] integralG = np.sqrt(2 * np.pi) * psf.stddev_1 * psf.amplitude_1 ''' begin Control plot''' if ((not i % 10) and (i < 400) and (plot == True)): pl.plot(pixels, psf(pixels), label='Gauss+Moffat') pl.yscale('log') pl.ylim(1., 1E6) pl.plot(pixels, projected_footprint) pl.legend() pl.show() ''' End control plot''' '''Filling residuals''' residuals[:, i] = psf(pixels) - projected_footprint return integralGM, integralG, psf.amplitude_0.value, psf.x_0_0.value, psf.gamma_0.value, psf.alpha_0.value, psf.amplitude_1.value, psf.mean_1.value, psf.stddev_1.value
def test_moffat_fwhm(gamma): ans = 34.641016151377542 kwargs = {'gamma': gamma, 'alpha': 0.5} m1 = models.Moffat1D(**kwargs) m2 = models.Moffat2D(**kwargs) assert_allclose([m1.fwhm, m2.fwhm], ans) assert_array_less(0, [m1.fwhm, m2.fwhm])
def make_2dspec_image( nx=3000, ny=1000, background=5, trace_center=None, trace_order=3, trace_coeffs={'c0': 0, 'c1': 50, 'c2': 100}, source_amplitude=10, source_alpha=0.1 ): """ Create synthetic 2D spectroscopic image with a single source. The spatial (y-axis) position of the source along the dispersion (x-axis) direction is modeled using a Chebyshev polynomial. The flux units are counts and the noise is modeled as Poisson. Parameters ---------- nx : int (default=3000) Size of image in X axis which is assumed to be the dispersion axis ny : int (default=1000) Size of image in Y axis which is assumed to be the spatial axis background : int (default=5) Level of constant background in counts trace_center : int (default=None) Zeropoint of the trace. If None, then use center of Y (spatial) axis. trace_order : int (default=3) Order of the Chebyshev polynomial used to model the source's trace trace_coeffs : dict (default={'c0': 0, 'c1': 50, 'c2': 100}) Dict containing the Chebyshev polynomial coefficients to use in the trace model source_amplitude : int (default=10) Amplitude of modeled source in counts source_alpha : float (default=0.1) Power index of the source's Moffat profile. Use small number here to emulate extended source. Returns ------- ccd_im : `~astropy.nddata.CCDData` CCDData instance containing synthetic 2D spectroscopic image """ x = np.arange(nx) y = np.arange(ny) xx, yy = np.meshgrid(x, y) profile = models.Moffat1D() profile.amplitude = source_amplitude profile.alpha = source_alpha if trace_center is None: trace_center = ny / 2 trace_mod = models.Chebyshev1D(degree=trace_order, **trace_coeffs) trace = yy - trace_center + trace_mod(xx/nx) z = background + profile(trace) noisy_image = apply_poisson_noise(z) ccd_im = CCDData(noisy_image, unit=u.count) return ccd_im
def MoffatFit(pixels, projected_footprint, A, mu, sigma): g_init = models.Moffat1D(amplitude=A, x_0=mu, gamma=sigma) fit_g = fitting.LevMarLSQFitter() psf = fit_g(g_init, pixels, projected_footprint) start = psf.x_0 - 5 * psf.gamma end = psf.x_0 + 5 * psf.gamma integral = (integrate.quad(lambda pixels: psf(pixels), start, end))[0] ''' begin Control plot''' #pl.plot(pixels, psf(pixels), label='Moffat') #pl.yscale('log') #pl.ylim(1., 1E6) #pl.plot(projected_footprint) ''' End control plot''' return
def model_data(self): #Astropy Simplex LSQ Fitter for PSFs fitter = fitting.SimplexLSQFitter() #Try to fit Moffat in X direction try: moffatx_init = models.Moffat1D(1.2 * np.max(self.xdata), self.x, 1.0, 1.0) moffatx_fit = fitter(moffatx_init, self.X[self.x0:self.x1], self.xdata[self.x0:self.x1]) self.xmoff = moffatx_fit(self.Xs) self.x_opt = moffatx_fit.x_0.value except: self.xmoff = np.zeros_like(self.Xs) self.x_opt = self.x #Try to fit Moffat in Y direction try: #self.ydata -= np.median(self.ydata) moffaty_init = models.Moffat1D(1.2 * np.max(self.ydata), self.y, 1.0, 1.0) moffaty_fit = fitter(moffaty_init, self.Y[self.y0:self.y1], self.ydata[self.y0:self.y1]) self.ymoff = moffaty_fit(self.Ys) self.y_opt = moffaty_fit.x_0.value except: self.ymoff = np.zeros_like(self.Ys) self.y_opt = self.y #Update upper and lower bounds for fitting PSF self.x0 = max(0, self.x - self.dx) self.x1 = min(self.X[-1] - 1, self.x + self.dx) self.y0 = max(0, self.y - self.dy) self.y1 = min(self.Y[-1] - 1, self.y + self.dy)
def fit_moffat_1d(data, gamma=2., alpha=1.): """Fit a 1D moffat profile to the data and return the fit.""" # data is assumed to already be chunked to a reasonable size ldata = len(data) x = np.arange(ldata) # Fit model to data fit = fitting.LevMarLSQFitter() # Moffat1D model = models.Moffat1D(amplitude=max(data), x_0=ldata/2, gamma=gamma, alpha=alpha) with warnings.catch_warnings(): # Ignore model linearity warning from the fitter warnings.simplefilter('ignore') results = fit(model, x, data) # previous yield amp, ycenter, xcenter, sigma, offset return results
def fit_moffat_1d(x, y, gamma=2., alpha=1.): """Fit a 1D moffat profile to the data and return the fit.""" # Fit model to data fit = fitting.LevMarLSQFitter() x_0 = x[0] # Moffat1D model = models.Moffat1D(amplitude=max(y), x_0=x_0, gamma=gamma, alpha=alpha) with warnings.catch_warnings(): # Ignore model linearity warning from the fitter warnings.simplefilter('ignore') results = fit(model, x, y) # previous yield amp, ycenter, xcenter, sigma, offset return results
def MoffatFit(pixels, projected_footprint, A, mu, sigma, residuals, i, plot): g_init = models.Moffat1D(amplitude=A, x_0=mu, gamma=sigma) fit_g = fitting.LevMarLSQFitter() psf = fit_g(g_init, pixels, projected_footprint) start = psf.x_0 - 5 * psf.gamma end = psf.x_0 + 5 * psf.gamma integral = (integrate.quad(lambda pixels: psf(pixels), start, end))[0] ''' begin Control plot''' if ((not i % 10) and (i < 400) and (plot == True)): pl.plot(pixels, psf(pixels), label='Moffat') pl.yscale('log') pl.ylim(1., 1E6) pl.plot(pixels, projected_footprint) pl.legend() pl.show() ''' End control plot''' '''Filling residuals''' #residuals[:, i] = psf(pixels)-projected_footprint return integral, psf.x_0.value, psf.gamma.value, psf.alpha.value
astmodels.Const1D(amplitude=5.), astmodels.Const2D(amplitude=5.), astmodels.Disk2D(amplitude=10., x_0=0.5, y_0=1.5, R_0=5.), astmodels.Ellipse2D(amplitude=10., x_0=0.5, y_0=1.5, a=2., b=4., theta=0.1), astmodels.Exponential1D(amplitude=10., tau=3.5), astmodels.Gaussian1D(amplitude=10., mean=5., stddev=3.), astmodels.Gaussian2D(amplitude=10., x_mean=5., y_mean=5., x_stddev=3., y_stddev=3.), astmodels.KingProjectedAnalytic1D(amplitude=10., r_core=5., r_tide=2.), astmodels.Logarithmic1D(amplitude=10., tau=3.5), astmodels.Lorentz1D(amplitude=10., x_0=0.5, fwhm=2.5), astmodels.Moffat1D(amplitude=10., x_0=0.5, gamma=1.2, alpha=2.5), astmodels.Moffat2D(amplitude=10., x_0=0.5, y_0=1.5, gamma=1.2, alpha=2.5), astmodels.Planar2D(slope_x=0.5, slope_y=1.2, intercept=2.5), astmodels.RedshiftScaleFactor(z=2.5), astmodels.RickerWavelet1D(amplitude=10., x_0=0.5, sigma=1.2), astmodels.RickerWavelet2D(amplitude=10., x_0=0.5, y_0=1.5, sigma=1.2), astmodels.Ring2D(amplitude=10., x_0=0.5, y_0=1.5, r_in=5., width=10.), astmodels.Sersic1D(amplitude=10., r_eff=1., n=4.), astmodels.Sersic2D(amplitude=10., r_eff=1., n=4., x_0=0.5, y_0=1.5, ellip=0.0, theta=0.0), astmodels.Sine1D(amplitude=10., frequency=0.5, phase=1.),
from astropy.modeling import Parameter, Fittable1DModel from astropy.modeling.polynomial import PolynomialModel registry = { 'Gaussian1D': models.Gaussian1D(1.0, 1.0, 1.0), 'GaussianAbsorption1D': models.GaussianAbsorption1D(1.0, 1.0, 1.0), 'Lorentz1D': models.Lorentz1D(1.0, 1.0, 1.0), 'MexicanHat1D': models.MexicanHat1D(1.0, 1.0, 1.0), 'Trapezoid1D': models.Trapezoid1D(1.0, 1.0, 1.0, 1.0), 'Moffat1D': models.Moffat1D(1.0, 1.0, 1.0, 1.0), 'ExponentialCutoffPowerLaw1D': models.ExponentialCutoffPowerLaw1D(1.0, 1.0, 1.0, 1.0), 'BrokenPowerLaw1D': models.BrokenPowerLaw1D(1.0, 1.0, 1.0, 1.0), 'LogParabola1D': models.LogParabola1D(1.0, 1.0, 1.0, 1.0), 'PowerLaw1D': models.PowerLaw1D(1.0, 1.0, 1.0), 'Linear1D': models.Linear1D(1.0, 0.0), 'Const1D': models.Const1D(0.0), 'Redshift': models.Redshift(0.0), 'Scale':
def fit_ellipse_for_source( friendid=None, detectid=None, coords=None, shotid=None, subcont=True, convolve_image=False, pixscale=pixscale, imsize=imsize, wave_range=None, ): if detectid is not None: global deth5 detectid_obj = detectid if detectid_obj <= 2190000000: det_info = deth5.root.Detections.read_where("detectid == detectid_obj")[0] linewidth = det_info["linewidth"] wave_obj = det_info["wave"] redshift = wave_obj / (1216) - 1 else: det_info = conth5.root.Detections.read_where("detectid == detectid_obj")[0] redshift = 0 wave_obj = 4500 coords_obj = SkyCoord(det_info["ra"], det_info["dec"], unit="deg") shotid_obj = det_info["shotid"] fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")["fwhm_virus"][0] amp = det_info["multiframe"] if wave_range is not None: wave_range_obj = wave_range else: if detectid_obj <= 2190000000: wave_range_obj = [wave_obj - 2 * linewidth, wave_obj + 2 * linewidth] else: wave_range_obj = [4100, 4200] if coords is not None: coords_obj = coords if shotid is not None: shotid_obj = shotid fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")[ "fwhm_virus" ][0] try: hdu = make_narrowband_image( coords=coords_obj, shotid=shotid_obj, wave_range=wave_range_obj, imsize=imsize * u.arcsec, pixscale=pixscale * u.arcsec, subcont=subcont, convolve_image=convolve_image, include_error=True, ) except: print("Could not make narrowband image for {}".format(detectid)) return np.nan, np.nan elif friendid is not None: global friend_cat sel = friend_cat["friendid"] == friendid group = friend_cat[sel] coords_obj = SkyCoord(ra=group["icx"][0] * u.deg, dec=group["icy"][0] * u.deg) wave_obj = group["icz"][0] redshift = wave_obj / (1216) - 1 linewidth = group["linewidth"][0] shotid_obj = group["shotid"][0] fwhm = group["fwhm"][0] amp = group["multiframe"][0] if wave_range is not None: wave_range_obj = wave_range else: wave_range_obj = [wave_obj - 2 * linewidth, wave_obj + 2 * linewidth] if shotid is not None: shotid_obj = shotid fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")[ "fwhm_virus" ][0] try: hdu = make_narrowband_image( coords=coords_obj, shotid=shotid_obj, wave_range=wave_range_obj, imsize=imsize * u.arcsec, pixscale=pixscale * u.arcsec, subcont=subcont, convolve_image=convolve_image, include_error=True, ) except: print("Could not make narrowband image for {}".format(friendid)) return None elif coords is not None: coords_obj = coords if wave_range is not None: wave_range_obj = wave_range else: print( "You need to supply wave_range=[wave_start, wave_end] for collapsed image" ) if shotid is not None: shotid_obj = shotid fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")[ "fwhm_virus" ][0] else: print("Enter the shotid to use (eg. 20200123003)") hdu = make_narrowband_image( coords=coords_obj, shotid=shotid_obj, wave_range=wave_range_obj, imsize=imsize * u.arcsec, pixscale=pixscale * u.arcsec, subcont=subcont, convolve_image=convolve_image, include_error=True, ) else: print("You must provide a detectid, friendid or coords/wave_range/shotid") return np.nan, np.nan w = wcs.WCS(hdu[0].header) if friendid is not None: sel_friend_group = friend_cat["friendid"] == friendid group = friend_cat[sel_friend_group] eps = 1 - group["a2"][0] / group["b2"][0] pa = group["pa"][0] * np.pi / 180.0 - 90 sma = group["a"][0] * 3600 / pixscale coords = SkyCoord(ra=group["icx"][0] * u.deg, dec=group["icy"][0] * u.deg) wave_obj = group["icz"][0] redshift = wave_obj / (1216) - 1 linewidth = np.nanmedian(group["linewidth"]) shotid_obj = group["shotid"][0] fwhm = group["fwhm"][0] geometry = EllipseGeometry( x0=w.wcs.crpix[0], y0=w.wcs.crpix[0], sma=sma, eps=eps, pa=pa ) else: geometry = EllipseGeometry( x0=w.wcs.crpix[0], y0=w.wcs.crpix[0], sma=20, eps=0.2, pa=20.0 ) geometry = EllipseGeometry( x0=w.wcs.crpix[0], y0=w.wcs.crpix[0], sma=20, eps=0.2, pa=20.0 ) # geometry.find_center(hdu.data) # aper = EllipticalAperture((geometry.x0, geometry.y0), geometry.sma, # geometry.sma*(1 - geometry.eps), geometry.pa) # plt.imshow(hdu.data, origin='lower') # aper.plot(color='white') ellipse = Ellipse(hdu[0].data) isolist = ellipse.fit_image() iso_tab = isolist.to_table() if len(iso_tab) == 0: geometry.find_center(hdu[0].data, verbose=False, threshold=0.5) ellipse = Ellipse(hdu[0].data, geometry) isolist = ellipse.fit_image() iso_tab = isolist.to_table() if len(iso_tab) == 0: return np.nan, np.nan, np.nan try: # compute iso's manually in steps of 3 pixels ellipse = Ellipse(hdu[0].data) # reset ellipse iso_list = [] for sma in np.arange(1, 60, 2): iso = ellipse.fit_isophote(sma) if np.isnan(iso.intens): # print('break at {}'.format(sma)) break else: iso_list.append(iso) isolist = IsophoteList(iso_list) iso_tab = isolist.to_table() except: return np.nan, np.nan, np.nan try: model_image = build_ellipse_model(hdu[0].data.shape, isolist) residual = hdu[0].data - model_image except: return np.nan, np.nan, np.nan sma = iso_tab["sma"] * pixscale const_arcsec_to_kpc = cosmo.kpc_proper_per_arcmin(redshift).value / 60.0 def arcsec_to_kpc(sma): dist = const_arcsec_to_kpc * sma return dist def kpc_to_arcsec(dist): sma = dist / const_arcsec_to_kpc return sma dist_kpc = ( sma * u.arcsec.to(u.arcmin) * u.arcmin * cosmo.kpc_proper_per_arcmin(redshift) ) dist_arcsec = kpc_to_arcsec(dist_kpc) # print(shotid_obj, fwhm) # s_exp1d = models.Exponential1D(amplitude=0.2, tau=-50) alpha = 3.5 s_moffat = models.Moffat1D( amplitude=1, gamma=(0.5 * fwhm) / np.sqrt(2 ** (1.0 / alpha) - 1.0), x_0=0.0, alpha=alpha, fixed={"amplitude": False, "x_0": True, "gamma": True, "alpha": True}, ) s_init = models.Exponential1D(amplitude=0.2, tau=-50) fit = fitting.LevMarLSQFitter() s_r = fit(s_init, dist_kpc, iso_tab["intens"]) # Fitting can be done using the uncertainties as weights. # To get the standard weighting of 1/unc^2 for the case of # Gaussian errors, the weights to pass to the fitting are 1/unc. # fitted_line = fit(line_init, x, y, weights=1.0/yunc) # s_r = fit(s_init, dist_kpc, iso_tab['intens'])#, weights=iso_tab['intens']/iso_tab['intens_err'] ) print(s_r) try: r_n = -1.0 * s_r.tau # _0 #* const_arcsec_to_kpc except: r_n = np.nan # r_n = -1. * s_r.tau_0 try: sel_iso = np.where(dist_kpc >= 2 * r_n)[0][0] except: sel_iso = -1 aper = EllipticalAperture( (isolist.x0[sel_iso], isolist.y0[sel_iso]), isolist.sma[sel_iso], isolist.sma[sel_iso] * (1 - isolist.eps[sel_iso]), isolist.pa[sel_iso], ) phottable = aperture_photometry(hdu[0].data, aper, error=hdu[1].data) flux = phottable["aperture_sum"][0] * 10 ** -17 * u.erg / (u.cm ** 2 * u.s) flux_err = phottable["aperture_sum_err"][0] * 10 ** -17 * u.erg / (u.cm ** 2 * u.s) lum_dist = cosmo.luminosity_distance(redshift).to(u.cm) lum = flux * 4.0 * np.pi * lum_dist ** 2 lum_err = flux_err * 4.0 * np.pi * lum_dist ** 2 if detectid: name = detectid elif friendid: name = friendid # Get Image data from Elixer catlib = catalogs.CatalogLibrary() try: cutout = catlib.get_cutouts( position=coords_obj, side=imsize, aperture=None, dynamic=False, filter=["r", "g", "f606W"], first=True, allow_bad_image=False, allow_web=True, )[0] except: print("Could not get imaging for " + str(name)) zscale = ZScaleInterval(contrast=0.5, krej=1.5) vmin, vmax = zscale.get_limits(values=hdu[0].data) fig = plt.figure(figsize=(20, 12)) fig.suptitle( "{} ra={:3.2f}, dec={:3.2f}, wave={:5.2f}, z={:3.2f}, mf={}".format( name, coords_obj.ra.value, coords_obj.dec.value, wave_obj, redshift, amp ), fontsize=22, ) ax1 = fig.add_subplot(231, projection=w) plt.imshow(hdu[0].data, vmin=vmin, vmax=vmax) plt.xlabel("RA") plt.ylabel("Dec") plt.colorbar() plt.title("Image summed across 4*linewidth") ax2 = fig.add_subplot(232, projection=w) plt.imshow(model_image, vmin=vmin, vmax=vmax) plt.xlabel("RA") plt.ylabel("Dec") plt.colorbar() plt.title("model") ax3 = fig.add_subplot(233, projection=w) plt.imshow(residual, vmin=vmin, vmax=vmax) plt.xlabel("RA") plt.ylabel("Dec") plt.colorbar() plt.title("residuals (image-model)") # fig = plt.figure(figsize=(10,5)) im_zscale = ZScaleInterval(contrast=0.5, krej=2.5) im_vmin, im_vmax = im_zscale.get_limits(values=cutout["cutout"].data) ax4 = fig.add_subplot(234, projection=cutout["cutout"].wcs) plt.imshow( cutout["cutout"].data, vmin=im_vmin, vmax=im_vmax, origin="lower", cmap=plt.get_cmap("gray"), interpolation="none", ) plt.text( 0.8, 0.9, cutout["instrument"] + cutout["filter"], transform=ax4.transAxes, fontsize=20, color="w", ) plt.contour(hdu[0].data, transform=ax4.get_transform(w)) plt.xlabel("RA") plt.ylabel("Dec") aper.plot( color="white", linestyle="dashed", linewidth=2, transform=ax4.get_transform(w) ) ax5 = fig.add_subplot(235) plt.errorbar( dist_kpc.value, iso_tab["intens"], yerr=iso_tab["intens_err"] * iso_tab["intens"], linestyle="none", marker="o", label="Lya SB profile", ) plt.plot(dist_kpc, s_r(dist_kpc), color="r", label="Lya exp SB model", linewidth=2) plt.xlabel("Semi-major axis (kpc)") # plt.xlabel('Semi-major axis (arcsec)') plt.ylabel("Flux ({})".format(10 ** -17 * (u.erg / (u.s * u.cm ** 2)))) plt.text(0.4, 0.7, "r_n={:3.2f}".format(r_n), transform=ax5.transAxes, fontsize=16) plt.text( 0.4, 0.6, "L_lya={:3.2e}".format(lum), transform=ax5.transAxes, fontsize=16 ) secax = ax5.secondary_xaxis("top", functions=(kpc_to_arcsec, kpc_to_arcsec)) secax.set_xlabel("Semi-major axis (arcsec)") # secax.set_xlabel('Semi-major axis (kpc)') plt.xlim(0, 100) # plt.plot(sma, s_r(sma), label='moffat psf') # plt.plot(dist_kpc.value, s1(kpc_to_arcsec(dist_kpc.value)), # linestyle='dashed', linewidth=2, # color='green', label='PSF seeing:{:3.2f}'.format(fwhm)) # These two are the exact same # s1 = models.Moffat1D() # s1.amplitude = iso_tab['intens'][0] # alpha=3.5 # s1.gamma = 0.5*(fwhm*const_arcsec_to_kpc)/ np.sqrt(2 ** (1.0 / alpha) - 1.0) # s1.alpha = alpha # plt.plot(r_1d, moffat_1d, color='orange') # plt.plot(dist_kpc.value, (s1(dist_kpc.value)), # linestyle='dashed', linewidth=2, # color='blue', label='PSF seeing:{:3.2f}'.format(fwhm)) E = Extract() E.load_shot(shotid_obj) moffat_psf = E.moffat_psf(seeing=fwhm, boxsize=imsize, scale=pixscale) moffat_shape = np.shape(moffat_psf) xcen = int(moffat_shape[1] / 2) ycen = int(moffat_shape[2] / 2) moffat_1d = ( moffat_psf[0, xcen:-1, ycen] / moffat_psf[0, xcen, ycen] * iso_tab["intens"][0] ) r_1d = moffat_psf[1, xcen:-1, ycen] E.close() plt.plot( arcsec_to_kpc(pixscale * np.arange(80)), iso_tab["intens"][0] * (moffat_psf[0, 80:-1, 80] / moffat_psf[0, 80, 80]), linestyle="dashed", color="green", label="PSF seeing:{:3.2f}".format(fwhm), ) plt.legend() if friendid is not None: ax6 = fig.add_subplot(236, projection=cutout["cutout"].wcs) plot_friends(friendid, friend_cat, cutout, ax=ax6, label=False) plt.savefig("fit2d_{}.png".format(name)) # filename = 'param_{}.txt'.format(name) # np.savetxt(filename, (r_n.value, lum.value)) return r_n, lum, lum_err
Image_Data_All2 = Image_Data_All[300:400] Image_Data_All2= np.nan_to_num(Image_Data_All2) #Define a running median calculator def RunMedian(x,N): idx = np.arange(N) + np.arange(len(x)-N+1)[:,None] b = [row[row>0] for row in x[idx]] return np.array(map(np.median,b)) #Create the Gaussian and Moffat fits and creates Fit_Data and Fit_Data2 #which contains the parameters for the two models (Moffat still does not work correct) x = np.linspace(-50,50,100) Gauss_Model = models.Gaussian1D(amplitude = 1000., mean = 0, stddev = 1., ) Gauss_Model.mean.fixed = False Moffat_Model = models.Moffat1D(amplitude = 1000, x_0 = 0, gamma = 1, alpha = 2) Fitting_Model = fitting.LevMarLSQFitter() Fit_Data_1 = [] Fit_Data_2 = [] for i in range(0, Image_Data_All2.shape[1]): Fit_Data_1.append(Fitting_Model(Gauss_Model, x, Image_Data_All2[:,i])) x2 = np.empty((1024,3)) for n in range(1024): x2[n,:] = Fit_Data_1[n].parameters Amp_Data_1 = x2[:,0]
def RunMedian(x, N): idx = np.arange(N) + np.arange(len(x) - N + 1)[:, None] b = [row[row > 0] for row in x[idx]] return np.array(map(np.median, b)) #Create the Gaussian and Moffat fits and creates Fit_Data and Fit_Data2 #which contains the parameters for the two models (Moffat still does not work correct) x = np.linspace(-50, 50, 100) Gauss_Model = models.Gaussian1D( amplitude=1000., mean=0, stddev=1., ) Gauss_Model.mean.fixed = False Moffat_Model = models.Moffat1D(amplitude=1000, x_0=0, gamma=1, alpha=2) Fitting_Model = fitting.LevMarLSQFitter() Fit_Data_1 = [] Fit_Data_2 = [] #Normal Gauss Model Fitting for i in range(0, Image_Data_All2.shape[1]): Fit_Data_1.append(Fitting_Model(Gauss_Model, x, Image_Data_All2[:, i])) x2 = np.empty((1024, 3)) for n in range(1024): x2[n, :] = Fit_Data_1[n].parameters
else: print('Sigma Clipping Complete') Mean_Poly_Fit = np.ma.polyfit(Mean_Range, Mean_Data_Clipped, Polydegree) Mean_Range_Fit = np.linspace(0, Image_Pixel_Data.shape[1] - 1, Image_Pixel_Data.shape[1]) Mean_Data_Fit = Mean_Poly_Fit[0] * Mean_Range_Fit**2 + Mean_Poly_Fit[ 1] * Mean_Range_Fit**1 + Mean_Poly_Fit[2] plt.plot(Mean_Data_1) plt.plot(Mean_Data_Fit) plt.show() #Normal Moffat Model Fitting Moffat_Model = models.Moffat1D(amplitude=1000, x_0=0, gamma=1, alpha=2) Fit_Data_2 = [] for i in range(0, Image_Pixel_Data.shape[1]): if Fit_Data_2: # true if not an empty list Moffat_Model = models.Moffat1D(amplitude=Ampl_Data_1[-1], x_0=Mean_Data_Fit[i], gamma=1.3, alpha=0.9) Moffat_Model.x_0.fixed = True Fit_Data_2.append(Fitting_Model(Moffat_Model, x, Image_Pixel_Data[:, i])) Fit_Data_2[0] = Fitting_Model( models.Moffat1D(amplitude=Ampl_Data_1[0], x_0=Mean_Data_Fit[0], gamma=1.3,
def moffatModel(pars): amp, mu, sigma = pars moff = models.Moffat1D(amplitude=amp, x_0=mu, gamma=sigma) return moff
fit = fitting.LevMarLSQFitter() gaus_1 = models.Gaussian1D(amplitude=np.amax(c1_height), mean=0.5*np.pi, stddev=0.05*np.pi) gaus_2 = models.Gaussian1D(amplitude=np.amax(c2_height), mean=0.5*np.pi, stddev=0.05*np.pi) th_fit = 0.5 * (c1_th[1:] + c1_th[:-1]) # np.linspace(c_min,c_max,len(c1_height)) th_plot = np.linspace(c_min,c_max,10000) gausfit_1 = fit(gaus_1, th_fit, c1_height) # ax_CC[0].plot(th_plot, gausfit_1(th_plot), ls=':', lw=0.9, color='black') gausfit_2 = fit(gaus_2, th_fit, c2_height) # ax_CC[1].plot(th_plot, gausfit_2(th_plot), ls=':', lw=0.9, color='black') c1_stddev = 1. * gausfit_1.stddev # * 2.355 se fwhm c2_stddev = 1. * gausfit_2.stddev print('c1_stddev = {:.3f} pi'.format(c1_stddev / np.pi)) print('c2_stddev = {:.3f} pi'.format(c2_stddev / np.pi)) moff_1 = models.Moffat1D(amplitude=np.amax(c1_height), x_0=0.5*np.pi, gamma=1, alpha=1) moff_2 = models.Moffat1D(amplitude=np.amax(c2_height), x_0=0.5*np.pi, gamma=1, alpha=1) th_fit = 0.5 * (c1_th[1:] + c1_th[:-1]) # np.linspace(c_min,c_max,len(c1_height)) th_plot = np.linspace(c_min,c_max,10000) moffit_1 = fit(moff_1, th_fit, c1_height) ax_CC[0].plot(th_plot, moffit_1(th_plot), ls='--', lw=0.9, color='black') moffit_2 = fit(moff_2, th_fit, c2_height) ax_CC[1].plot(th_plot, moffit_2(th_plot), ls='--', lw=0.9, color='black') # fig_CC.tight_layout() for i in range(2): ax_CC[i].grid(ls=':',which='both') ax_CC[i].set_xlim(c_min,c_max) # ax_CC[i].xaxis.set_major_locator(tck.MultipleLocator(0.15 / 2 * np.pi)) ax_CC[i].set_xticks(np.array([0.350, 0.425, 0.500, 0.575, 0.650]) * np.pi)
def psfSubtract(fits, pos, redshift=None, vwindow=1000, radius=5, mode='scale2D', errLimit=3, inst='PCWI'): global lines, skylines ##### EXTRACT DATA FROM FITS data = fits[0].data #data cube head = fits[0].header #header #ROTATE (TEMPORARILY) SO THAT AXIS 2 IS 'IN-SLICE' for KCWI DATA if instrument == 'KCWI': data_rot = np.zeros((data.shape[0], data.shape[2], data.shape[1])) for wi in range(len(data)): data_rot[wi] = np.rot90(data[wi], k=1) data = data_rot pos = (pos[1], pos[0]) w, y, x = data.shape #Cube dimensions X = np.arange(x) #Create domains X,Y and W Y = np.arange(y) W = np.array([ head["CRVAL3"] + head["CD3_3"] * (i - head["CRPIX3"]) for i in range(w) ]) ##### CREATE USEFUl VARIABLES & DATA STRUCTURES cmodel = np.zeros_like(data) #Cube to store 3D continuum model usewav = np.ones_like( W, dtype=bool ) #Boolean array for whether or not to use wavelengths in fitting Xs = np.linspace(X[0], X[-1], 10 * x) #Smooth X-Y domains for PSF modelling Ys = np.linspace(Y[0], Y[-1], 10 * y) ydist = 3600 * np.sqrt( np.cos(head["CRVAL2"] * np.pi / 180) * head["CD1_2"]**2 + head["CD2_2"]**2) #X & Y pixel sizes in arcseconds xdist = 3600 * np.sqrt( np.cos(head["CRVAL2"] * np.pi / 180) * head["CD1_1"]**2 + head["CD2_1"]**2) ry = int(round(radius / ydist)) #X and Y 'radius' extent in pixels rx = int(round(radius / xdist)) ##### EXCLUDE EMISSION LINE WAVELENGTHS usewav[W < head["WAVGOOD0"]] = 0 usewav[W > head["WAVGOOD1"]] = 0 if redshift != None: for line in lines: wc = (redshift + 1) * line dw = (vwindow * 1e5 / 3e10) * wc a, b = params.getband(wc - dw, wc + dw, head) usewav[a:b] = 0 ##### OPTIMIZE CENTROID xc, yc = pos #Take input position tuple x0, x1 = max(0, xc - rx), min(x, xc + rx + 1) #Get bounding box for PSF fit y0, y1 = max(0, yc - ry), min(y, yc + ry + 1) img = np.sum(data[usewav, y0:y1, x0:x1], axis=0) #Create white light image xdomain, xdata = range(x1 - x0), np.sum( img, axis=0) #Get X and Y PSF profiles/domains ydomain, ydata = range(y1 - y0), np.sum(img, axis=1) fit = fitting.SimplexLSQFitter() #Get astropy fitter class moffat_bounds = {'amplitude': (0, float("inf"))} xMoffInit = models.Moffat1D(max(xdata), x_0=xc - x0, bounds=moffat_bounds) #Initial guesses yMoffInit = models.Moffat1D(max(ydata), x_0=yc - y0, bounds=moffat_bounds) xMoffFit = fit(xMoffInit, xdomain, xdata) #Fit Moffat1Ds to each axis yMoffFit = fit(yMoffInit, ydomain, ydata) xc_new = xMoffFit.x_0.value + x0 yc_new = yMoffFit.x_0.value + y0 #If the new centroid is beyond our anticipated error range away... just use scale method if abs(xc - xc_new) * xdist > errLimit or abs(yc - yc_new) * ydist > errLimit: mode = 'scale2D' #Otherwise, update the box to center better on our continuum source else: xc, yc = int(round(xc_new)), int( round(yc_new)) #Round to nearest integer x0, x1 = max(0, xc - rx), min(x, xc + rx + 1) #Get new ranges y0, y1 = max(0, yc - ry), min(y, yc + ry + 1) xc = max(0, min(x - 1, xc)) #Bound new variables to within image yc = max(0, min(y - 1, yc)) #This method creates a 2D continuum image and scales it at each wavelength. if mode == 'scale2D': print 'scale2D', ##### CREATE CROPPED CUBE cube = data[:, y0:y1, x0:x1].copy( ) #Create smaller working cube to isolate continuum source ##### CREATE 2D CONTINUUM IMAGE cont2d = np.mean(cube[usewav], axis=0) #Create 2D continuum image fitter = fitting.LinearLSQFitter() ##### BUILD 3D CONTINUUM MODEL for i in range(cube.shape[0]): A0 = max(0, float(np.sum(cube[i])) / np.sum(cont2d)) #Initial guess for scaling factor scale_init = models.Scale() scale_fit = fitter(scale_init, np.ndarray.flatten(cont2d), np.ndarray.flatten(cube[i])) model = scale_fit.factor.value * cont2d #Add this wavelength layer to the model data[i, y0:y1, x0:x1] -= model #Subtract from data cube cmodel[i, y0:y1, x0:x1] += model #Add to larger model cube #This method just fits a simple line to the spectrum each spaxel; for flat continuum sources. elif mode == 'lineFit': print 'lineFit', #Define custom astropy model class (just a line) @custom_model def line(xx, m=0, c=0): return m * xx + c #Run through pixels in 2D region for yi in range(y0, y1): for xi in range(x0, x1): m_init = line() #Create initial guess model m = fit(m_init, W[usewav], data[usewav, yi, xi]) #Optimize model model = m(W) cmodel[:, yi, xi] += model data[:, yi, xi] -= model #This method extracts a central spectrum and fits it to each spaxel elif mode == 'specFit': print 'specFit', #Define custom astropy model class (just a line) @custom_model def line(xx, m=0, c=0): return m * xx + c ##### GET QSO SPECTRUM q_spec = data[:, yc, xc].copy() q_spec_fit = q_spec[usewav == 1] #Run through slices for yi in range(y0, y1): print yi, sys.stdout.flush() #If this not the main QSO slice if yi != yc: #Extract QSO spectrum for this slice s_spec = data[:, yi, xc].copy() s_spec_fit = s_spec[usewav == 1] #Estimate wavelength shift needed corr = scipy.signal.correlate(s_spec, q_spec) corrs = scipy.ndimage.filters.gaussian_filter1d(corr, 5.0) w_offset = (np.nanargmax(corrs) - len(corrs) / 2) / 2.0 #Find wavelength offset (px) for this slice chisq = lambda x: s_spec_fit[10:-10] - x[ 0] * scipy.ndimage.interpolation.shift( q_spec_fit, x[1], order=4, mode='reflect')[10:-10] p0 = [np.max(s_spec) / np.max(q_spec), w_offset] lbound = [0.0, -5] ubound = [5.1, 5] for j in range(len(p0)): if p0[j] < lbound[j]: p0[j] = lbound[j] elif p0[j] > ubound[j]: p0[j] = ubound[j] p_fit = scipy.optimize.least_squares(chisq, p0, bounds=(lbound, ubound), jac='3-point') A0, dw0 = p_fit.x q_spec_shifted = scipy.ndimage.interpolation.shift( q_spec_fit, dw0, order=3, mode='reflect') else: q_spec_shifted = q_spec_fit A0 = 0.5 dw0 = 0 lbound = [0.0, -5] ubound = [20.0, 5] for xi in range(x0, x1): spec = data[:, yi, xi] spec_fit = spec[usewav == 1] #First fit to find wav offset for this slice chisq = lambda x: spec_fit - x[ 0] * scipy.ndimage.interpolation.shift( q_spec_fit, x[1], order=3, mode='reflect') p0 = [A0, dw0] for j in range(len(p0)): if p0[j] < lbound[j]: p0[j] = lbound[j] elif p0[j] > ubound[j]: p0[j] = ubound[j] #elif abs(p0[j]<1e-6): p0[j]=0 sys.stdout.flush() p_fit = scipy.optimize.least_squares(chisq, p0, bounds=(lbound, ubound), jac='3-point') A, dw = p_fit.x m_spec = A * scipy.ndimage.interpolation.shift( q_spec, dw, order=4, mode='reflect') #Do a linear fit to residual and correct linear errors residual = data[:, yi, xi] - m_spec ydata = residual[usewav == 1] xdata = W[usewav == 1] #m_init = line() #Create initial guess model #m = fit(m_init, xdata, ydata) #Optimize model #linefit = m(W) model = m_spec #residual = data[:,yi,xi] - model if 0 and abs(yi - yc) < 1 and abs(xi - xc) < 1: plt.figure(figsize=(16, 8)) plt.subplot(311) plt.title(r"$A=%.4f,d\lambda=%.3fpx$" % (A, dw)) plt.plot(W, spec, 'bx', alpha=0.5) plt.plot(W[usewav == 1], spec[usewav == 1], 'kx') plt.plot(W, A * q_spec, 'g-', alpha=0.8) plt.plot(W, model, 'r-') plt.xlim([W[0], W[-1]]) plt.ylim([1.5 * min(spec), max(spec) * 1.5]) plt.subplot(312) plt.xlim([W[0], W[-1]]) plt.plot(W, residual, 'gx') plt.ylim([1.5 * min(residual), max(spec) * 1.5]) plt.subplot(313) plt.hist(residual) plt.tight_layout() plt.show() cmodel[:, yi, xi] += model data[:, yi, xi] -= model #ROTATE BACK IF ROTATED AT START if instrument == 'KCWI': data_rot = np.zeros((data.shape[0], data.shape[2], data.shape[1])) cmodel_rot = np.zeros((data.shape[0], data.shape[2], data.shape[1])) for wi in range(len(data)): data_rot[wi] = np.rot90(data[wi], k=3) cmodel_rot[wi] = np.rot90(cmodel[wi], k=3) data = data_rot cmodel = cmodel_rot return data, cmodel
fitsimage_1.info() fits1 = fitsimage_1[0] #print(fits1.data.shape) #print(fits1.header) Image_Data_0 = fits1.data[:,0] Image_Data2_0 = Image_Data_0[325:375] Image_Data_All = fits1.data[:,:] Image_Data_All2 = Image_Data_All[325:375] x = np.linspace(-50,50,50) Gauss_Model = models.Gaussian1D(amplitude = 1000., mean = 0, stddev = 1.) Moffat_Model = models.Moffat1D(amplitude = 1000., x_0 = 0, gamma = 2., alpha = 1.) Fitting_Model = fitting.LevMarLSQFitter() Fit_Data = [] Fit_Data_2 = [] for i in range(0, Image_Data_All2.shape[1]): Fit_Data.append(Fitting_Model(Gauss_Model, x, Image_Data_All2[:,i])) for i in range(0, Image_Data_All2.shape[1]): Fit_Data_2.append(Fitting_Model(Moffat_Model, x, Image_Data_All2[:,i]))
def get_mask(fits, regfile): print "\tGenerating 2D mask from region file" #EXTRACT/CREATE USEFUL VARS############ data3D = fits[0].data head3D = fits[0].header W, Y, X = data3D.shape #Dimensions mask = np.zeros((Y, X), dtype=int) #Mask to be filled in x, y = np.arange(X), np.arange(Y) #Create X/Y image coordinate domains xx, yy = np.meshgrid(x, y) #Create meshgrid of X, Y ww = np.array([ head3D["CRVAL3"] + head3D["CD3_3"] * (i - head3D["CRPIX3"]) for i in range(W) ]) yPS = np.sqrt( np.cos(head3D["CRVAL2"] * np.pi / 180) * head3D["CD1_2"]**2 + head3D["CD2_2"]**2) #X & Y plate scales (deg/px) xPS = np.sqrt( np.cos(head3D["CRVAL2"] * np.pi / 180) * head3D["CD1_1"]**2 + head3D["CD2_1"]**2) fit = fitting.SimplexLSQFitter() #Get astropy fitter class Lfit = fitting.LinearLSQFitter() usewav = np.ones_like(ww, dtype=bool) usewav[ww < head3D["WAVGOOD0"]] = 0 usewav[ww > head3D["WAVGOOD1"]] = 0 data2D = np.sum(data3D[usewav], axis=0) med = np.median(data2D) #BUILD MASK############################ if regfile[0].coord_format == 'image': rr = np.sqrt((xx - x0)**2 + (yy - y0)**2) mask[rr <= R] = i + 1 elif regfile[0].coord_format == 'fk5': #AIC = 2k + n Log(RSS/n) [ - (2k**2 +2k )/(n-k-1) ] def AICc(dat, mod, k): RSS = np.sum((dat - mod)**2) n = np.size(dat) return 2 * k + n * np.log(RSS / n) #+ (2*k**2 + 2*k)/(n-k-1) head2D = head3D.copy() #Create a 2D header by modifying 3D header for key in [ "NAXIS3", "CRPIX3", "CD3_3", "CRVAL3", "CTYPE3", "CNAME3", "CUNIT3" ]: head2D.remove(key) head2D["NAXIS"] = 2 head2D["WCSDIM"] = 2 wcs = WCS(head2D) ra, dec = wcs.wcs_pix2world(xx, yy, 0) #Get meshes of RA/DEC for i, reg in enumerate(regfile): ra0, dec0, R = reg.coord_list #Extract location and default radius rr = np.sqrt( (np.cos(dec * np.pi / 180) * (ra - ra0))**2 + (dec - dec0)**2) #Create meshgrid of distance to source if np.min(rr) > R: continue #Skip any sources more than one default radius outside the FOV else: yc, xc = np.where(rr == np.min(rr)) #Take input position tuple xc, yc = xc[0], yc[0] rx = 2 * int(round( R / xPS)) #Convert angular radius to distance in pixels ry = 2 * int(round(R / yPS)) x0, x1 = max(0, xc - rx), min(X, xc + rx + 1) #Get bounding box for PSF fit y0, y1 = max(0, yc - ry), min(Y, yc + ry + 1) img = np.mean(data3D[usewav, y0:y1, x0:x1], axis=0) #Not strictly a white-light image img -= np.median(img) #Correct in case of bad sky subtraction xdomain, xdata = range(x1 - x0), np.mean( img, axis=0) #Get X and Y domains/data ydomain, ydata = range(y1 - y0), np.mean(img, axis=1) moffat_bounds = {'amplitude': (0, float("inf"))} xMoffInit = models.Moffat1D( max(xdata), x_0=xc - x0, bounds=moffat_bounds) #Initial guess Moffat profiles yMoffInit = models.Moffat1D(max(ydata), x_0=yc - y0, bounds=moffat_bounds) xLineInit = models.Linear1D(slope=0, intercept=np.mean(xdata)) yLineInit = models.Linear1D(slope=0, intercept=np.mean(ydata)) xMoffFit = fit(xMoffInit, xdomain, xdata) #Fit Moffat1Ds to each axis yMoffFit = fit(yMoffInit, ydomain, ydata) xLineFit = Lfit(xLineInit, xdomain, xdata) #Fit Linear1Ds to each axis yLineFit = Lfit(yLineInit, ydomain, ydata) kMoff = len(xMoffFit.parameters ) #Get number of parameters in each model kLine = len(xLineFit.parameters) xMoffAICc = AICc( xdata, xMoffFit(xdomain), kMoff) #Get Akaike Information Criterion for each xLineAICc = AICc(xdata, xLineFit(xdomain), kLine) yMoffAICc = AICc(ydata, yMoffFit(ydomain), kMoff) yLineAICc = AICc(ydata, yLineFit(ydomain), kLine) xIsMoff = xMoffAICc < xLineAICc # Determine if Moffat is a better fit than a simple line yIsMoff = yMoffAICc < yLineAICc if xIsMoff and yIsMoff: #If source has detectable moffat profile (i.e. bright source) expand mask xfwhm = xMoffFit.gamma.value * 2 * np.sqrt( 2**(1 / xMoffFit.alpha.value) - 1) #Get FWHMs yfwhm = yMoffFit.gamma.value * 2 * np.sqrt( 2**(1 / yMoffFit.alpha.value) - 1) R = 2 * max(xfwhm * xPS, yfwhm * yPS) mask[rr < R] = i + 1 return mask
# ------Gaussian smoothing for tip-tilt uncertainties---------- final_kernel = con.convolve(added_kernel, gaussian_kernel, normalize_kernel=True) if not plot_breakdown: final_kernel.normalize(mode='integral') if plot2d: fig = plot_kernel(final_kernel, 'Convolved') # ------plotting 1D cross-sections of the 2D kernels---------- portion = int(fraction_to_plot * (size/2)) + 1 if plot_breakdown: ax.plot(np.arange(portion), airy_kernel.array[size/2][size/2 : size/2 + portion], c='r', lw=1, label='Airy (%.2F")'%radius_arcsec) ax.plot(np.arange(portion), moffat_kernel.array[size/2][size/2 : size/2 + portion], c='brown', lw=1, label='Moffat (%.2F")'%fwhm_arcsec) ax.plot(np.arange(portion), added_kernel.array[size/2][size/2 : size/2 + portion], c='gray', lw=1, label='Airy + Moffat', linestyle='--') ax.plot(np.arange(portion), gaussian_kernel.array[size/2][size/2 : size/2 + portion], c='g', lw=1, label='Gaussian (%.2F")'%gaussian_blur_arcsec) fwhm_guess = 0.1#fwhm_arcsec / 4. gamma_guess = (fwhm_guess/arcsec_per_pixel)/(2 * np.sqrt(np.power(2, 1./beta) - 1)) fitted_moffat = fitting.LevMarLSQFitter()(models.Moffat1D(x_0=0, alpha=beta, gamma=gamma_guess, \ fixed={'alpha':True, 'x_0':True, 'gamma':False, 'amplitude':False}), \ np.arange(portion), final_kernel.array[size / 2][size / 2: size / 2 + portion]) print fitted_moffat # fitted_moffat_fwhm_pix = fitted_moffat.gamma * (2 * np.sqrt(np.power(2, 1./fitted_moffat.alpha) - 1)) fitted_moffat_fwhm_arcsec = fitted_moffat_fwhm_pix * arcsec_per_pixel ax.plot(np.arange(portion), fitted_moffat(np.arange(portion)), c='goldenrod', lw=1, label='Fitted Moffat (%.2F")'%fitted_moffat_fwhm_arcsec) rms = np.sqrt(np.sum((fitted_moffat(np.arange(portion)) - final_kernel.array[size / 2][size / 2: size / 2 + portion])**2)) print 'RMS = %.3E'%rms # label = 'Convolved: Strehl %.1F'%strehl else: label = '%s band: Strehl %.1F'%(band, strehl) ax.plot(np.arange(portion), final_kernel.array[size/2][size/2 : size/2 + portion], lw=2, label=label) ax.legend() ax.set_xlim(0, 0.2 / arcsec_per_pixel) ax.set_xticklabels(['%.2F' % (float(item) * arcsec_per_pixel) for item in ax.get_xticks()])
def gausMoffatModel(pars): moffatAmp, x0, gamma, alpha, gausAmp, mu, gausSigma = pars moff = models.Moffat1D(amplitude=moffatAmp, x_0=x0, gamma=gamma, alpha=alpha) gaus = models.Gaussian1D(amplitude=gausAmp, mean=mu, stddev=gausSigma) model = gaus + moff return model
def fit_moffat_1d(data, gamma=2., alpha=1., sigma_factor=0., center_at=None, weighted=False): """Fit a 1D moffat profile to the data and return the fit. Parameters ---------- data: 2D data array The input sigma to use gamma: float (optional) The input gamma to use alpha: float (optional) The input alpha to use sigma_factor: float (optional) If sigma>0 then sigma clipping of the data is performed at that level center_at: float or None None by default, set to value to use as center weighted: bool if weighted is True, then weight the values by basic uncertainty hueristic Returns ------- The fitted 1D moffat model for the data """ # data is assumed to already be chunked to a reasonable size ldata = len(data) # guesstimate mean if center_at: x0 = 0 else: x0 = int(ldata / 2.) # assumes negligable background if weighted: z = np.nan_to_num(1. / np.sqrt(data)) # use as weight x = np.arange(ldata) # Initialize the fitter fitter = fitting.LevMarLSQFitter() if sigma_factor > 0: fit = fitting.FittingWithOutlierRemoval(fitter, sigma_clip, niter=3, sigma=sigma_factor) else: fit = fitter # Moffat1D + constant model = (models.Moffat1D( amplitude=max(data), x_0=x0, gamma=gamma, alpha=alpha) + models.Polynomial1D(c0=data.min(), degree=0)) with warnings.catch_warnings(): # Ignore model linearity warning from the fitter warnings.simplefilter('ignore') if weighted: results = fit(model, x, data, weights=z) else: results = fit(model, x, data) # previous yield amp, ycenter, xcenter, sigma, offset # if sigma clipping is used, results is a tuple of data, model if sigma_factor > 0: return results[1] else: return results