def fit(im, impsf, hpsf, xpos=None, ypos=None, radius=32): x = xpos y = ypos ny, nx = np.shape(im) ixlo, iylo = int(x - radius), int(y - radius) if ixlo < 0: ixlo = 0 if iylo < 0: iylo = 0 ixhi = int(x + radius) + 1 iyhi = int(y + radius) + 1 if ixhi > (nx - 1): ixhi = nx - 1 if iyhi > (ny - 1): iyhi = ny - 1 ixx = ixhi - ixlo + 1 iyy = iyhi - iylo + 1 dx = np.arange(ixx) + ixlo - x dy = np.arange(iyy) + iylo - y gauss = [ hpsf['GAUSS1'], hpsf['GAUSS2'], hpsf['GAUSS3'], hpsf['GAUSS4'], hpsf['GAUSS5'] ] dx = dx.reshape(1, len(dx)) dy = dy.reshape(len(dy), 1) dx = rebin.rebin(dx, [np.shape(dx)[1], np.shape(dx)[1]]) dy = rebin.rebin(dy, [len(dy), len(dy)]) try: model = dao_value.dao_value(dx, dy, gauss, impsf, deriv=False) except: return 1, 1, 0, 0, False, 0, 0, 0 subim = im[iylo - 1:iyhi, ixlo - 1:ixhi] model = model / 10**(-0.4 * (hpsf['PSFMAG'] - 25)) return model, subim
def fit(im, impsf,hpsf,xpos=None, ypos=None, radius=32): x = xpos y = ypos ny, nx = np.shape(im) ixlo, iylo = int(x - radius), int(y - radius) if ixlo < 0: ixlo = 0 if iylo < 0: iylo = 0 ixhi = int(x + radius) + 1 iyhi = int(y + radius) + 1 if ixhi > (nx - 1): ixhi = nx - 1 if iyhi > (ny - 1): iyhi = ny - 1 ixx = ixhi - ixlo + 1 iyy = iyhi - iylo + 1 dx = np.arange(ixx) + ixlo - x dy = np.arange(iyy) + iylo - y gauss = [hpsf['GAUSS1'], hpsf['GAUSS2'], hpsf['GAUSS3'], hpsf['GAUSS4'], hpsf['GAUSS5']] dx = dx.reshape(1, len(dx)) dy = dy.reshape(len(dy), 1) dx = rebin.rebin(dx, [np.shape(dx)[1], np.shape(dx)[1]]) dy = rebin.rebin(dy, [len(dy), len(dy)]) try: model = dao_value.dao_value(dx, dy, gauss, impsf, deriv=False) except: return 1, 1, 0, 0, False, 0, 0, 0 subim = im[iylo - 1:iyhi, ixlo - 1:ixhi] model = model/ 10 ** (-0.4 * (hpsf['PSFMAG'] - 25)) return model,subim
def rdpsf(psfname): """Read the FITS file created by GETPSF in the DAOPHOT sequence Combines the Gaussian with the residuals to create an output PSF array. psf,hpsf = rdpsf.rdpsf( PSFname ) INPUTS: PSFname - string giving the name of the FITS file containing the PSF residuals RETURNS: psf - array containing the actual PSF hpsf - header associated with psf PROCEDURES CALLED: DAO_VALUE() REVISION HISTORY: Written W. Landsman December, 1988 Checked for IDL Version 2 J. Isensee & J. Hill December, 1990 Converted to IDL V5.0 W. Landsman September, 1997 Converted to Python D. Jones January, 2014 """ resid = pyfits.getdata(psfname) hpsf = pyfits.getheader(psfname) gauss1 = hpsf['GAUSS1'] #Get Gaussian parameters (5) gauss2 = hpsf['GAUSS2'] # gauss3 = hpsf['GAUSS3'] # gauss4 = hpsf['GAUSS4'] # gauss5 = hpsf['GAUSS5'] # gauss = [gauss1, gauss2, gauss3, gauss4, gauss5] psfrad = hpsf['PSFRAD'] # Get PSF radius npsf = 2 * psfrad + 1 #hpsf['NAXIS1'] # Width of output array containing PSF psf = np.zeros([npsf, npsf]) # Create output array dx = np.arange( npsf, dtype='int') - psfrad # Vector gives X distance from center of array dy = np.arange(npsf, dtype='int') - psfrad # Ditto for dy ny = len(dy) nx = len(dx) dx = dx.reshape(1, nx) dy = dy.reshape(ny, 1) dx = rebin(dx, [ny, nx]) dy = rebin(dy, [ny, nx]) psf = psf + dao_value.dao_value( dx, dy, gauss, resid, deriv=False) #Compute DAOPHOT value at each point hpsf['NAXIS1'] = npsf hpsf['NAXIS2'] = npsf return (psf, hpsf)
def rdpsf(psfname): """Read the FITS file created by GETPSF in the DAOPHOT sequence Combines the Gaussian with the residuals to create an output PSF array. psf,hpsf = rdpsf.rdpsf( PSFname ) INPUTS: PSFname - string giving the name of the FITS file containing the PSF residuals RETURNS: psf - array containing the actual PSF hpsf - header associated with psf PROCEDURES CALLED: DAO_VALUE() REVISION HISTORY: Written W. Landsman December, 1988 Checked for IDL Version 2 J. Isensee & J. Hill December, 1990 Converted to IDL V5.0 W. Landsman September, 1997 Converted to Python D. Jones January, 2014 """ resid=pyfits.getdata(psfname) hpsf = pyfits.getheader(psfname) gauss1 = hpsf['GAUSS1'] #Get Gaussian parameters (5) gauss2 = hpsf['GAUSS2'] # gauss3 = hpsf['GAUSS3'] # gauss4 = hpsf['GAUSS4'] # gauss5 = hpsf['GAUSS5'] # gauss=[gauss1,gauss2,gauss3,gauss4,gauss5] psfrad = hpsf['PSFRAD'] # Get PSF radius npsf = 2*psfrad + 1 #hpsf['NAXIS1'] # Width of output array containing PSF psf = np.zeros([npsf,npsf]) # Create output array dx = np.arange(npsf,dtype='int') - psfrad # Vector gives X distance from center of array dy = np.arange(npsf,dtype='int') - psfrad # Ditto for dy ny = len(dy) nx = len(dx) dx = dx.reshape(1,nx) dy = dy.reshape(ny,1) dx = rebin(dx, [ny, nx]) dy = rebin(dy, [ny, nx]) psf = psf + dao_value.dao_value(dx,dy,gauss,resid,deriv=False) #Compute DAOPHOT value at each point hpsf['NAXIS1'] = npsf hpsf['NAXIS2'] = npsf return(psf,hpsf)
def mkpsfimage(psfmodel, x, y, size, fluxscale=1): """ Construct a numpy array with the psf model appropriately scaled, using the gaussian parameters from the header and the residual components from the image array of the given fits file :param psfmodel: the psf model, provided as either (a) a fits file containing the psf model with gaussian parameters in the header and a lookup table of residual values in the data array, or (b) a tuple with the list of gaussian parameters as the first value and the lookup table in the second, as returned by getpsfmodel :param x,y: float values in the range [0,1) giving the sub-pixel position for the desired center of the psf in the output image :param size: width and height in pixels for the output image showing the psf realization :param fluxscale: scale the output psf image to have this total value for the flux, summed across all pixels in the output image :return: a numpy array holding the psf image realization """ # require stampsize to be odd, so the central pixel contains # the peak of the psf. Define dx,dy as the half-width of the stamp on # either side of the central row/column assert isinstance(size, int) if size % 2 == 0: size += 1 dx = dy = (size - 1) / 2 gaussparam, lookuptable, psfmag, psfzpt = rdpsfmodel(psfmodel) # make a 2D data array with the realized psf image (gaussian+residuals) xx = np.tile(np.arange(-dx, dx + 1, 1, float), (size, 1)) yy = np.tile(np.arange(-dy, dy + 1, 1, float), (size, 1)).T psfimage = dao_value(xx - x, yy - y, gaussparam, lookuptable, deriv=False) psfstarflux = 10 ** (-0.4 * (psfmag - psfzpt)) psfimage *= fluxscale / psfstarflux return psfimage
def pkfit(self, scale, x, y, sky, radius, debug=False, xyout=False, maxiter=25, recenter=True): f = self.f gauss = self.gauss psf = self.psf fnoise = self.fnoise fmask = self.fmask if f.dtype != 'float64': f = f.astype('float64') # psf1d = psf.reshape(shape(psf)[0]**2.) s = shape(f) #Get array dimensions nx = s[1] ny = s[0] #Initialize a few things for the solution redo = 0 pkerr = 0.027 / (gauss[3] * gauss[4])**2. clamp = zeros(3) + 1. dtold = zeros(3) niter = 0 chiold = 1. if debug: print('PKFIT: ITER X Y SCALE ERRMAG CHI SHARP') loop = True while loop: #Begin the big least-squares loop niter = niter + 1 if isnan(x) or isnan(y): scale = np.nan errmag = np.nan chi = np.nan sharp = np.nan if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) ixlo = int(x - radius) if ixlo < 0: ixlo = 0 #Choose boundaries of subarray containing iylo = int(y - radius) if iylo < 0: iylo = 0 # 3points inside the fitting radius ixhi = int(x + radius) + 1 if ixhi > (nx - 1): ixhi = nx - 1 iyhi = int(y + radius) + 1 if iyhi > ny - 1: iyhi = ny - 1 ixx = ixhi - ixlo + 1 iyy = iyhi - iylo + 1 dy = arange( iyy) + iylo - y #X distance vector from stellar centroid dysq = dy**2. dx = arange(ixx) + ixlo - x dxsq = dx**2. rsq = zeros([iyy, ixx]) #RSQ - array of squared rsq = array([(dxsq + dysqj) / radius**2 for dysqj in dysq]) # The fitting equation is of the form # # Observed brightness = # SCALE + delta(SCALE) * PSF + delta(Xcen)*d(PSF)/d(Xcen) + # delta(Ycen)*d(PSF)/d(Ycen) # # and is solved for the unknowns delta(SCALE) ( = the correction to # the brightness ratio between the program star and the PSF) and # delta(Xcen) and delta(Ycen) ( = corrections to the program star's # centroid). # # The point-spread function is equal to the sum of the integral under # a two-dimensional Gaussian profile plus a value interpolated from # a look-up table. good = where(rsq < 1.) if fnoise: good = good[where(fnoise[iylo:iyhi + 1, ixlo:ixhi + 1] > 0)] if fmask: good = good[where(fmask[iylo:iyhi + 1, ixlo:ixhi + 1] == 0)] ngood = len(good[0]) if ngood < 1: ngood = 1 t = zeros([3, ngood]) if not len(good[0]): scale = np.nan errmag = np.nan chi = np.nan sharp = np.nan if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) dx = dx[good[1]] dy = dy[good[0]] model, dvdx, dvdy = dao_value.dao_value( dx, dy, gauss, psf, #psf1d=psf1d, deriv=True) #,ps1d=True) if debug: print('model created ') if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) t[0, :] = model sa = shape(dvdx) if sa[0] > ngood or len(sa) == 0: scale = 0 if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) t[1, :] = -scale * dvdx t[2, :] = -scale * dvdy fsub = f[iylo:iyhi + 1, ixlo:ixhi + 1] fsub = fsub[good[0], good[1]] if fnoise: # D. Jones - noise addition from Scolnic fsubnoise = fnoise[iylo:iyhi + 1, ixlo:ixhi + 1] fsubnoise = fsubnoise[good[0], good[1]] sig = fsubnoise[:] sigsq = fsubnoise**2. rsq = rsq[good[0], good[1]] # Scolnic Added!!! # yx = zeros(1) yx[0] = sky skys = yx[0] sky = skys df = fsub - scale * model - sky #Residual of the brightness from the PSF fit # The expected random error in the pixel is the quadratic sum of # the Poisson statistics, plus the readout noise, plus an estimated # error of 0.75% of the total brightness for the difficulty of flat- # fielding and bias-correcting the chip, plus an estimated error of # of some fraction of the fourth derivative at the peak of the profile, # to account for the difficulty of accurately interpolating within the # point-spread function. The fourth derivative of the PSF is # proportional to H/sigma**4 (sigma is the Gaussian width parameter for # the stellar core); using the geometric mean of sigma(x) and sigma(y), # this becomes H/ sigma(x)*sigma(y) **2. The ratio of the fitting # error to this quantity is estimated from a good-seeing CTIO frame to # be approximately 0.027 (see definition of PKERR above.) if not fnoise: fpos = (fsub - df ) #Raw data - residual = model predicted intensity fposrow = where(fpos < 0.)[0] if len(fposrow): fpos[fposrow] = 0 sigsq = fpos / self.phpadu + self.ronois + ( 0.0075 * fpos)**2 + (pkerr * (fpos - skys))**2 sig = sqrt(sigsq) relerr = df / sig # SIG is the anticipated standard error of the intensity # including readout noise, Poisson photon statistics, and an estimate # of the standard error of interpolating within the PSF. rhosq = zeros([iyy, ixx]) rhosq = array([dxsq / gauss[3]**2 + j / gauss[4]**2 for j in dysq]) rhosq = rhosq[good[0], good[1]] if niter >= 2: #Reject any pixel with 10 sigma residual badpix = where(abs(relerr / chiold) >= 10.)[0] nbad = len(badpix) # scolnic added sbd = shape(badpix) sdf = shape(df) if sbd[0] == sdf[0]: scale = np.nan errmag = np.nan if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) if nbad > 0: fsub = item_remove(badpix, fsub) if fnoise: fsubnoise = item_remove(badpix, fsubnoise) df = item_remove(badpix, df) sigsq = item_remove(badpix, sigsq) sig = item_remove(badpix, sig) relerr = item_remove(badpix, relerr) rsq = item_remove(badpix, rsq) rhosq = item_remove(badpix, rhosq) ngood = ngood - badpix wt = 5. / (5. + rsq / (1. - rsq)) lilrho = where(rhosq <= 36.)[ 0] #Include only pixels within 6 sigma of centroid if not len(lilrho): scale = np.nan errmag = np.nan chi = np.nan sharp = np.nan if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) rhosq[lilrho] = 0.5 * rhosq[lilrho] dfdsig = exp(-rhosq[lilrho]) * (rhosq[lilrho] - 1.) if not fnoise: # FPOS-SKY = raw data minus sky = estimated value of the stellar # intensity (which presumably is non-negative). fpos = fsub[lilrho] fposrow = where(fsub[lilrho] - sky < 0.)[0] fpos[fposrow] = sky sig = fpos / self.phpadu + self.ronois + (0.0075 * fpos)**2 + ( pkerr * (fpos - sky))**2 else: sig = fsubnoise[lilrho[0]]**2. numer = sum(dfdsig * df[0:len(lilrho)] / sig) denom = sum(dfdsig**2 / sig) # Derive the weight of this pixel. First of all, the weight depends # upon the distance of the pixel from the centroid of the star-- it # is determined from a function which is very nearly unity for radii # much smaller than the fitting radius, and which goes to zero for # radii very near the fitting radius. chi = sum(wt * abs(relerr)) sumwt = sum(wt) wt = wt / sigsq #Scale weight to inverse square of expected mean error if niter >= 2: #Reduce weight of a bad pixel wt = wt / (1. + (0.4 * relerr / chiold)**8) v = zeros(3) #Compute vector of residuals and the normal matrix. c = zeros([3, 3]) lenwt = len(wt) for kk in range(3): v[kk] = sum(df * t[kk, 0:lenwt] * wt) for ll in range(3): c[ll, kk] = sum(t[kk, 0:lenwt] * t[ll, 0:lenwt] * wt) # Compute the (robust) goodness-of-fit index CHI. # CHI is pulled toward its expected value of unity before being stored # in CHIOLD to keep the statistics of a small number of pixels from # completely dominating the error analysis. if sumwt > 3.0: chi = 1.2533 * chi * sqrt(1. / (sumwt * (sumwt - 3.))) chiold = ((sumwt - 3.) * chi + 3.) / sumwt if not isnan(sum(c)) and not isinf(sum(c)): try: c = linalg.inv(c) #Invert the normal matrix except: print('singular matrix') scale = np.nan errmag = np.nan chi = np.nan sharp = np.nan if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) else: print('infinite matrix') scale = np.nan errmag = np.nan chi = np.nan sharp = np.nan if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale) dt = matrix(v) * c #Compute parameter corrections dt = array(dt)[0] # In the beginning, the brightness of the star will not be permitted # to change by more than two magnitudes per iteration (that is to say, # if the estimate is getting brighter, it may not get brighter by # more than 525% per iteration, and if it is getting fainter, it may # not get fainter by more than 84% per iteration). The x and y # coordinates of the centroid will be allowed to change by no more # than one-half pixel per iteration. Any time that a parameter # correction changes sign, the maximum permissible change in that # parameter will be reduced by a factor of 2. div = where(dtold * dt < -1.e-38)[0] nbad = len(div) if nbad > 0: clamp[div] = clamp[div] / 2. dtold = dt adt = abs(dt) denom2 = (dt[0] / (5.25 * scale)) if denom2 < (-1 * dt[0] / (0.84 * scale)): denom2 = (-1 * dt[0] / (0.84 * scale)) scale = scale + dt[0] / (1 + denom2 / clamp[0]) if recenter: x = x + dt[1] / (1. + adt[1] / (0.5 * clamp[1])) y = y + dt[2] / (1. + adt[2] / (0.5 * clamp[2])) redo = 0 # Convergence criteria: if the most recent computed correction to the # brightness is larger than 0.1% or than 0.05 * sigma(brightness), # whichever is larger, OR if the absolute change in X or Y is # greater than 0.01 pixels, convergence has not been achieved. sharp = 2. * gauss[3] * gauss[4] * numer / (gauss[0] * scale * denom) errmag = chiold * sqrt(c[0, 0]) if (adt[0] > max(0.05 * errmag, 0.001 * scale)): redo = 1 if (adt[1] > 0.01) or (adt[2] > 0.01): redo = 1 if debug: print(niter, x, y, scale, errmag, chiold, sharp) if niter >= 3: loop = False #At least 3 iterations required # If the solution has gone 25 iterations, OR if the standard error of # the brightness is greater than 200%, give up. if (redo and (errmag <= 1.9995) and (niter < maxiter)): loop = True # if sharp < -99.999: sharp = -99.999 # elif sharp > 99.999: sharp = 99.999 if xyout: return (errmag, chi, sharp, niter, scale, x, y) else: return (errmag, chi, sharp, niter, scale)
def pkfit(self,scale,x,y,sky,radius, debug=False, xyout=False, maxiter=25): f = self.f; gauss = self.gauss; psf = self.psf if f.dtype != 'float64': f = f.astype('float64') # psf1d = psf.reshape(shape(psf)[0]**2.) s = shape(f) #Get array dimensions nx = s[1] ; ny = s[0] #Initialize a few things for the solution redo = 0 pkerr = 0.027/(gauss[3]*gauss[4])**2. clamp = zeros(3) + 1. dtold = zeros(3) niter = 0 chiold = 1. if debug: print('PKFIT: ITER X Y SCALE ERRMAG CHI SHARP') loop=True while loop: #Begin the big least-squares loop niter = niter+1 if isnan(x) or isnan(y): scale=1000000.0; errmag=100000 chi=100000 sharp=100000 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) ixlo = int(x-radius) if ixlo < 0: ixlo = 0 #Choose boundaries of subarray containing iylo = int(y-radius) if iylo < 0: iylo = 0 # 3points inside the fitting radius ixhi = int(x+radius) +1 if ixhi > (nx-1): ixhi = nx-1 iyhi = int(y+radius) +1 if iyhi > ny-1: iyhi = ny-1 ixx = ixhi-ixlo+1 iyy = iyhi-iylo+1 dy = arange(iyy) + iylo - y #X distance vector from stellar centroid dysq = dy**2. dx = arange(ixx) + ixlo - x dxsq = dx**2. rsq = zeros([iyy,ixx]) #RSQ - array of squared rsq = array([(dxsq+dysqj)/radius**2 for dysqj in dysq]) # The fitting equation is of the form # # Observed brightness = # SCALE + delta(SCALE) * PSF + delta(Xcen)*d(PSF)/d(Xcen) + # delta(Ycen)*d(PSF)/d(Ycen) # # and is solved for the unknowns delta(SCALE) ( = the correction to # the brightness ratio between the program star and the PSF) and # delta(Xcen) and delta(Ycen) ( = corrections to the program star's # centroid). # # The point-spread function is equal to the sum of the integral under # a two-dimensional Gaussian profile plus a value interpolated from # a look-up table. good = where(rsq < 1.) ngood = len(good[0]) if ngood < 1: ngood = 1 t = zeros([3,ngood]) if not len(good[0]): scale=1000000.0; errmag=100000 chi=100000 sharp=100000 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) dx = dx[good[1]] dy = dy[good[0]] model,dvdx,dvdy = dao_value.dao_value(dx, dy, gauss, psf, #psf1d=psf1d, deriv=True)#,ps1d=True) if debug: print('model created ') if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) t[0,:] = model sa=shape(dvdx) if sa[0] > ngood or len(sa) == 0: scale=0 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) t[1,:] = -scale*dvdx t[2,:] = -scale*dvdy fsub = f[iylo:iyhi+1,ixlo:ixhi+1] fsub = fsub[good[0],good[1]] rsq = rsq[good[0],good[1]] # Scolnic Added!!! # yx=zeros(1) yx[0]=sky skys=yx[0] sky=skys df = fsub - scale*model - sky #Residual of the brightness from the PSF fit # The expected random error in the pixel is the quadratic sum of # the Poisson statistics, plus the readout noise, plus an estimated # error of 0.75% of the total brightness for the difficulty of flat- # fielding and bias-correcting the chip, plus an estimated error of # of some fraction of the fourth derivative at the peak of the profile, # to account for the difficulty of accurately interpolating within the # point-spread function. The fourth derivative of the PSF is # proportional to H/sigma**4 (sigma is the Gaussian width parameter for # the stellar core); using the geometric mean of sigma(x) and sigma(y), # this becomes H/ sigma(x)*sigma(y) **2. The ratio of the fitting # error to this quantity is estimated from a good-seeing CTIO frame to # be approximately 0.027 (see definition of PKERR above.) fpos = (fsub-df) #Raw data - residual = model predicted intensity fposrow = where(fpos < 0.)[0] if len(fposrow): fpos[fposrow] = 0 sigsq = fpos/self.phpadu + self.ronois + (0.0075*fpos)**2 + (pkerr*(fpos-skys))**2 sig = sqrt(sigsq) relerr = df/sig # SIG is the anticipated standard error of the intensity # including readout noise, Poisson photon statistics, and an estimate # of the standard error of interpolating within the PSF. rhosq = zeros([iyy,ixx]) rhosq = array([dxsq/gauss[3]**2+j/gauss[4]**2 for j in dysq]) rhosq = rhosq[good[0],good[1]] if niter >= 2: #Reject any pixel with 10 sigma residual badpix = where( abs(relerr/chiold) >= 10. )[0] nbad = len(badpix) # scolnic added sbd=shape(badpix) sdf=shape(df) if sbd[0] == sdf[0]: scale=1000000.0 errmag=100000 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) if nbad > 0: fsub = item_remove(badpix, fsub) df = item_remove(badpix,df) sigsq = item_remove(badpix,sigsq) sig = item_remove(badpix,sig) relerr = item_remove(badpix,relerr) rsq = item_remove(badpix,rsq) rhosq = item_remove(badpix,rhosq) ngood = ngood-badpix wt = 5./(5.+rsq/(1.-rsq)) lilrho = where(rhosq <= 36.)[0] #Include only pixels within 6 sigma of centroid if not len(lilrho): scale=1000000.0 errmag=100000 chi=100000 sharp=100000 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) rhosq[lilrho] = 0.5*rhosq[lilrho] dfdsig = exp(-rhosq[lilrho])*(rhosq[lilrho]-1.) fpos = fsub[lilrho] fposrow = where(fsub[lilrho]-sky < 0.)[0] fpos[fposrow] = sky # FPOS-SKY = raw data minus sky = estimated value of the stellar # intensity (which presumably is non-negative). sig = fpos/self.phpadu + self.ronois + (0.0075*fpos)**2 + (pkerr*(fpos-sky))**2 numer = sum(dfdsig*df[0:len(lilrho)]/sig) denom = sum(dfdsig**2/sig) # Derive the weight of this pixel. First of all, the weight depends # upon the distance of the pixel from the centroid of the star-- it # is determined from a function which is very nearly unity for radii # much smaller than the fitting radius, and which goes to zero for # radii very near the fitting radius. chi = sum(wt*abs(relerr)) sumwt = sum(wt) wt = wt/sigsq #Scale weight to inverse square of expected mean error if niter >= 2: #Reduce weight of a bad pixel wt = wt/(1.+(0.4*relerr/chiold)**8) v = zeros(3) #Compute vector of residuals and the normal matrix. c = zeros([3,3]) lenwt = len(wt) for kk in xrange(3): v[kk] = sum(df*t[kk,0:lenwt]*wt) for ll in xrange(3): c[ll,kk] = sum(t[kk,0:lenwt]*t[ll,0:lenwt]*wt) # Compute the (robust) goodness-of-fit index CHI. # CHI is pulled toward its expected value of unity before being stored # in CHIOLD to keep the statistics of a small number of pixels from # completely dominating the error analysis. if sumwt > 3.0: chi = 1.2533*chi*sqrt(1./(sumwt*(sumwt-3.))) chiold = ((sumwt-3.)*chi+3.)/sumwt if not isnan(sum(c)) and not isinf(sum(c)): try: c = linalg.inv(c) #Invert the normal matrix except: print('singular matrix') scale=1000000.0 errmag=100000 chi=100000 sharp=100000 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) else: print('infinite matrix') scale=1000000.0 errmag=100000 chi=100000 sharp=100000 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale) dt = matrix(v)*c #Compute parameter corrections dt = array(dt)[0] # In the beginning, the brightness of the star will not be permitted # to change by more than two magnitudes per iteration (that is to say, # if the estimate is getting brighter, it may not get brighter by # more than 525% per iteration, and if it is getting fainter, it may # not get fainter by more than 84% per iteration). The x and y # coordinates of the centroid will be allowed to change by no more # than one-half pixel per iteration. Any time that a parameter # correction changes sign, the maximum permissible change in that # parameter will be reduced by a factor of 2. div = where( dtold*dt < -1.e-38)[0] nbad = len(div) if nbad > 0: clamp[div] = clamp[div]/2. dtold = dt adt = abs(dt) denom2 = ( dt[0]/(5.25*scale)) if denom2 < (-1*dt[0]/(0.84*scale)): denom2 = (-1*dt[0]/(0.84*scale)) scale = scale+dt[0]/(1 + denom2/clamp[0]) x = x + dt[1]/(1.+adt[1]/(0.5*clamp[1])) y = y + dt[2]/(1.+adt[2]/(0.5*clamp[2])) redo = 0 # Convergence criteria: if the most recent computed correction to the # brightness is larger than 0.1% or than 0.05 * sigma(brightness), # whichever is larger, OR if the absolute change in X or Y is # greater than 0.01 pixels, convergence has not been achieved. sharp = 2.*gauss[3]*gauss[4]*numer/(gauss[0]*scale*denom) errmag = chiold*sqrt(c[0,0]) if ( adt[0] > max(0.05*errmag,0.001*scale)): redo = 1 if (adt[1] > 0.01) or (adt[2] > 0.01): redo = 1 if debug: print niter,x,y,scale,errmag,chiold,sharp if niter >= 3: loop=False #At least 3 iterations required # If the solution has gone 25 iterations, OR if the standard error of # the brightness is greater than 200%, give up. if (redo and (errmag <= 1.9995) and (niter < maxiter) ): loop=True # if sharp < -99.999: sharp = -99.999 # elif sharp > 99.999: sharp = 99.999 if xyout: return(errmag,chi,sharp,niter,scale,x,y) else: return(errmag,chi,sharp,niter,scale)
def pkfit_fast_norecenter(self, scale, x, y, sky, radius, debug=False, maxiter=25, sigclip=4): """ Fit the target star with a psf model, using quick numpy-based least squares fitting, with iterative sigma clipping. :param scale: initial guess of the optimized psf scale :param x: x position of the target in the data array :param y: y position of the target in the data array :param sky: fixed value of the background sky flux :param radius: fitting radius in pixels :param maxiter: max number of sigma clipping iterations :param sigclip: after each fitting iteration, clip pixels that have residuals discrepant by more than sigclip times the expected random flux error :param debug: enter the pdb debugger. Set >1 for diagnostic plots. :return: The best-fit scale factor that matches the PSF to the target star, without any recentering. """ # TODO : better checking for valid input data, with exceptions assert not (isnan(x) or isnan(y)) f = self.f gauss = self.gauss psf = self.psf # psf1d = psf.reshape(shape(psf)[0]**2.) s = shape(f) # Get array dimensions nx = s[1] ny = s[0] # Initialize a few things for the solution redo = 0 pkerr = 0.027 / (gauss[3] * gauss[4]) ** 2. clamp = zeros(3) + 1. dtold = zeros(3) niter = 0 chiold = 1. if debug: import time tstart = time.time() print('PKFIT: ITER X Y SCALE ERRMAG CHI SHARP') import pdb pdb.set_trace() # Set the x,y pixel position boundaries for a subarray # containing all pixels that fall within the fitting radius # NOTE: in this version, with no recentering, the x,y position # never changes, so we can define this subarray outside the loop ixlo = int(x - radius) if ixlo < 0: ixlo = 0 iylo = int(y - radius) if iylo < 0: iylo = 0 ixhi = int(x + radius) + 1 if ixhi > (nx - 1): ixhi = nx - 1 iyhi = int(y + radius) + 1 if iyhi > ny - 1: iyhi = ny - 1 ixx = ixhi - ixlo + 1 iyy = iyhi - iylo + 1 dy = arange(iyy) + iylo - y # Y dist. vector from stellar centroid dysq = dy ** 2 dx = arange(ixx) + ixlo - x dxsq = dx ** 2 # construct rsq as an array giving the square of the radial distance # of each pixel from the center of the target star, in units of the # user-defined fitting radius (i.e. rsq=1 is the circle at the # fitting radius) rsq = zeros([iyy, ixx]) for j in range(iyy): rsq[j, :] = (dxsq + dysq[j]) / radius ** 2 # define a list of indices in the subarray for those pixels that # are within the fitting radius from the stellar center i_tofit = where(rsq.reshape(shape(rsq)[0] * shape(rsq)[1]) < 1)[0] n_tofit = len(i_tofit) if n_tofit < 1: n_tofit = 1 # Extract a subarray with the observed flux for all pixels within the # fitting radius of the center of the target star flux_observed_tofit = ( f[iylo:iyhi + 1, ixlo:ixhi + 1].ravel()[[i_tofit]]) # Call the function dao_value to generate realized flux values # from the given psf model for each pixel position in the image # subarray that is within the fitting radius dx = dx[i_tofit % ixx] dy = dy[i_tofit / ixx] flux_model_tofit = dao_value.dao_value(dx, dy, gauss, psf, deriv=False) # Set the weight of each pixel for the least squares fitter based on # its distance from the fixed center of the target star: # weight ~ 1 at the center, and diminishes rapidly to ~0 at the edge # of the fitting radius. weight_tofit = (5. / (5. + rsq / (1. - rsq))).ravel()[i_tofit] flux_observed_tofit_weighted_minussky = \ flux_observed_tofit * weight_tofit - sky flux_model_tofit_weighted = flux_model_tofit * weight_tofit # Since we are not allowing the PSF to be recentered in this version, # the error function to minimize has only one free variable, SCALE: # err = sum(flux_observed * weight - SCALE * flux_model * weight) def errfunc(psf_scale, goodpixmask=1): """ Error function to minimize :param psf_scale: the psf scaling factor :param goodpixmask: set to 1 if all pixels are good; or set to an array of with 1 for good pixels and 0 for bad :return: vector of pixel residuals """ fobs = flux_observed_tofit_weighted_minussky * goodpixmask fmod = flux_model_tofit_weighted * goodpixmask error_vector = fobs - psf_scale * fmod return error_vector # The expected random error in the pixel is the quadratic sum of # the Poisson statistics, plus the readout noise, plus an estimated # error of 0.75% of the total brightness for the difficulty of flat # fielding and bias-correcting the chip, plus an estimated error # of some fraction of the fourth derivative at the peak of the # profile, to account for the difficulty of accurately # interpolating within the point-spread function. The fourth # derivative of the PSF is proportional to H/sigma**4 (sigma is # the Gaussian width parameter for the stellar core); using the # geometric mean of sigma(x) and sigma(y), this becomes # H/ sigma(x)*sigma(y) **2. The ratio of the fitting error to # this quantity is estimated to be approximately 0.027 # (see definition of PKERR above.) flux_observed_tofit_noneg = where(flux_observed_tofit - sky < 0, abs(sky), flux_observed_tofit) fluxerr2 = (flux_observed_tofit_noneg / self.phpadu + self.ronois + (0.0075 * flux_observed_tofit_noneg) ** 2 + (pkerr * (flux_observed_tofit_noneg - sky)) ** 2) fluxerr = sqrt(fluxerr2) # Solve for the SCALE factor using least squares minimization, # iteratively rejecting bad pixels that are more than 'sigclip' # sigma discrepant from the model, where sigma is the expected # random error (fluxerr above), separately defined for each pixel goodpix_mask = 1 n_badpix_beforefit = 0 for iteration in xrange(maxiter): bestfit_scale, cov = leastsq(errfunc, scale, args=goodpix_mask) scale = bestfit_scale[0] flux_resid_tofit = (flux_observed_tofit - scale * flux_model_tofit - sky) goodpix_mask = abs(flux_resid_tofit / fluxerr) < sigclip n_badpix_afterfit = n_tofit - sum(goodpix_mask) if n_badpix_afterfit <= n_badpix_beforefit: break if n_badpix_afterfit > 0.5 * n_tofit: #raise RuntimeWarning( print( ">50pct of pixels >%.1f sigma discrepant. " % sigclip + "Disabling badpix masking in iteration %i." % iteration) goodpix_mask = 1 if iteration == maxiter - 1: raise RuntimeWarning("Max # of iterations exceeded") if not debug: return scale elif debug > 1: # Serious debugging: # collect the full output from the least squares fitting # routine, plot the observed, model and residual fluxes, # and enter the pdb debugger. flux_obs_subarray = f[iylo:iyhi + 1, ixlo:ixhi + 1] flux_model_subarray = np.zeros(flux_obs_subarray.shape).ravel() flux_model_subarray[i_tofit] = flux_model_tofit * scale + sky flux_model_subarray = flux_model_subarray.reshape( flux_obs_subarray.shape) flux_resid_subarray = np.zeros(flux_obs_subarray.shape).ravel() flux_resid_subarray[i_tofit] = (flux_observed_tofit - scale * flux_model_tofit - sky) flux_resid_subarray = flux_resid_subarray.reshape( flux_obs_subarray.shape) from matplotlib import pyplot as pl, cm pl.figure(2, figsize=[10, 3.5]) pl.clf() fig = pl.gcf() fig.subplots_adjust(left=0.05, right=0.95, bottom=0.1, top=0.95) ax1 = fig.add_subplot(1, 3, 1) ax2 = fig.add_subplot(1, 3, 2) ax3 = fig.add_subplot(1, 3, 3) im1 = ax1.imshow(flux_obs_subarray, interpolation='nearest', aspect='equal', cmap=cm.Greys_r) cb1 = pl.colorbar(im1, ax=ax1, use_gridspec=True, orientation='horizontal') im2 = ax2.imshow(flux_model_subarray, interpolation='nearest', aspect='equal', cmap=cm.Greys_r) cb2 = pl.colorbar(im2, ax=ax2, use_gridspec=True, orientation='horizontal') im3 = ax3.imshow(flux_resid_subarray, interpolation='nearest', aspect='equal', cmap=cm.Greys_r) cb3 = pl.colorbar(im3, ax=ax3, use_gridspec=True, orientation='horizontal') return scale
def fit( fileroot='/export/scratch0/ps1sn1/data/v10.0/GPC1v3/eventsv1/workspace/PSc560121/g/PSc560121.md01s043.g.ut090831e.1917665_14.sw', xpos=None, ypos=None, radius=10, pdf_pages=None, ra=None, dec=None, title='', returnstamps = False, maskfile=None, mysky=None,mysig=None): # xpos = xpos +1 # ypos = ypos +1 # from matplotlib.backends.backend_pdf import PdfPages #pdf_pages = PdfPages('daophot_resid.pdf') dofcmp = False good = False im = pyfits.getdata('%s.fits' % fileroot) mask = pyfits.getdata(maskfile) impsf = pyfits.getdata('%s.dao.psf.fits' % fileroot) fullpsf, hpsf = rdpsf.rdpsf('%s.dao.psf.fits' % fileroot) imhdr = pyfits.getheader('%s.fits' % fileroot) if dofcmp: p = pyfits.open('%s.fcmp' % fileroot) p.verify("fix") if os.path.exists('test.fcmp'): os.remove('test.fcmp') p.writeto('test.fcmp', output_verify='fix') # fcmp = p[0].header # print p[1] fcmp = txtobj('test.fcmp', cmpheader=True) # print fcmp.__dict__['class'] # print fcmp['class'] # raw_input() w = wcs.WCS('%s.fits' % fileroot) #results2 = w.wcs_world2pix(np.array([[ra, dec]]), 0) # xpos,ypos =results2[0][0], results2[0][1] psfsize = np.shape(impsf)[0] fcmp.Xpos = fcmp.Xpos[1:].astype(float) fcmp.Ypos = fcmp.Ypos[1:].astype(float) fcmp.__dict__['class'] = fcmp.__dict__['class'][1:].astype(float) fcmp.flux = fcmp.flux[1:].astype(float) fcmp.dflux = fcmp.dflux[1:].astype(float) # for x,y,flux,fluxerr in zip(fcmp.Xpos,fcmp.Ypos, # fcmp.flux,fcmp.dflux): # print fcmp.Xpos-xpos # print fcmp.Ypos-ypos # raw_input() #print fcmp.__dict__['class'] #print fcmp.Xpos #print xpos #raw_input() ww = (abs(fcmp.Xpos - xpos) < 1.) & (abs(fcmp.Ypos - ypos) < 1.) thisclass = fcmp.__dict__['class'][ww] #print 'THIS CLASS IS', thisclass #print 'all classes', fcmp.__dict__['class'] # flux = fcmp.flux # fluxerr = fcmp.dflux if len(thisclass) == 1: if thisclass[0] == 1: good = True fluxerr = 100. chisq = 1. dms = 1. x = xpos y = ypos ny, nx = np.shape(im) psfy, psfx = np.shape(impsf) ixlo, iylo = int(x - radius), int(y - radius) if ixlo < 0: ixlo = 0 if iylo < 0: iylo = 0 ixhi = int(x + radius) + 1 iyhi = int(y + radius) + 1 if ixhi > (nx - 1): ixhi = nx - 1 if iyhi > (ny - 1): iyhi = ny - 1 ixx = ixhi - ixlo + 1 iyy = iyhi - iylo + 1 dx = np.arange(ixx) + ixlo - x dy = np.arange(iyy) + iylo - y psf1d = impsf.reshape(np.shape(impsf)[0] ** 2.) gauss = [hpsf['GAUSS1'], hpsf['GAUSS2'], hpsf['GAUSS3'], hpsf['GAUSS4'], hpsf['GAUSS5']] dx = dx.reshape(1, len(dx)) dy = dy.reshape(len(dy), 1) dx = rebin.rebin(dx, [np.shape(dx)[1], np.shape(dx)[1]]) dy = rebin.rebin(dy, [len(dy), len(dy)]) try: model = dao_value.dao_value(dx, dy, gauss, impsf, # psf1d=psf1d, deriv=False) # ,ps1d=False) except: return 1, 1, 0, 0, False, 0, 0, 0 subim = im[iylo - 1:iyhi, ixlo - 1:ixhi] #print 'modelshape', model.shape, 'imshape', subim.shape #raw_input() submask = mask[iylo - 1:iyhi, ixlo - 1:ixhi] submask[submask != 0] = 9 submask[submask == 0 ] = 1 submask[submask == 9 ] = 0 # scaledpsf = model+impsf[psfy/2+1-radius:psfy/2+1+radius+1, # psfx/2+1-radius:psfx/2+1+radius+1] # print model.shape # print flux.shape # print hpsf['PSFMAG'] # print imhdr['SKYADU'] chisqvec = [] fluxvec = [] substamp = model.shape[0] fitrad = np.zeros([substamp, substamp]) radius = 4 for x in np.arange(substamp): for y in np.arange(substamp): if np.sqrt((substamp / 2. - x) ** 2 + (substamp / 2. - y) ** 2) < radius: fitrad[int(x), int(y)] = 1. ''' for flux in range(1,500000,200): scaledpsf = model*flux/10**(-0.4*(hpsf['PSFMAG']-25)) + imhdr['SKYADU'] chisq = np.sum(fitrad*(subim-scaledpsf)**2/imhdr['SKYSIG']**2) chisqvec.append(chisq) fluxvec.append(flux) chisqvec = np.array(chisqvec) fluxvec = np.array(fluxvec) flux = fluxvec[np.argmin(chisqvec)] scaledpsf = model*flux/10**(-0.4*(hpsf['PSFMAG']-25)) + imhdr['SKYADU'] #resid(param,psf,im,sigma,fitrad,sky,psfmag) #print model, subim, imhdr['SKYSIG'] ''' # fluxls, cov = opti.leastsq(resid, 100000, # args=(model, subim, imhdr['SKYSIG'], fitrad, imhdr['SKYADU'], hpsf['PSFMAG']),full_output=False) fluxls, cov = opti.leastsq(resid, 100000,args=(model, subim, mysig, fitrad, mysky, hpsf['PSFMAG']),full_output=False) #print cov.shape #print fluxls, cov #raw_input('covshape') # print 'flux fit comparo',flux,fluxls, scaledpsf = model*fluxls/10**(-0.4*(hpsf['PSFMAG']-25)) + imhdr['SKYADU'] dontplot = False if not pdf_pages is None: if not dontplot: print 'plottingggg' fig = plt.figure() plt.clf() axim = plt.subplot(131) axpsf = plt.subplot(132) axdiff = plt.subplot(133) for ax,title in zip([axim,axpsf,axdiff],['image','model','difference']): ax.set_title(title) axim.imshow(subim, cmap='gray',interpolation='nearest') axpsf.imshow(model,cmap='gray',interpolation='nearest') axdiff.imshow(subim-scaledpsf,cmap='gray',interpolation='nearest') #plt.colorbar() axim = plt.subplot(131) axpsf = plt.subplot(132) axdiff = plt.subplot(133) #for ax,title in zip([axim,axpsf,axdiff],['image','model','difference']): if good: ax.set_title(title + 'GOOD') else: ax.set_title(title + 'BADD') #ax.set_title(title) axim.imshow(subim,cmap='gray',interpolation='nearest') axpsf.imshow(scaledpsf,cmap='gray',interpolation='nearest') ax = axdiff.imshow(subim-scaledpsf,cmap='gray',interpolation='nearest') cbar = fig.colorbar(ax) #plt.imshow((subim-scaledpsf)/imhdr['SKYSIG'],cmap='gray',interpolation='nearest') #plt.colorbar() if good: plt.title(title + 'GOOD' ) else: plt.title(title + 'BADD' ) pdf_pages.savefig(fig) #pdf_pages.close() #plt.savefig('') if returnstamps: rpsf = model good = True#we know this wont be in the starcat file so set to good is true #print 'fluxls', fluxls #print 'maxpsf', np.max(rpsf) return fluxls,fluxerr,chisq,dms,good,subim, rpsf, imhdr['SKYSIG'], fitrad, imhdr['SKYADU'], hpsf['PSFMAG'], submask #print fluxls #print np.max(model) #raw_input('fluxls') sstamp = simstamp(fluxls,model, subim, imhdr['SKYSIG'], fitrad, imhdr['SKYADU'], hpsf['PSFMAG']) return fluxls, fluxerr, chisq, dms, good, subim, sstamp, model