def get_hpd_from_image_fit(image, pix_size=0.020, arcsec=arcsec): cdisp_dir_counts = sum(image, axis=0) fit_levmar = fitting.SimplexLSQFitter() gmodel = models.Gaussian1D() g = fit_levmar(gmodel, range(len(cdisp_dir_counts)), cdisp_dir_counts, acc=1e-6) return g.stddev * 2.35 * pix_size / arcsec, cdisp_dir_counts, g( range(len(cdisp_dir_counts)))
def get_fwhm_from_image_fit(image, pix_size=0.020, arcsec=arcsec): disp_dir_counts = sum(image, axis=1) fit_levmar = fitting.SimplexLSQFitter() lmodel = models.Lorentz1D() lmodel.amplitude, lmodel.x0, lmodel.fwhm = max(disp_dir_counts), float( len(disp_dir_counts)) / 2, 2.35 l = fit_levmar(lmodel, range(len(disp_dir_counts)), disp_dir_counts, acc=1e-6) return l.fwhm * pix_size / arcsec, disp_dir_counts, l( range(len(disp_dir_counts)))
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)
xx = np.linspace(np.min(asc), np.max(asc), 1000) #----------------------------------------------------------------- # #--------------------Fitting using Astropy------------------------ # #----------------------------------------------------------------- print('-----------------------------------------') print(' ') print(' Astropy Fitting ') print(' ') print('-----------------------------------------') s1 = src() fit_s1 = fitting.SimplexLSQFitter() s1_fit = fit_s1(s1, x=asc, y=sb, maxiter=10000, weights=1/sbe) print(' ') print(s1_fit) print(' ') residuals = sb - s1_fit(asc) #----------For figure fig1 = plt.figure(figsize = (8,10)) gs1 = gd.GridSpec(2, 1, height_ratios = [4,1]) ax1 = plt.subplot(gs1[0]) ax1.errorbar(asc, sb, yerr=sbe, color='orangered', fmt='.', alpha=0.5, label='Data') ax1.plot(xx, s1_fit(xx), color='orangered', label='Best fit Sersic profile') ax1.grid()
class AstropyQSpectraFit(ProcessingPlugin): name = 'Q Fit (Astropy)' q = InOut(description='Q bin center positions', type=np.array) Iq = InOut(description='Q spectra bin intensities', type=np.array) model = Input(description='Fittable model class in the style of Astropy', type=Enum) domainmin = Input(description='Min bound on the domain of the input data', type=float) domainmax = Input(description='Max bound on the domain of the input data', type=float) fitter = Input(description='Fitting algorithm', default=fitting.LevMarLSQFitter(), type=Enum, limits={'Linear LSQ': fitting.LinearLSQFitter(), 'Levenberg-Marquardt LSQ': fitting.LevMarLSQFitter(), 'SLSQP LSQ': fitting.SLSQPLSQFitter(), 'Simplex LSQ': fitting.SimplexLSQFitter()}) fittedmodel = Output(description='A new model with the fitted parameters; behaves as parameterized function', type=Fittable1DModel) fittedprofile = Output( description='The fitted profile from the evaluation of the resulting model over the input range.') hints = [PlotHint(q, Iq), PlotHint(q, fittedprofile)] modelvars = {} def __init__(self): super(AstropyQSpectraFit, self).__init__() self.model.limits = {plugin.name: plugin.plugin_object for plugin in pluginmanager.getPluginsOfCategory('Fittable1DModelPlugin')} self.model.value = list(self.model.limits.values())[0] @property def parameter(self): # clear cache in for input in self.modelvars: del self.__dict__[input] varcache = self.modelvars.copy() self.modelvars = {} self._inputs = None self._inverted_vars = None if hasattr(self, '_inverted_vars'): del self._inverted_vars for name in self.model.value.param_names: param = getattr(self.model.value, name) # TODO: CHECK NAMESPACE if name in varcache: input = varcache[name] else: input = InOut(name=name, default=param.default, limits=param.bounds, type=float, fixed=False, fixable=True) setattr(self, name, input) self.modelvars[name] = input parameter = super(AstropyQSpectraFit, self).parameter parameter.child('model').sigValueChanged.connect(self.reset_parameter) return parameter def reset_parameter(self): # cache old parameter oldparam = self._param # empty it for child in oldparam.children(): child.remove() # reset attribute so new parameter is generated self._param = None # add new children to old parameter for child in self.parameter.children(): # type: Parameter oldparam.addChild(child) # set old parameter to attribute self._param = oldparam def evaluate(self): if self.model.value is None or self.model.value == '----': return norange = self.domainmin.value == self.domainmax.value if self.domainmin.value is None and self.q.value is not None or norange: # truncate the q and I arrays with limits self.domainmin.value = self.q.value.min() if self.domainmax.value is None and self.q.value is not None or norange: # truncate the q and I arrays with limits self.domainmax.value = self.q.value.max() for name, input in self.modelvars.items(): # propogate user-defined values to the model getattr(self.model.value, name).value = input.value getattr(self.model.value, name).fixed = input.fixed filter = np.logical_and(self.domainmin.value <= self.q.value, self.q.value <= self.domainmax.value) q = self.q.value[filter] Iq = self.Iq.value[filter] self.fittedmodel.value = self.fitter.value(self.model.value, q, Iq) self.fittedprofile.value = self.fittedmodel.value(self.q.value) def getCategory() -> str: return "Fits"
def goodimage(self, data, camera, psfx=1.3, fitmax=.2, xrange=[1024, 2048], yrange=[512, 1536], maxp=50000., nplot=100, background=None, noise=None, sources=None, model='simp', det_sigma=5.0, base_fwhm=8.0, plot_stuff=False, expid=None): ''' test if an image has a star with a good psf in its center ''' log = logging.getLogger('star_sizes') from astropy.modeling import models, fitting if background == None or noise == None: log.debug("Finding background level...") mean, median, std = sigma_clipped_stats(data, sigma=5.0, iters=2) background = median noise = std print(background, noise) global cic_count global cis_count global cin_count global ciw_count global cie_count # Detect sources #print('base fwhm'+str(base_fwhm)) if sources == None: log.debug("Detecting sources..") daofind = DAOStarFinder(fwhm=base_fwhm, threshold=det_sigma * noise) sources = daofind(data - background) #print('found '+str(len(sources))+' sources') # Sort by SN w = sources['peak'] < maxp sources = sources[w] sources.sort('flux') sources.reverse() fwhm_array = [] fwhm_weight_array = [] x_coordinate = [] y_coordinate = [] final_func_val = [] if len(sources) > nplot: sources = sources[:nplot] #print(len(sources)) model_init = models.Gaussian1D(amplitude=1.0, mean=0, stddev=base_fwhm) for isource in sources: flux_counts = [] pixel_distance = [] npix = 0 x_cen = int(isource['xcentroid']) y_cen = int(isource['ycentroid']) ##print(x_cen,y_cen) if x_cen >= xrange[0] and x_cen < xrange[1] and y_cen >= yrange[ 0] and y_cen < yrange[1]: analysis_radius = 20 #print(isource['peak']) try: for x in range(x_cen - analysis_radius, x_cen + analysis_radius): for y in range(y_cen - analysis_radius, y_cen + analysis_radius): #flux_counts.append((data[y][x] - 0.) / isource['peak']) val = (data[y][x] - background) / isource['peak'] flux_counts.append(val) dis = numpy.linalg.norm((isource['xcentroid'] - x, isource['ycentroid'] - y)) pixel_distance.append(dis) flux_counts = np.array(flux_counts) pixel_distance = np.array(pixel_distance) w = (flux_counts > 0.5) #& (flux_counts < 0.65) psel = pixel_distance[w] fsel = flux_counts[w] mpsel = np.mean(psel) wn = psel < 2. * mpsel psel = psel[wn] fsel = fsel[wn] A = np.vstack([fsel, np.ones(len(psel))]).T m, c = np.linalg.lstsq(A, psel, rcond=None)[0] ssq = sum((psel - m * fsel + c)**2.) / float(len(psel)) fitter = fitting.SimplexLSQFitter() fitted_model = fitter(model_init, psel, fsel) iFWHM = 2.355 * fitted_model.stddev * self.pix_scale * self.micron_to_seeing[ camera] #print(np.mean(psel),m*.5+c,ssq,iFWHM,fitter.fit_info['final_func_val']) if iFWHM < psfx and fitter.fit_info[ 'final_func_val'] < fitmax: return True except: pass #print('hit edge?') return False
def estimate_fwhm(self, data, camera, nstar=10, background=None, noise=None, sources=None, model='simp', maxfitv=1., det_sigma=5.0, base_fwhm=8.0, plot_stuff=False, expid=None): '''estimate fwhm for a single image plot_stuff:plot the fit for each single source return:fwhm of this image''' log = logging.getLogger('star_sizes') if background == None or noise == None: log.debug("Finding background level...") mean, median, std = sigma_clipped_stats(data, sigma=5.0, iters=2) background = median noise = std print(background, noise) global cic_count global cis_count global cin_count global ciw_count global cie_count # Detect sources #print('base fwhm'+str(base_fwhm)) if sources == None: log.debug("Detecting sources..") daofind = DAOStarFinder(fwhm=base_fwhm, threshold=det_sigma * noise) sources = daofind(data - background) print('found ' + str(len(sources)) + ' sources') # Sort by SN sources.sort('flux') sources.reverse() fwhm_array = [] fwhm_weight_array = [] x_coordinate = [] y_coordinate = [] final_func_val = [] if len(sources) > nstar: sources = sources[:nstar] model_init = models.Gaussian1D(amplitude=1.0, mean=0, stddev=base_fwhm) for isource in sources: flux_counts = [] pixel_distance = [] npix = 0 x_cen = int(isource['xcentroid']) y_cen = int(isource['ycentroid']) ##print(x_cen,y_cen) analysis_radius = 20 #print(isource['peak']) try: for x in range(x_cen - analysis_radius, x_cen + analysis_radius): for y in range(y_cen - analysis_radius, y_cen + analysis_radius): #flux_counts.append((data[y][x] - 0.) / isource['peak']) val = (data[y][x] - background) / isource['peak'] flux_counts.append(val) dis = numpy.linalg.norm((isource['xcentroid'] - x, isource['ycentroid'] - y)) pixel_distance.append(dis) flux_counts = np.array(flux_counts) pixel_distance = np.array(pixel_distance) w = (flux_counts > 0.5) #& (flux_counts < 0.65) psel = pixel_distance[w] fsel = flux_counts[w] mpsel = np.mean(psel) wn = psel < 3. * mpsel psel = psel[wn] fsel = fsel[wn] #A = np.vstack([fsel, np.ones(len(psel))]).T #m, c = np.linalg.lstsq(A, psel, rcond=None)[0] #ssq = sum((psel-m*fsel+c)**2.)/float(len(psel)) fitter = fitting.SimplexLSQFitter() fitted_model = fitter(model_init, psel, fsel) fitv = fitter.fit_info['final_func_val'] iFWHM = 2.355 * fitted_model.stddev * self.pix_scale * self.micron_to_seeing[ camera] #if iFWHM/(m*.5+c) > 1: # fitv = 999 #print(np.mean(psel),m*.5+c,ssq,iFWHM,fitv) #fo.write(str(x_cen)+' '+str(y_cen)+' '+str(iFWHM)+ ' '+str(fitter.fit_info['final_func_val'])+' '+str(isource['peak'])+'\n') #plt.plot(pixel_distance,flux_counts,'k-') #plt.plot(psel,fsel,'ro') #plt.show() #if val > 0.5: # print(dis) log.debug("\n>>>>> New source") log.debug(("Fit value:", fitter.fit_info['final_func_val'])) log.debug(("SN:", isource['flux'] * det_sigma)) log.debug(("single FWHM estimated: ", iFWHM)) #if fitv < maxfitv: #print('final value less than 100, info going into arrays') x_coordinate.append(x_cen) y_coordinate.append(y_cen) fwhm_array.append(iFWHM) final_func_val.append(fitv) #print(iFWHM,fitter.fit_info['final_func_val']) fwhm_weight_array.append(isource['flux'] * det_sigma / fitter.fit_info['final_func_val']) #else: #print('final value greater than 100, no info for you') ##print(fitter.fit_info['final_func_val']) #if fitter.fit_info['final_func_val'] < 5.0: # fwhm_array.append(iFWHM) # fwhm_weight_array.append(isource['flux'] * det_sigma / fitter.fit_info['final_func_val']) # print(fitv) # color = 'red' except: #print('exception') pass if np.min(final_func_val) > maxfitv: maxfitv = 1.01 * np.min( final_func_val) #ensure at least one point gets fit if len(fwhm_array) > 0: print(str(len(fwhm_array)) + ' stars found with some fwhm') #thredshold = np.array(fwhm_weight_array).max()*0.1 fwhm_array_new = np.array(fwhm_array)[ np.array(final_func_val) < maxfitv] print(str(len(fwhm_array_new)) + ' stars found with accepted fwhm') fwhm_weight_array_new = np.array(fwhm_weight_array)[ np.array(final_func_val) < maxfitv] x_coordinate_new = np.array(x_coordinate)[ np.array(final_func_val) < maxfitv] y_coordinate_new = np.array(y_coordinate)[ np.array(final_func_val) < maxfitv] final_func_val_new = np.array(final_func_val)[ np.array(final_func_val) < maxfitv] #FWHM_estimation = numpy.average(fwhm_array_new, weights=fwhm_weight_array_new) FWHM_estimation = numpy.median(fwhm_array_new) log.info('Final FWHM estimated is %.2f"' % FWHM_estimation) if len(fwhm_array) > 0: return FWHM_estimation, len(fwhm_array_new) else: return FWHM_estimation, None
def return_sources(self, data, camera, maxp=50000., nplot=1000, background=None, noise=None, sources=None, model='simp', det_sigma=5.0, base_fwhm=8.0, plot_stuff=False, expid=None): ''' plot light distribution for top ten sources ''' log = logging.getLogger('star_sizes') from astropy.modeling import models, fitting if background == None or noise == None: log.debug("Finding background level...") mean, median, std = sigma_clipped_stats(data, sigma=5.0, iters=2) background = median noise = std #print(background, noise) global cic_count global cis_count global cin_count global ciw_count global cie_count # Detect sources #print('base fwhm'+str(base_fwhm)) if sources == None: log.debug("Detecting sources..") daofind = DAOStarFinder(fwhm=base_fwhm, threshold=det_sigma * noise) sources = daofind(data - background) #print('found '+str(len(sources))+' sources') # Sort by SN w = sources['peak'] < maxp sources = sources[w] sources.sort('flux') sources.reverse() fwhm_array = [] fwhm_weight_array = [] x_coord = [] y_coord = [] final_func_val = [] if len(sources) > nplot: sources = sources[:nplot] #print(len(sources)) model_init = models.Gaussian1D(amplitude=1.0, mean=0, stddev=base_fwhm) for isource in sources: flux_counts = [] pixel_distance = [] npix = 0 x_cen = int(isource['xcentroid']) y_cen = int(isource['ycentroid']) ##print(x_cen,y_cen) analysis_radius = 20 #print(isource['peak']) try: for x in range(x_cen - analysis_radius, x_cen + analysis_radius): for y in range(y_cen - analysis_radius, y_cen + analysis_radius): #flux_counts.append((data[y][x] - 0.) / isource['peak']) val = (data[y][x] - background) / isource['peak'] flux_counts.append(val) dis = numpy.linalg.norm((isource['xcentroid'] - x, isource['ycentroid'] - y)) pixel_distance.append(dis) flux_counts = np.array(flux_counts) pixel_distance = np.array(pixel_distance) w = (flux_counts > 0.5) #& (flux_counts < 0.65) psel = pixel_distance[w] fsel = flux_counts[w] mpsel = np.mean(psel) wn = psel < 2. * mpsel psel = psel[wn] fsel = fsel[wn] #A = np.vstack([fsel, np.ones(len(psel))]).T #m, c = np.linalg.lstsq(A, psel, rcond=None)[0] #ssq = sum((psel-m*fsel+c)**2.)/float(len(psel)) fitter = fitting.SimplexLSQFitter() fitted_model = fitter(model_init, psel, fsel) iFWHM = 2.355 * fitted_model.stddev * self.pix_scale * self.micron_to_seeing[ camera] x_coord.append(x_cen) y_coord.append(y_cen) fwhm_array.append(iFWHM) final_func_val.append(fitter.fit_info['final_func_val']) #print(np.mean(psel),m*.5+c,ssq,iFWHM,fitter.fit_info['final_func_val']) #fo.write(str(x_cen)+' '+str(y_cen)+' '+str(iFWHM)+ ' '+str(fitter.fit_info['final_func_val'])+' '+str(isource['peak'])+'\n') #plt.plot(pixel_distance,flux_counts,'k-') #plt.plot(psel,fsel,'ro') #plt.show() except: #print('hit edge?') pass #fo.close() return np.array(x_coord), np.array(y_coord), np.array( fwhm_array), np.array(final_func_val)
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
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
uv_mean_structure_function = np.loadtxt( f'sf_data/uv/structure_function_most_recent')[2:] uv_std_structure_function = np.loadtxt( f'sf_data/uv/errors_most_recent')[2:] # Fit the GP UVW2 structure functions using a power law or broken power law power_law_model = PowerLaw1D() if kernel == 'RQ': broken_power_law_model = BrokenPowerLaw1D(alpha_1=0, alpha_2=0, x_break=110) else: broken_power_law_model = BrokenPowerLaw1D(alpha_1=-1, alpha_2=0, x_break=110) fitter_plm = fitting.SimplexLSQFitter() power_law_tao_vals = uv_gp_tao_plot uv_gp_mean_structure_function *= 1e29 # multiply by a large number to prevent error in fitting power laws. uv_gp_std_structure_function *= 1e29 power_law_sf_vals = uv_gp_mean_structure_function # Large number of iterations required for optimiser convergence (maxiter argument) # Use uncertainties to improve the fit as per this tutorial: # https://docs.astropy.org/en/stable/modeling/example-fitting-line.html#fit-using-uncertainties plm = fitter_plm(broken_power_law_model, power_law_tao_vals, power_law_sf_vals, weights=1 / uv_gp_std_structure_function, maxiter=5000)
def fit_models(input_model: Fittable2DModel, pixelphase=0., n_sources=4, img_size=128, img_border=16, σ=0, λ=None, fitshape=(7, 7), model_oversampling=2, model_degree=5, model_mode='grid', fit_accuracy=1e-8, use_weights=True, fitter_name='LM', return_imgs=False, fit_bounds=None, seed=0) -> dict: rng = np.random.default_rng(seed) if fitter_name == 'LM': iter_name = 'nfev' fitter = fitting.LevMarLSQFitter() elif fitter_name == 'Simplex': iter_name = 'numiter' fitter = fitting.SimplexLSQFitter() else: raise NotImplementedError fitshape = np.array(fitshape) img, xy_sources = gen_image(input_model, n_sources, img_size, img_border, pixelphase, σ, λ, rng) fit_model = make_fit_model(input_model, model_mode, fitshape, model_degree, model_oversampling) y_grid, x_grid = np.indices(img.shape) res = np.full((len(xy_sources), 7), np.nan) residual = img.copy() for i, xy_position in enumerate(xy_sources): fitted = fit_single_image(fit_model, img, x_grid, y_grid, xy_position, fitter, fitshape, σ, λ, fit_accuracy, use_weights, fit_bounds, rng) res[i] = (xy_position[0], xy_position[1], fitted.x.value, fitted.y.value, fitted.flux.value, fitted.snr, fitter.fit_info[iter_name]) residual -= fitted(x_grid, y_grid) x_dev = res[:, 0] - res[:, 2] y_dev = res[:, 1] - res[:, 3] dev = np.sqrt(x_dev**2 + y_dev**2) ret = {'x': res[:, 0], 'y': res[:, 1], 'dev': dev, 'x_dev': x_dev, 'y_dev': y_dev, 'flux': res[:, 4], 'snr': res[:, 5], 'fititers': res[:, 6], 'peak_flux': np.max(img)} if return_imgs: ret |= {'img': img, 'residual': residual} return ret