def main(): np.set_printoptions(threshold=np.nan) testPixelRow = 24 testPixelCol = 17 #obs_20120919-131142.h5,obs_20120919-131346.h5 #create a cal file from a twilight flat cal = FlatCal('../../params/flatCal.dict') #open another twilight flat as an observation and apply a wavelength cal and the new flat cal # run='LICK2012' # obsFileName = FileName(run=run,date='20120918',tstamp='20120919-131142').flat() # flatCalFileName = FileName(run=run,date='20120918',tstamp='20120919-131448').flatSoln() # wvlCalFileName = FileName(run=run,date='20120916',tstamp='20120917-072537').calSoln() run = 'PAL2012' obsFileName = FileName(run=run,date='20121211',tstamp='20121212-140003').obs() flatCalFileName = FileName(run=run,date='20121210',tstamp='').flatSoln() wvlCalFileName = FileName(run=run,date='20121210',tstamp='20121211-133056').calSoln() flatCalPath = os.path.dirname(flatCalFileName) ob = ObsFile(obsFileName)#('obs_20120919-131142.h5') ob.loadWvlCalFile(wvlCalFileName)#('calsol_20120917-072537.h5') ob.loadFlatCalFile(flatCalFileName)#('flatsol_20120919-131142.h5') #plot some uncalibrated and calibrated spectra for one pixel fig = plt.figure() ax = fig.add_subplot(211) ax2 = fig.add_subplot(212) print ob.getPixelCount(testPixelRow,testPixelCol) #flatSpectrum,wvlBinEdges = ob.getPixelSpectrum(testPixelRow,testPixelCol,weighted=False) spectrum,wvlBinEdges = ob.getPixelSpectrum(testPixelRow,testPixelCol,wvlStart=cal.wvlStart,wvlStop=cal.wvlStop,wvlBinWidth=cal.wvlBinWidth,weighted=False,firstSec=0,integrationTime=-1) weightedSpectrum,wvlBinEdges = ob.getPixelSpectrum(testPixelRow,testPixelCol,weighted=True) #flatSpectrum,wvlBinEdges = cal.flatFile.getPixelSpectrum(testPixelRow,testPixelCol,wvlStart=cal.wvlStart,wvlStop=cal.wvlStop,wvlBinWidth=cal.wvlBinWidth,weighted=False,firstSec=0,integrationTime=-1) flatSpectrum = cal.spectra[testPixelRow,testPixelCol] x = wvlBinEdges[0:-1] ax.plot(x,cal.wvlMedians,label='median spectrum',alpha=.5) ax2.plot(x,cal.flatFactors[testPixelRow,testPixelCol,:],label='pixel weights',alpha=.5) ax2.set_title('flat weights for pixel %d,%d'%(testPixelRow,testPixelCol)) ax.plot(x,spectrum+20,label='unweighted spectrum for pixel %d,%d'%(testPixelRow,testPixelCol),alpha=.5) ax.plot(x,weightedSpectrum+10,label='weighted %d,%d'%(testPixelRow,testPixelCol),alpha=.5) ax.plot(x,flatSpectrum+30,label='flatFile %d,%d'%(testPixelRow,testPixelCol),alpha=.5) ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.3),fancybox=True,ncol=3) plt.show() #display a time-flattened image of the twilight flat as it is and after using itself as it's flat cal #cal.flatFile.loadFlatCalFile(flatCalFileName)#('flatsol_20120919-131142.h5') #cal.flatFile.displaySec(weighted=True,integrationTime=-1) #ob.displaySec(integrationTime=-1) #ob.displaySec(weighted=True,integrationTime=-1) for idx in range(0,100,20): factors10 = cal.flatFactors[:,:,idx] plt.matshow(factors10,vmax=np.mean(factors10)+1.5*np.std(factors10)) plt.title('Flat weights at %d'%cal.wvlBinEdges[idx]) plt.colorbar() plt.savefig('plots/factors%d.png'%idx) plt.show()
plt.step(obs.filterWvlBinEdges[:-1], obs.filterTrans, where='post', color='blue', label = "Rebinned to wvlBinEdges") plt.legend() plt.xlabel('Angstroms') plt.ylabel('Transmission') plt.show() ''' # By default loadFilter() will also turn on the application of the filter. You can set # switchOnFilter to False when calling loadFilter to not activate the filter immediately, # and also toggle the filters on and off with switchOffFilter() and switchOnFilter(). obs.switchOffFilter() # With the filter off let's get the full spectrum of the selected pixel. specDict = obs.getPixelSpectrum(row, col, firstSec=0, integrationTime= -1, weighted=True, fluxWeighted=True) specWvlBinEdges = specDict['wvlBinEdges'] fullSpectrum = specDict['spectrum'] # Now let's switch the filter back on and overplot the filtered spectrum with the full # spectrum. obs.switchOnFilter() filteredSpecDict = obs.getPixelSpectrum(row, col, firstSec=0, integrationTime= -1, weighted=True, fluxWeighted=True) filteredSpecWvlBinEdges = filteredSpecDict['wvlBinEdges'] filteredSpectrum = filteredSpecDict['spectrum'] plt.step(specWvlBinEdges[:-1], fullSpectrum, where = 'post', label = 'Full ARCONS spectrum of single pixel') plt.step(filteredSpecWvlBinEdges[:-1], filteredSpectrum, where = 'post', label = 'Filtered spectrum') plt.xlabel('Angstroms') plt.ylabel('Total counts (not counts/s/Angstrom)') plt.ylim(0,1200000)
class FluxCal: def __init__(self,paramFile,plots=False,verbose=False): """ Opens flux file, prepares standard spectrum, and calculates flux factors for the file. Method is provided in param file. If 'relative' is selected, an obs file with standard star defocused over the entire array is expected, with accompanying sky file to do sky subtraction. If any other method is provided, 'absolute' will be done by default, wherein a point source is assumed to be present. The obs file is then broken into spectral frames with photometry (psf or aper) performed on each frame to generate the ARCONS observed spectrum. """ self.verbose=verbose self.plots = plots self.params = readDict() self.params.read_from_file(paramFile) run = self.params['run'] sunsetDate = self.params['fluxSunsetLocalDate'] self.fluxTstamp = self.params['fluxTimestamp'] skyTstamp = self.params['skyTimestamp'] wvlSunsetDate = self.params['wvlCalSunsetLocalDate'] wvlTimestamp = self.params['wvlCalTimestamp'] flatCalFileName = self.params['flatCalFileName'] needTimeAdjust = self.params['needTimeAdjust'] self.deadtime = float(self.params['deadtime']) #from firmware pulse detection self.timeSpacingCut = self.params['timeSpacingCut'] bLoadBeammap = self.params.get('bLoadBeammap',False) self.method = self.params['method'] self.objectName = self.params['object'] self.r = float(self.params['energyResolution']) self.photometry = self.params['photometry'] self.centroidRow = self.params['centroidRow'] self.centroidCol = self.params['centroidCol'] self.aperture = self.params['apertureRad'] self.annulusInner = self.params['annulusInner'] self.annulusOuter = self.params['annulusOuter'] self.collectingArea = self.params['collectingArea'] self.startTime = self.params['startTime'] self.intTime = self.params['integrationTime'] fluxFN = FileName(run=run,date=sunsetDate,tstamp=self.fluxTstamp) self.fluxFileName = fluxFN.obs() self.fluxFile = ObsFile(self.fluxFileName) if self.plots: self.plotSavePath = os.environ['MKID_PROC_PATH']+os.sep+'fluxCalSolnFiles'+os.sep+run+os.sep+sunsetDate+os.sep+'plots'+os.sep if not os.path.exists(self.plotSavePath): os.mkdir(self.plotSavePath) if self.verbose: print "Created directory %s"%self.plotSavePath obsFNs = [fluxFN] self.obsList = [self.fluxFile] if self.startTime in ['',None]: self.startTime=0 if self.intTime in ['',None]: self.intTime=-1 if self.method=="relative": try: print "performing Relative Flux Calibration" skyFN = FileName(run=run,date=sunsetDate,tstamp=skyTstamp) self.skyFileName = skyFN.obs() self.skyFile = ObsFile(self.skyFileName) obsFNs.append(skyFN) self.obsList.append(self.skyFile) except: print "For relative flux calibration a sky file must be provided in param file" self.__del__() else: self.method='absolute' print "performing Absolute Flux Calibration" if self.photometry not in ['aperture','PSF']: self.photometry='PSF' #default to PSF fitting if no valid photometry selected timeMaskFileNames = [fn.timeMask() for fn in obsFNs] timeAdjustFileName = FileName(run=run).timeAdjustments() #make filename for output fluxCalSoln file self.fluxCalFileName = FileName(run=run,date=sunsetDate,tstamp=self.fluxTstamp).fluxSoln() print "Creating flux cal: %s"%self.fluxCalFileName if wvlSunsetDate != '': wvlCalFileName = FileName(run=run,date=wvlSunsetDate,tstamp=wvlTimestamp).calSoln() if flatCalFileName =='': flatCalFileName=FileName(obsFile=self.fluxFile).flatSoln() #load cal files for flux file and, if necessary, sky file for iObs,obs in enumerate(self.obsList): if bLoadBeammap: print 'loading beammap',os.environ['MKID_BEAMMAP_PATH'] obs.loadBeammapFile(os.environ['MKID_BEAMMAP_PATH']) if wvlSunsetDate != '': obs.loadWvlCalFile(wvlCalFileName) else: obs.loadBestWvlCalFile() obs.loadFlatCalFile(flatCalFileName) obs.setWvlCutoffs(-1,-1) if needTimeAdjust: obs.loadTimeAdjustmentFile(timeAdjustFileName) timeMaskFileName = timeMaskFileNames[iObs] print timeMaskFileName if not os.path.exists(timeMaskFileName): print 'Running hotpix for ',obs hp.findHotPixels(obsFile=obs,outputFileName=timeMaskFileName,fwhm=np.inf,useLocalStdDev=True) print "Flux cal/sky file pixel mask saved to %s"%(timeMaskFileName) obs.loadHotPixCalFile(timeMaskFileName) if self.verbose: print "Loaded hot pixel file %s"%timeMaskFileName #get flat cal binning information since flux cal will need to match it self.wvlBinEdges = self.fluxFile.flatCalFile.root.flatcal.wavelengthBins.read() self.nWvlBins = self.fluxFile.flatWeights.shape[2] self.binWidths = np.empty((self.nWvlBins),dtype=float) self.binCenters = np.empty((self.nWvlBins),dtype=float) for i in xrange(self.nWvlBins): self.binWidths[i] = self.wvlBinEdges[i+1]-self.wvlBinEdges[i] self.binCenters[i] = (self.wvlBinEdges[i]+(self.binWidths[i]/2.0)) if self.method=='relative': print "Extracting ARCONS flux and sky spectra" self.loadRelativeSpectrum() print "Flux Spectrum loaded" self.loadSkySpectrum() print "Sky Spectrum loaded" elif self.method=='absolute': print "Extracting ARCONS point source spectrum" self.loadAbsoluteSpectrum() print "Loading standard spectrum" try: self.loadStdSpectrum(self.objectName) except KeyError: print "Invalid spectrum object name" self.__del__() sys.exit() print "Generating sensitivity curve" self.calculateFactors() print "Sensitivity Curve calculated" print "Writing fluxCal to file %s"%self.fluxCalFileName self.writeFactors(self.fluxCalFileName) if self.plots: self.makePlots() print "Done" def __del__(self): try: self.fluxFile.close() self.calFile.close() except AttributeError:#fluxFile was never defined pass def getDeadTimeCorrection(self, obs): #WRONG RIGHT NOW. NEEDS TO HAVE RAW COUNTS SUMMED, NOT CUBE WHICH EXCLUDES NOISE TAIL if self.verbose: print "Making raw cube to get dead time correction" cubeDict = obs.getSpectralCube(firstSec=self.startTime, integrationTime=self.intTime, weighted=False) cube= np.array(cubeDict['cube'], dtype=np.double) wvlBinEdges= cubeDict['wvlBinEdges'] effIntTime= cubeDict['effIntTime'] if self.verbose: print "median effective integration time = ", np.median(effIntTime) nWvlBins=len(wvlBinEdges)-1 if self.verbose: print "cube shape ", np.shape(cube) if self.verbose: print "effIntTime shape ", np.shape(effIntTime) #add third dimension to effIntTime for broadcasting effIntTime = np.reshape(effIntTime,np.shape(effIntTime)+(1,)) #put cube into counts/s in each pixel cube /= effIntTime #CALCULATE DEADTIME CORRECTION #NEED TOTAL COUNTS PER SECOND FOR EACH PIXEL TO DO PROPERLY #ASSUMES SAME CORRECTION FACTOR APPLIED FOR EACH WAVELENGTH, MEANING NO WL DEPENDANCE ON DEAD TIME EFFECT DTCorr = np.zeros((np.shape(cube)[0],np.shape(cube)[1]),dtype=float) for f in range(0,np.shape(cube)[2]): #if self.verbose: print cube[:,:,f] #if self.verbose: print '-----------------------' DTCorr += cube[:,:,f] #if self.verbose: print DTCorr #if self.verbose: print '\n=====================\n' #Correct for firmware dead time (100us in 2012 ARCONS firmware) DTCorrNew=DTCorr/(1-DTCorr*self.deadtime) CorrFactors = DTCorrNew/DTCorr #This is what the frames need to be multiplied by to get their true values if self.verbose: print "Dead time correction factors: ", CorrFactors #add third dimension to CorrFactors for broadcasting CorrFactors = np.reshape(CorrFactors,np.shape(CorrFactors)+(1,)) return CorrFactors def loadAbsoluteSpectrum(self): ''' extract the ARCONS measured spectrum of the spectrophotometric standard by breaking data into spectral cube and performing photometry (aper or psf) on each spectral frame ''' if self.verbose:print "Making spectral cube" cubeDict = self.fluxFile.getSpectralCube(firstSec=self.startTime, integrationTime=self.intTime, weighted=True, fluxWeighted=False) cube= np.array(cubeDict['cube'], dtype=np.double) effIntTime= cubeDict['effIntTime'] if self.verbose: print "median effective integration time in flux file cube = ", np.median(effIntTime) if self.verbose: print "cube shape ", np.shape(cube) if self.verbose: print "effIntTime shape ", np.shape(effIntTime) #add third dimension to effIntTime for broadcasting effIntTime = np.reshape(effIntTime,np.shape(effIntTime)+(1,)) #put cube into counts/s in each pixel cube /= effIntTime #get dead time correction factors DTCorr = self.getDeadTimeCorrection(self.fluxFile) cube*=DTCorr #cube now in units of counts/s and corrected for dead time if self.plots and not 'figureHeader' in sys.modules: if self.verbose: print "Saving spectral frames as movie..." movieCube = np.zeros((self.nWvlBins,np.shape(cube)[0],np.shape(cube)[1]),dtype=float) for i in xrange(self.nWvlBins): movieCube[i,:,:] = cube[:,:,i] makeMovie(movieCube,frameTitles=self.binCenters,cbar=True,outName=self.plotSavePath+'FluxCal_Cube_%s.gif'%(self.objectName), normMin=0, normMax=50) if self.verbose: print "Movie saved in %s"%self.plotSavePath LCplot=False #light curve pop-ups not compatible with FLuxCal plotting 2/18/15 #if self.photometry=='PSF': LCplot = False LC = LightCurve.LightCurve(verbose=self.verbose, showPlot=LCplot) self.fluxSpectrum=np.empty((self.nWvlBins),dtype=float) self.skySpectrum=np.zeros((self.nWvlBins),dtype=float) for i in xrange(self.nWvlBins): frame = cube[:,:,i] if self.verbose: print "%s photometry on frame %i of cube, central wvl = %f Angstroms"%(self.photometry,i,self.binCenters[i]) if self.photometry == 'aperture': fDict = LC.performPhotometry(self.photometry,frame,[[self.centroidCol,self.centroidRow]],expTime=None,aper_radius = self.aperture, annulus_inner = self.annulusInner, annulus_outer = self.annulusOuter, interpolation="linear") self.fluxSpectrum[i] = fDict['flux'] self.skySpectrum[i] = fDict['skyFlux'] print "Sky estimate = ", fDict['skyFlux'] else: fDict = LC.performPhotometry(self.photometry,frame,[[self.centroidCol,self.centroidRow]],expTime=None,aper_radius = self.aperture) self.fluxSpectrum[i] = fDict['flux'] self.fluxSpectrum=self.fluxSpectrum/self.binWidths/self.collectingArea #spectrum now in counts/s/Angs/cm^2 self.skySpectrum=self.skySpectrum/self.binWidths/self.collectingArea return self.fluxSpectrum, self.skySpectrum def loadRelativeSpectrum(self): self.fluxSpectra = [[[] for i in xrange(self.nCol)] for j in xrange(self.nRow)] self.fluxEffTime = [[[] for i in xrange(self.nCol)] for j in xrange(self.nRow)] for iRow in xrange(self.nRow): for iCol in xrange(self.nCol): count = self.fluxFile.getPixelCount(iRow,iCol) fluxDict = self.fluxFile.getPixelSpectrum(iRow,iCol,weighted=True,firstSec=0,integrationTime=-1) self.fluxSpectra[iRow][iCol],self.fluxEffTime[iRow][iCol] = fluxDict['spectrum'],fluxDict['effIntTime'] self.fluxSpectra = np.array(self.fluxSpectra) self.fluxEffTime = np.array(self.fluxEffTime) DTCorr = self.getDeadTimeCorrection(self.fluxFile) #print "Bin widths = ",self.binWidths self.fluxSpectra = self.fluxSpectra/self.binWidths/self.fluxEffTime*DTCorr self.fluxSpectrum = self.calculateMedian(self.fluxSpectra) #find median of subtracted spectra across whole array return self.fluxSpectrum def loadSkySpectrum(self): self.skySpectra = [[[] for i in xrange(self.nCol)] for j in xrange(self.nRow)] self.skyEffTime = [[[] for i in xrange(self.nCol)] for j in xrange(self.nRow)] for iRow in xrange(self.nRow): for iCol in xrange(self.nCol): count = self.skyFile.getPixelCount(iRow,iCol) skyDict = self.skyFile.getPixelSpectrum(iRow,iCol,weighted=True,firstSec=0,integrationTime=-1) self.skySpectra[iRow][iCol],self.skyEffTime[iRow][iCol] = skyDict['spectrum'],skyDict['effIntTime'] self.skySpectra = np.array(self.skySpectra) self.skyEffTime = np.array(self.skyEffTime) DTCorr = self.getDeadTimeCorrection(self.skyFile) self.skySpectra = self.skySpectra/self.binWidths/self.skyEffTime*DTCorr self.skySpectrum = self.calculateMedian(self.skySpectra) #find median of subtracted spectra across whole array return self.skySpectrum def loadStdSpectrum(self, objectName="G158-100"): #import the known spectrum of the calibrator and rebin to the histogram parameters given #must be imported into array with dtype float so division later does not have error std = MKIDStd.MKIDStd() a = std.load(objectName) a = std.countsToErgs(a) #convert std spectrum to ergs/s/Angs/cm^2 for BB fitting and cleaning self.stdWvls = np.array(a[:,0]) self.stdFlux = np.array(a[:,1]) #std object spectrum in ergs/s/Angs/cm^2 if self.plots: #create figure for plotting standard spectrum modifications self.stdFig = plt.figure() self.stdAx = self.stdFig.add_subplot(111) plt.xlim(4000,11000) plt.plot(self.stdWvls,self.stdFlux*1E15,linewidth=1,color='grey',alpha=0.75) convX_rev,convY_rev = self.cleanSpectrum(self.stdWvls,self.stdFlux) convX = convX_rev[::-1] #convolved spectrum comes back sorted backwards, from long wvls to low which screws up rebinning convY = convY_rev[::-1] #rebin cleaned spectrum to flat cal's wvlBinEdges newa = rebin(convX,convY,self.wvlBinEdges) rebinnedWvl = np.array(newa[:,0]) rebinnedFlux = np.array(newa[:,1]) if self.plots: #plot final resampled spectrum plt.plot(convX,convY*1E15,color='blue') plt.step(rebinnedWvl,rebinnedFlux*1E15,color = 'black',where='mid') plt.legend(['%s Spectrum'%self.objectName,'Blackbody Fit','Gaussian Convolved Spectrum','Rebinned Spectrum'],'upper right', numpoints=1) plt.xlabel(ur"Wavelength (\r{A})") plt.ylabel(ur"Flux (10$^{-15}$ ergs s$^{-1}$ cm$^{-2}$ \r{A}$^{-1}$)") plt.ylim(1,5) plt.savefig(self.plotSavePath+'FluxCal_StdSpectrum_%s.eps'%self.objectName,format='eps') #convert standard spectrum back into counts/s/angstrom/cm^2 newa = std.ergsToCounts(newa) self.binnedSpectrum = np.array(newa[:,1]) def cleanSpectrum(self,x,y): ##=============== BB Fit to extend spectrum beyond 11000 Angstroms ================== fraction = 1.0/3.0 nirX = np.arange(int(x[(1.0-fraction)*len(x)]),20000) T, nirY = fitBlackbody(x,y,fraction=fraction,newWvls=nirX,tempGuess=5600) if self.plots: plt.plot(nirX,nirY*1E15,linestyle='--',linewidth=2, color="black",alpha=0.5) extendedWvl = np.concatenate((x,nirX[nirX>max(x)])) extendedFlux = np.concatenate((y,nirY[nirX>max(x)])) ##======= Gaussian convolution to smooth std spectrum to MKIDs median resolution ======== newX, newY = gaussianConvolution(extendedWvl,extendedFlux,xEnMin=0.005,xEnMax=6.0,xdE=0.001,fluxUnits = "lambda",r=self.r,plots=False) return newX, newY def calculateFactors(self): """ Calculate the sensitivity spectrum: the weighting factors that correct the flat calibrated spectra to the real spectra For relative calibration: First subtract sky spectrum from ARCONS observed spectrum. Then take median of this spectrum as it should be identical across the array, assuming the flat cal has done its job. Then divide this into the known spectrum of the object. For absolute calibration: self.fluxSpectra already has sky subtraction included. Simply divide this spectrum into the known standard spectrum. """ self.subtractedSpectrum = self.fluxSpectrum - self.skySpectrum self.subtractedSpectrum = np.array(self.subtractedSpectrum,dtype=float) #cast as floats so division does not fail later if self.method=='relative': normWvl = 5500 #Angstroms. Choose an arbitrary wvl to normalize the relative correction at ind = np.where(self.wvlBinEdges >= normWvl)[0][0]-1 self.subtractedSpectrum = self.subtractedSpectrum/(self.subtractedSpectrum[ind]) #normalize self.binnedSpectrum = self.binnedSpectrum/(self.binnedSpectrum[ind]) #normalize treated Std spectrum while we are at it #Calculate FluxCal factors self.fluxFactors = self.binnedSpectrum/self.subtractedSpectrum #self.fluxFlags = np.zeros(np.shape(self.fluxFactors),dtype='int') self.fluxFlags = np.empty(np.shape(self.fluxFactors),dtype='int') self.fluxFlags.fill(pipelineFlags.fluxCal['good']) #Initialise flag array filled with 'good' flags. JvE 5/1/2013. #set factors that will cause trouble to 1 #self.fluxFlags[self.fluxFactors == np.inf] = 1 self.fluxFlags[self.fluxFactors == np.inf] = pipelineFlags.fluxCal['infWeight'] #Modified to use flag dictionary - JvE 5/1/2013 self.fluxFactors[self.fluxFactors == np.inf]=1.0 self.fluxFlags[np.isnan(self.fluxFactors)] = pipelineFlags.fluxCal['nanWeight'] #Modified to use flag dictionary - JvE 5/1/2013 self.fluxFactors[np.isnan(self.fluxFactors)]=1.0 self.fluxFlags[self.fluxFactors <= 0]=pipelineFlags.fluxCal['LEzeroWeight'] #Modified to use flag dictionary - JvE 5/1/2013 self.fluxFactors[self.fluxFactors <= 0]=1.0 def calculateMedian(self, spectra): spectra2d = np.reshape(spectra,[self.nRow*self.nCol,self.nWvlBins]) wvlMedian = np.empty(self.nWvlBins,dtype=float) for iWvl in xrange(self.nWvlBins): spectrum = spectra2d[:,iWvl] goodSpectrum = spectrum[spectrum != 0]#dead pixels need to be taken out before calculating medians wvlMedian[iWvl] = np.median(goodSpectrum) return wvlMedian def makePlots(self): """ Output all debugging plots of ARCONS sky and object spectra, known calibrator spectrum, and sensitivity curve """ scratchDir = os.getenv('MKID_PROC_PATH') fluxDir = self.plotSavePath fluxCalBase = 'FluxCal_%s'%self.objectName plotFileName = fluxCalBase+".pdf" fullFluxPlotFileName = os.path.join(fluxDir,plotFileName) #uncomment to make some plots for the paper. Proper formatting Will also require figureheader to be imported and for movie making to be turned off self.paperFig = plt.figure() self.paperAx = self.paperFig.add_subplot(111) plt.xlim(4000,11000) plt.plot(self.binCenters,self.fluxFactors,linewidth=3,color='black') plt.xlabel(ur"Wavelength (\r{A})") plt.ylabel(ur"Spectral Calibration Curve") plt.ylim(0,150) plt.savefig(self.plotSavePath+'FluxCal_Sensitivity_%s.eps'%self.objectName,format='eps') #save throughput as a .npz file that other code uses when making paper plots np.savez(self.plotSavePath+'%s_%s_throughput.npz'%(self.objectName.strip(),self.fluxTstamp),throughput=1.0/self.fluxFactors,wvls=self.binCenters) pp = PdfPages(fullFluxPlotFileName) #plt.rcParams['font.size'] = 2 wvls = self.binCenters plt.figure() ax1 = plt.subplot(111) ax1.set_title('ARCONS median flat cal\'d flux in counts') plt.plot(wvls,self.fluxSpectrum) pp.savefig() plt.figure() ax2 = plt.subplot(111) ax2.set_title('ARCONS median flat cal\'d sky in counts') plt.plot(wvls,self.skySpectrum) pp.savefig() plt.figure() ax3 = plt.subplot(111) ax3.set_title('Flux data minus sky in counts') plt.plot(wvls,self.subtractedSpectrum) pp.savefig() plt.figure() ax4 = plt.subplot(111) ax4.set_title('Std Spectrum of %s'%(self.objectName)) plt.plot(self.stdWvls,self.stdFlux) pp.savefig() plt.figure() ax5 = plt.subplot(111) ax5.set_title('Binned Std Spectrum') plt.plot(wvls,self.binnedSpectrum) pp.savefig() plt.figure() ax6 = plt.subplot(111) ax6.set_title('Median Sensitivity Spectrum') ax6.set_xlim((3000,13000)) #ax6.set_ylim((0,5)) plt.plot(wvls,self.fluxFactors) pp.savefig() plt.figure() ax7 = plt.subplot(111) ax7.set_title('1/Sensitivity (Throughput)') ax7.set_xlim((4000,11000)) plt.plot(wvls,1.0/self.fluxFactors) pp.savefig() plt.figure() ax8 = plt.subplot(111) ax8.set_title('Flux Cal\'d ARCONS Spectrum of Std') plt.plot(wvls,self.fluxFactors*self.subtractedSpectrum) pp.savefig() pp.close() print "Saved Flux Cal plots to %s"%(fullFluxPlotFileName) def writeFactors(self,fluxCalFileName): """ Write flux cal weights to h5 file """ if os.path.isabs(fluxCalFileName) == True: fullFluxCalFileName = fluxCalFileName else: scratchDir = os.getenv('MKID_PROC_PATH') fluxDir = os.path.join(scratchDir,'fluxCalSolnFiles') fullFluxCalFileName = os.path.join(fluxDir,fluxCalFileName) try: fluxCalFile = tables.openFile(fullFluxCalFileName,mode='w') except: print 'Error: Couldn\'t create flux cal file, ',fullFluxCalFileName return calgroup = fluxCalFile.createGroup(fluxCalFile.root,'fluxcal','Table of flux calibration weights by wavelength') caltable = tables.Array(calgroup,'weights',object=self.fluxFactors,title='Flux calibration Weights indexed by wavelengthBin') flagtable = tables.Array(calgroup,'flags',object=self.fluxFlags,title='Flux cal flags indexed by wavelengthBin. 0 is Good') bintable = tables.Array(calgroup,'wavelengthBins',object=self.wvlBinEdges,title='Wavelength bin edges corresponding to third dimension of weights array') fluxCalFile.flush() fluxCalFile.close() print "Finished Flux Cal, written to %s"%(fullFluxCalFileName) def cleanSpectrum_old(self,x,y,objectName): ''' function to take high resolution spectrum of standard star, extend IR coverage with an exponential tail, then rebin down to ARCONS resolution. This function has since been deprecated with the current cleanSpectrum which uses a BB fit to extend IR coverage, and does the rebinning using a gaussian convolution. This is left in for reference. ''' #locations and widths of absorption features in Angstroms #features = [3890,3970,4099,4340,4860,6564,6883,7619] #widths = [50,50,50,50,50,50,50,50] #for i in xrange(len(features)): # #check for absorption feature in std spectrum # ind = np.where((x<(features[i]+15)) & (x>(features[i]-15)))[0] # if len(ind)!=0: # ind = ind[len(ind)/2] # #if feature is found (flux is higher on both sides of the specified wavelength where the feature should be) # if y[ind]<y[ind+1] and y[ind]<y[ind-1]: # #cut out width[i] around feature[i] # inds = np.where((x >= features[i]+widths[i]) | (x <= features[i]-widths[i])) # x = x[inds] # y = y[inds] #fit a tail to the end of the spectrum to interpolate out to desired wavelength in angstroms fraction = 3.0/4.0 newx = np.arange(int(x[fraction*len(x)]),20000) slopeguess = (np.log(y[-1])-np.log(y[fraction*len(x)]))/(x[-1]-x[fraction*len(x)]) print "Guess at exponential slope is %f"%(slopeguess) guess_a, guess_b, guess_c = float(y[fraction*len(x)]), x[fraction*len(x)], slopeguess guess = [guess_a, guess_b, guess_c] fitx = x[fraction*len(x):] fity = y[fraction*len(x):] exp_decay = lambda fx, A, x0, t: A * np.exp((fx-x0) * t) params, cov = curve_fit(exp_decay, fitx, fity, p0=guess, maxfev=2000) A, x0, t= params print "A = %s\nx0 = %s\nt = %s\n"%(A, x0, t) best_fit = lambda fx: A * np.exp((fx-x0)*t) calcx = np.array(newx,dtype=float) newy = best_fit(calcx) #func = interpolate.splrep(x[fration*len(x):],y[fraction*len(x):],s=smooth) #newx = np.arange(int(x[fraction*len(x)]),self.wvlBinEdges[-1]) #newy = interpolate.splev(newx,func) wl = np.concatenate((x,newx[newx>max(x)])) flux = np.concatenate((y,newy[newx>max(x)])) #new method, rebin data to grid of wavelengths generated from a grid of evenly spaced energy bins #R=7.0 at 4500 #R=E/dE -> dE = R/E dE = 0.3936 #eV start = 1000 #Angs stop = 20000 #Angs enBins = ObsFile.makeWvlBins(dE,start,stop) rebinned = rebin(wl,flux,enBins) re_wl = rebinned[:,0] re_flux = rebinned[:,1] #plt.plot(re_wl,re_flux,color='r') re_wl = re_wl[np.isnan(re_flux)==False] re_flux = re_flux[np.isnan(re_flux)==False] start1 = self.wvlBinEdges[0] stop1 = self.wvlBinEdges[-1] #regrid downsampled data new_wl = np.arange(start1,stop1) #print re_wl #print re_flux #print new_wl #weight=1.0/(re_flux)**(2/1.00) print len(re_flux) weight = np.ones(len(re_flux)) #decrease weights near peak ind = np.where(re_flux == max(re_flux))[0] weight[ind] = 0.3 for p in [1,2,3]: if p==1: wt = 0.3 elif p==2: wt = 0.6 elif p==3: wt = 0.7 try: weight[ind+p] = wt except IndexError: pass try: if ind-p >= 0: weight[ind-p] = wt except IndexError: pass weight[-4:] = 1.0 #weight = [0.7,1,0.3,0.3,0.5,0.7,1,1,1] #print len(weight) #weight = re_flux/min(re_flux) #weight = 1.0/weight #weight = weight/max(weight) #print weight f = interpolate.splrep(re_wl,re_flux,w=weight,k=3,s=max(re_flux)**1.71) new_flux = interpolate.splev(new_wl,f,der=0) return new_wl, new_flux
from util.ObsFile import ObsFile from util.FileName import FileName run = "PAL2014" date = "20141022" timeStamp = '20141023-033821' fn = FileName(run,date,timeStamp) of = ObsFile(fn.obs()) of.loadBeammapFile(fn.beammap()) of.loadBestWvlCalFile() print "wvlCalFileName=",of.wvlCalFileName fn2 = FileName(run,date,"") of.loadFlatCalFile(fn2.flatSoln()) row = 4 col = 4 firstSec = 72 integrationTime = 1 spec = of.getPixelSpectrum(row,col,firstSec,integrationTime) print "spec=",spec del of
class AppForm(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('Pixel Explorer') paramFile = sys.argv[1] self.params = readDict() self.params.read_from_file(paramFile) self.createMainFrame() self.createStatusBar() def createMainFrame(self): self.main_frame = QWidget() # Create the mpl Figure and FigCanvas objects. self.dpi = 100 self.fig = Figure((7.0, 7.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) self.axes0 = self.fig.add_subplot(111) cid=self.canvas.mpl_connect('button_press_event', self.clickCanvas) # Create the navigation toolbar, tied to the canvas self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) self.main_frame.setLayout(vbox) self.setCentralWidget(self.main_frame) def createStatusBar(self): self.status_text = QLabel("Awaiting orders.") self.statusBar().addWidget(self.status_text, 1) def openImage(self): timestampList = [self.params['obsUtcDate']+'-'+ts for ts in self.params['obsSequence']] run = self.params['run'] sunsetDate = self.params['obsSunsetDate'] utcDate = self.params['obsUtcDate'] self.intTime = self.params['intTime'] wvlLowerCutoff = self.params['wvlLowerCutoff'] wvlUpperCutoff = self.params['wvlUpperCutoff'] calTimestamp = self.params['wvlTimestamp'] wfn = FileName(run=run,date=sunsetDate,tstamp=calTimestamp).calSoln() calfn = FileName(run=run,date=self.params['wvlSunsetDate'],tstamp=calTimestamp).cal() ffn = FileName(run=run,date=self.params['flatCalSunsetDate'],tstamp='').flatSoln() obsFns = [FileName(run=run,date=sunsetDate,tstamp=timestamp).obs() for timestamp in timestampList] self.obList = [ObsFile(obsFn) for obsFn in obsFns] for ob in self.obList: print 'Loading ',ob.fullFileName ob.loadWvlCalFile(wfn) ob.loadFlatCalFile(ffn) self.cal = ObsFile(calfn) self.cal.loadWvlCalFile(wfn) self.cal.loadFlatCalFile(ffn) self.loadSpectra() def loadSpectra(self): fileName = self.params['outFileName'] if os.path.exists(fileName): data = np.load(fileName) self.spectra = data['cube'] self.frame = data['frame'] self.frameValues = self.frame[~np.isnan(self.frame)] self.wvlBinEdges = np.array(self.obList[0].flatCalWvlBins) self.frameIntTime = 300*6 else: self.spectra,self.wvlBinEdges = self.obList[0].getSpectralCube(weighted=True) self.frameIntTime = 0 for ob in self.obList[1:]: print ob.fileName cube,wvlBinEdges = ob.getSpectralCube(weighted=True) self.spectra += cube self.frameIntTime += ob.getFromHeader('exptime') self.spectra = np.array(self.spectra,dtype=np.float64) self.frame = np.sum(self.spectra,axis=2) hotPixMask = hotPixels.findHotPixels(image=self.frame,nsigma=2)['badflag'] self.frame[hotPixMask != 0] = np.nan self.frame[self.frame == 0] = np.nan self.frameValues = self.frame[~np.isnan(self.frame)] np.savez(fileName,cube=self.spectra,frame=self.frame) def plotWeightedImage(self): self.showFrame = np.array(self.frame) self.showFrame[np.isnan(self.frame)] = 0 handleMatshow = self.axes0.matshow(self.showFrame,cmap=matplotlib.cm.gnuplot2,origin='lower',vmax=np.mean(self.showFrame)+3*np.std(self.showFrame)) self.fig.colorbar(handleMatshow) def clickCanvas(self,event): self.showLaserSpectrum = True self.showPixelSpectrum = True self.showWvlLightCurves = True self.showWvlLightCurveHists = False self.showStdVsIntTime = True self.showNormStdVsIntTime = True col = round(event.xdata) row = round(event.ydata) if self.showPixelSpectrum: #next plot the integrated spectrum for this pixel in the total image spectrum = self.spectra[row,col] print sum(spectrum),' counts in broadband spectrum' def plotFunc(fig,axes): axes.plot(self.wvlBinEdges[:-1],spectrum) axes.set_xlabel(r'$\lambda$ ($\AA$)') axes.set_ylabel(r'total counts') popup = PopUp(parent=self,plotFunc=plotFunc,title='spectrum, pixel %d,%d (intTime=%d)'%(row,col,self.frameIntTime)) rebinSpecBins = 5 firstAfterConvolve = rebinSpecBins//2 rebinnedWvlEdges = self.wvlBinEdges[::rebinSpecBins] if self.showLaserSpectrum: #First plot the laser cal spectrum for this pixel to see if it's good laserSpectrum,binEdges = self.cal.getPixelSpectrum(row,col,weighted=True) def plotFunc(fig,axes): axes.plot(binEdges[:-1],laserSpectrum) axes.set_xlabel(r'$\lambda$ ($\AA$)') axes.set_ylabel(r'total counts') popup = PopUp(parent=self,plotFunc=plotFunc,title='Laser Cal Spectrum, pixel %d,%d'%(row,col)) if self.showWvlLightCurves: spectrumInTime = [] for iOb,ob in enumerate(self.obList): for sec in range(0,ob.getFromHeader('exptime'),self.intTime): spectrum,binEdges = ob.getPixelSpectrum(pixelRow=row,pixelCol=col,firstSec=sec,integrationTime=self.intTime,weighted=True) spectrum = np.convolve(spectrum,np.ones(rebinSpecBins),'same')[firstAfterConvolve::rebinSpecBins] spectrumInTime.append(spectrum) spectrumInTime = np.array(spectrumInTime) nBins = np.shape(spectrumInTime)[1] def plotFunc(fig,axes): #plot counts vs time for each wavelength bin t=np.arange(len(spectrumInTime[:,0]))*self.intTime for iBin in xrange(nBins): axes.plot(t,1.0*spectrumInTime[:,iBin]/self.intTime, c=cm.jet((iBin+1.)/nBins), label=r'%d-%d $\AA$'%(rebinnedWvlEdges[iBin], rebinnedWvlEdges[iBin+1])) axes.set_xlabel('time (s)') axes.set_ylabel('cps') #plot counts vs time summed over all wavelengths axes.plot(t,np.sum(spectrumInTime,axis=1)/self.intTime,c='k', label=r'%d-%d $\AA$'%(rebinnedWvlEdges[0],rebinnedWvlEdges[-1])) #axes.legend(loc='center right') popup = PopUp(parent=self,plotFunc=plotFunc,title='Light Curve by Band, Pixel %d,%d'%(row,col)) if self.showWvlLightCurveHists or self.showStdVsIntTime or self.showNormStdVsIntTime: intTimes = [1,2,3,5,10,15,30] spectrumVsIntTimeVsTime = [] for intTime in intTimes: spectrumInTime = [] for iOb,ob in enumerate(self.obList): for sec in range(0,ob.getFromHeader('exptime'),intTime): spectrum,binEdges = ob.getPixelSpectrum(pixelRow=row,pixelCol=col,firstSec=sec,integrationTime=intTime,weighted=True) spectrum = np.convolve(spectrum,np.ones(rebinSpecBins),'same')[firstAfterConvolve::rebinSpecBins] spectrumInTime.append(spectrum) spectrumInTime = np.array(spectrumInTime) spectrumVsIntTimeVsTime.append(spectrumInTime) #resulting array indexed as #spectrumVsIntTimeVsTime[iIntTime][iTimeChunk][iWvlBin] #sum over wavelength for total counts countsVsIntTimeVsTime = [np.sum(spectrumInTime,axis=1) for spectrumInTime in spectrumVsIntTimeVsTime] #countsVsIntTimeVsTime[iIntTime][iTimeChunk] if self.showWvlLightCurveHists: for iIntTime,countsVsTime in enumerate(countsVsIntTimeVsTime): def plotFunc(fig,axes): axes.hist(countsVsTime,bins=20) axes.set_xlabel('counts per intTime %d s'%intTimes[iIntTime]) popup = PopUp(parent=self,plotFunc=plotFunc,title='Int Time %d s, pixel %d,%d'%(intTimes[iIntTime],row,col)) countStds = [np.std(countsVsTime) for countsVsTime in countsVsIntTimeVsTime] countStds = np.array(countStds) countSqrts = [np.sqrt(np.median(countsVsTime)) for countsVsTime in countsVsIntTimeVsTime] countSqrts = np.array(countSqrts) spectrumStds = [np.std(spectrumVsTime,axis=0) for spectrumVsTime in spectrumVsIntTimeVsTime] spectrumSqrts = [np.sqrt(np.median(spectrumVsTime,axis=0)) for spectrumVsTime in spectrumVsIntTimeVsTime] spectrumStds = np.array(spectrumStds) spectrumSqrts = np.array(spectrumSqrts) if self.showStdVsIntTime: def plotFunc(fig,axes): axes.set_xlabel('integration time (s)') axes.plot(intTimes,countStds,'k',label=r'total $\sigma$') axes.plot(intTimes,countSqrts,'k--',label=r'$\sqrt{med(N)}$') nBins = np.shape(spectrumStds)[1] for iBin in xrange(nBins): axes.plot(intTimes,spectrumStds[:,iBin], c=cm.jet((iBin+1.)/nBins), label=r'%d-%d $\AA$ $\sigma$'%(rebinnedWvlEdges[iBin], rebinnedWvlEdges[iBin+1])) axes.plot(intTimes,spectrumSqrts[:,iBin], c=cm.jet((iBin+1.)/nBins),linestyle='--') axes.legend(loc='upper left') popup = PopUp(parent=self,plotFunc=plotFunc, title=r'$\sigma$ vs Integration Time, Pixel %d,%d'%(row,col)) if self.showNormStdVsIntTime: def plotFunc(fig,axes): axes.set_xlabel('integration time (s)') axes.set_ylabel('normalized $\sigma$') axes.plot(intTimes,countSqrts/np.max(countSqrts),'k--', label=r'$\sqrt{N}$') axes.plot(intTimes,countStds/np.max(countSqrts),'k', label=r'%d-%d $\AA$'%(rebinnedWvlEdges[0],rebinnedWvlEdges[-1])) nBins = np.shape(spectrumStds)[1] for iBin in xrange(nBins): axes.plot(intTimes, spectrumStds[:,iBin]/np.max(spectrumSqrts[:,iBin]), c=cm.jet((iBin+1.0)/nBins), label=r'%d-%d $\AA$'%(rebinnedWvlEdges[iBin], rebinnedWvlEdges[iBin+1])) axes.legend(loc='upper left') popup = PopUp(parent=self,plotFunc=plotFunc, title='Normalized Standard Deviation vs IntegrationTime, Pixel %d,%d'%(row,col))