CCFCenter
This script is used to find the center an meaure the width of a CCF peak.
|
00001 #!/usr/bin/python 00002 ## 00003 # 00004 # This module finds the center and width of a MARVELS ccf 00005 # 00006 # This module uses a non-linear least-squares optimizer from scipy to 00007 # fit a Gaussian to the central pixels of a ccf file. 00008 # 00009 # @package ccfcenter 00010 # @author Nathan De Lee 00011 # @version $Revision: 1.2 $ 00012 # @date $Date: 2012-01-13 20:12:24 $ 00013 # 00014 00015 # ----------------------------- 00016 # Standard library dependencies 00017 # ----------------------------- 00018 import os 00019 import sys 00020 import collections 00021 00022 # ------------------- 00023 # Third-party imports 00024 # ------------------- 00025 from scipy import optimize 00026 import numpy as np 00027 import matplotlib.pyplot as pl 00028 from matplotlib.backends.backend_pdf import PdfPages 00029 00030 ## 00031 # 00032 # Choose which section of the ccf to fit based on width parameter 00033 # or automatically using percent 00034 # 00035 # @param pixel Numpy array of pixel positions 00036 # @param ccf Numpy array of ccf values 00037 # @param width Integer number of pixels to include, if None: 00038 # then automatically determine. 00039 # @param percent Floating point percentage of the singal amplitude 00040 # above the noise that a point must be to be included in 00041 # the fit. 00042 # 00043 # @return This function returns a tuple (pixel,ccf,lmargin,rmargin 00044 # ,noiselevel). 00045 # 00046 # @retval pixel Numpy array of pixel positions 00047 # @retval ccf Numpy array of ccf values 00048 # @retval lmargin Integer index of the left margin of the input arrays 00049 # @retval rmargin Integer index of the right margin of the input arrays 00050 # @retval noiselevel Floating point values of the CCF plot noise level 00051 # 00052 def cutccf(pixel,ccf,width=None,percent=0.30): 00053 cent_index= ccf.argmax() 00054 if width == None: 00055 noiselevel = np.median(ccf) 00056 cutoff = (percent * (max(ccf) - noiselevel))+noiselevel 00057 indice = np.array((ccf < cutoff).nonzero()).flatten() 00058 lmargin = max(indice[np.array((indice < cent_index).nonzero()).flatten()]) 00059 rmargin = min(indice[np.array((indice > cent_index).nonzero()).flatten()]) 00060 else: 00061 lmargin = cent_index-width 00062 rmargin = cent_index+width+1 00063 00064 return(pixel[lmargin:rmargin],ccf[lmargin:rmargin],lmargin,rmargin,noiselevel) 00065 00066 ## 00067 # 00068 # Takes x points and returns a Gaussian function 00069 # 00070 # @param P A 3 element array (Amplitude,Offset,Sigma) 00071 # @param x Numpy array of x positions 00072 # 00073 # @returns y values for the x positions and parameters given 00074 # 00075 # @retval y Numpy array of y values 00076 # 00077 def gaussfunc(P,x): 00078 00079 ## @page Formula Mathematical Formula 00080 # @section gauss Gaussian 00081 # @{ 00082 # I use this formulation for my Gaussian Equation 00083 # @f$ y = Amp \exp(-\frac{(x - Offset)^{2}}{2\sigma^{2}})@f$ 00084 ## @} 00085 00086 return(P[0]*np.exp(-(x-P[1])**2/(2*P[2]**2))) 00087 00088 ## 00089 # 00090 # Creates a Gaussian signal and then fits it to see if I can recover the input parameters. 00091 # 00092 # @return None 00093 # 00094 # 00095 def gausstest(): 00096 ntrials = 10000 00097 #Open output pdf 00098 pp = PdfPages('test.pdf') 00099 print "Progress: ", 00100 for i in range(ntrials) : 00101 if ntrials >= 10 and i % (ntrials/10) == 0 : 00102 print str("."), 00103 (x,y) = make_gauss(6,amp=0.91,sigma=10.4,offset=5.1,error=0.015) 00104 #Fit a gaussian to the data cutout 00105 #(params,errors,chi)=fitgauss(x,y,inguess=[1,1,20],inerror=(np.zeros(len(x))+.015)) 00106 (params,errors,chi)=fitgauss(x,y,inguess=[1,1,20]) 00107 00108 #print i,params,errors,chi 00109 if i == 0: 00110 allparams = params 00111 allerrors = errors 00112 allchi = chi 00113 else: 00114 allparams = np.vstack([allparams,params]) 00115 allerrors = np.vstack([allerrors,errors]) 00116 allchi = np.vstack([allchi,chi]) 00117 00118 ##@fn def gausstest(): 00119 #@todo Make a histogram of all the parameters 00120 00121 #Print out how well I did. 00122 print "\nActual: ",np.std(allparams[:,0]),np.std(allparams[:,1]),np.std(allparams[:,2]) 00123 print "First: ",allerrors[0,0],allerrors[0,1],allerrors[0,2] 00124 print "Average: ",np.mean(allerrors[:,0]),np.mean(allerrors[:,1]),np.mean(allerrors[:,2]) 00125 pp.close() 00126 return(0) 00127 00128 ## 00129 # 00130 # Use a Levenberg-Marquadt non-linear least-squares fitter 00131 # to fit a Gaussian to Xin and Yin 00132 # 00133 # The scipy module optimize is used to find the smallest residual 00134 # to @ref error which is compared to the @ref gauss function. 00135 # 00136 # @param xin Numpy array of x-values 00137 # @param yin Numpy array of y-values 00138 # @param inerror Numpy array of error values 00139 # @param inguess A 3 element list (Amplitude,Offset,Sigma) of 00140 # starting values for Gaussian parameters 00141 # 00142 # @return This function returns a tuple (Params, perror , red_chi) 00143 # 00144 # @retval Params A 3 element Numpy array of Gaussian 00145 # Parameters (Amplitude,Offset,Sigma) 00146 # @retval perror A 3 element Numpy array of parameter errors 00147 # calculated from the covariance matrix 00148 # @retval red_chi A float giving the reduced @f$\chi^{2}@f$ 00149 # of the fit. 00150 # 00151 def fitgauss(xin,yin,inerror=[],inguess=[1,1,1]): 00152 00153 #Don't want my np.arrays messed up. 00154 x = np.array(xin,dtype='float64') 00155 y = np.array(yin,dtype='float64') 00156 guess = np.array(inguess[:]) 00157 if len(inerror) == len(xin): 00158 error = np.array(inerror,dtype='float64') 00159 else: 00160 error = np.ones(len(xin)) 00161 00162 if(len(inerror) == len(xin)): 00163 ## @page Formula Mathematical Formula 00164 # @section error Error Function 00165 # @{ 00166 # I use this formulation for optimizing my least-squares fitter 00167 # - With Errors: @f$\frac{y - Gaussian(P,x)}{errror}@f$ 00168 # - Without Errors: @f$y - Gaussian(P,x)@f$ 00169 # @} 00170 errfunc = lambda P,x,y,error: (y - gaussfunc(P, x))/error 00171 out = optimize.leastsq(errfunc, guess, args=(x, y, error),full_output=1) 00172 else: 00173 errfunc = lambda P,x,y: y - gaussfunc(P, x) 00174 out = optimize.leastsq(errfunc, guess, args=(x, y),full_output=1) 00175 00176 Params = out[0] 00177 #Sigma should be positive 00178 Params[2] = np.abs(Params[2]) 00179 00180 if out[1] != None : 00181 cov = out[1] 00182 perror = np.array([np.sqrt(cov[0][0]),np.sqrt(cov[1][1]),np.sqrt(cov[2][2])]) 00183 else: 00184 perror = np.array([1e11,1e11,1e11]) 00185 if(len(inerror) == len(xin)): 00186 red_chi = sum((errfunc(Params,x,y,error))**2)/(len(x)-3) 00187 else: 00188 red_chi = sum((errfunc(Params,x,y))**2)/(len(x)-3) 00189 00190 ##@fn def fitgauss(xin,yin,inerror=[],inguess=[1,1,1]): 00191 #@remarks 00192 #If there are no measurement errors then the parameter errors must be scaled by the reduced chisq 00193 #Section 15.2 in <a href="http://adsabs.harvard.edu/abs/1992nrca.book.....P/">Numerical 00194 #Recipes </a> discusses this. 00195 00196 if(len(inerror) == 0): 00197 perror = perror * np.sqrt(red_chi) 00198 00199 return(Params,perror,red_chi) 00200 00201 ## 00202 # 00203 # Generate Gaussian data using @ref gauss 00204 # 00205 # @param npts Integer number of points to create 00206 # @param amp Floating point amplitude of the Gaussian 00207 # @param offset Floating point offset of the Gaussian 00208 # @param sigma Floating point sigma of the Gaussian 00209 # @param error Floating point number or a Numpy array of sigmas for the error in each point 00210 # @param inflate_error Floating point factor to scale the real as versus reported error 00211 # @param randpts Bool True: Select x points from a uniform distribution; 00212 # False Take from a regular grid. 00213 # 00214 # @return This function returns a tuple (x,y,y_err) 00215 # 00216 # @retval x Numpy Array of x positions 00217 # @retval y Numpy Array of y positions 00218 # @retval y_err Numpy Array of y errors 00219 # 00220 def make_gauss(npts,amp=1,offset=0,sigma=1,error=0,inflate_error=1.0,randpts=False): 00221 00222 if randpts == True: 00223 x = (np.random.uniform(size=npts) - 0.5)* 6.0 * sigma + offset 00224 else: 00225 x = (np.arange(npts,dtype='float') -npts/2.0) * 6 * sigma /npts + offset 00226 #Get Gaussian value 00227 y = gaussfunc([amp,offset,sigma],x) 00228 00229 #Add Error to Gaussian 00230 if error > 0: 00231 y = y + (np.random.normal(0,error,npts)*inflate_error) 00232 00233 return(x,y) 00234 00235 ## 00236 # 00237 # This function is the main() program for this module 00238 # 00239 # @param filename String name of MARVELS ccf file to process 00240 # @param width Integer number of pixels to fit around peak; 00241 # None: Automatically choose 00242 # 00243 # @return The function returns 0 for success or an error message. 00244 # 00245 # @retval ret 0 for success or an error message 00246 # 00247 def ccfcenter(filename,width=None): 00248 #Numpy arrays are tricky. To make an empty shapeless numpy array np.ndarray((0,)) 00249 #I want a dictionary of dictionary of numpy arrays. 00250 #The collections defaultdict command allows one to do this. It makes a dictionary typed as a callable 00251 #function. For instance if you use list() it will allow you to use a .append on it. 00252 #You must define a factory which is callable, so I use the lambda to make np.ndarray((0,)) into a 00253 #callable function like list() 00254 ccfdict = collections.defaultdict(lambda : collections.defaultdict(lambda : np.ndarray((0,)))) 00255 00256 #Read in data file 00257 try: 00258 inputf = open(filename) 00259 except IOError: 00260 print "File %s could not be opened!" % filename 00261 return(1) 00262 00263 try: 00264 for line in inputf: 00265 #Ignore comments and blank lines 00266 if line.startswith("#") or line.isspace(): 00267 continue 00268 line.strip() 00269 aline = line.split() 00270 ccfdict[aline[0]]['pixel'] = np.append(ccfdict[aline[0]]['pixel'],float(aline[8])) 00271 ccfdict[aline[0]]['ccf'] = np.append(ccfdict[aline[0]]['ccf'],float(aline[9])) 00272 ccfdict[aline[0]]['rv'] = float(aline[1]) 00273 ccfdict[aline[0]]['rverr'] = float(aline[2]) 00274 ccfdict[aline[0]]['ccfrv'] = float(aline[3]) 00275 ccfdict[aline[0]]['ccfrverr'] = float(aline[4]) 00276 finally: 00277 00278 inputf.close() 00279 00280 #Get base filename 00281 basename = os.path.splitext(filename)[0] 00282 #Open output pdf 00283 pp = PdfPages(basename+'.pdf') 00284 #Open output file 00285 outputf = open(basename+'.out','w') 00286 #Loop through each epoch 00287 i=0 00288 for key in sorted(ccfdict.keys()): 00289 i += 1 00290 #Make data cutout 00291 (pixcut,ccfcut,lmargin,rmargin,noiselvl) = cutccf(ccfdict[key]['pixel'],np.array(ccfdict[key]['ccf']),width) 00292 print lmargin,rmargin,noiselvl 00293 #Fit a gaussian to the data cutout 00294 (params,errors,chi)=fitgauss(pixcut,ccfcut,inguess=[1,1,4]) 00295 #Print the results 00296 print i,key,params,errors,chi 00297 rv = ccfdict[key]['rv'] 00298 err = ccfdict[key]['rverr'] 00299 rvccf = ccfdict[key]['ccfrv'] 00300 ccferr = ccfdict[key]['ccfrverr'] 00301 #Write out to file 00302 out = map(str,(i,key,rv,err,rvccf,ccferr,' '.join(map(str,params)),' '.join(map(str,errors)),chi)) 00303 outputf.write(' '.join(out)) 00304 outputf.write('\n') 00305 #Plot the results 00306 pl.xlabel("Pixel") 00307 pl.ylabel("CCF") 00308 pl.ylim(-.2,1.1) 00309 pl.title("Date: %s" % key) 00310 pl.plot(ccfdict[key]['pixel'][lmargin-20:rmargin+20],ccfdict[key]['ccf'][lmargin-20:rmargin+20],'go') 00311 pl.plot(pixcut,ccfcut,'ro') 00312 pixcut = np.array(pixcut,dtype='float64') 00313 modelx = np.arange(100) * ((pixcut.max() - pixcut.min()+20)/ 100.0) + pixcut.min()-10 00314 pl.plot(modelx,gaussfunc(params,modelx)) 00315 pp.savefig() 00316 pl.clf() 00317 pp.close() 00318 outputf.close() 00319 return(0) 00320 if __name__ == '__main__': 00321 #Check to make sure we have at least one argument and no more than 2 arguments 00322 if len(sys.argv) < 2 or len(sys.argv) > 3: 00323 print 'Usage: ccf ccfilename {width}\n' 00324 print 'If test is given for the filename, then will run a montecarlo simulation' 00325 print 'ccfilename Output filename from ccfsearch.pro' 00326 print 'width Optional width parameter (in pixels)\n' 00327 sys.exit(1) 00328 filename = sys.argv[1] 00329 if filename == 'test': 00330 gausstest() 00331 sys.exit(0) 00332 else: 00333 width = None 00334 ret = ccfcenter(filename,width) 00335 sys.exit(ret) 00336 00337 ## 00338 #@mainpage A Guassian CCF Fitter 00339 #This module is designed to fit a gaussian to the peak of a cross-correlation function. 00340 #In it's current form, it uses a non-linear least squares fitter from the scipy optimize module. 00341 #@verbatim 00342 #Usage: ccf ccfilename {width} 00343 #If test is given for the filename, then will run a montecarlo simulation 00344 #ccfilename Output filename from ccfsearch.pro 00345 #width Optional width parameter (in pixels) 00346 #@endverbatim 00347