def _calculateSkyCoords(self): """ Calculates sky coordinates for the slits. Resamples the direct image pixels back to the slit image pixels. Takes the average in x-direction which is assumed to be across the slit, so that the sky coordinates are at the centre of the slit in this direction. """ #calculate the RA and DEC of the centre of the centre slit centre = np.array([[self.result['xcenter'], self.result['ycenter']], ], np.float_) sky = self.result['WCS'].wcs_pix2sky(centre, 1) self.result['RA'] = sky[0][0] self.result['DEC'] = sky[0][1] #each slit pixels for key, value in self.slits.items(): ymin = value['ymin'] + self.result['y'] ymax = value['ymax'] + self.result['y'] y = np.arange(ymin, ymax) #y is now sampled to the supersampled direct image #what we ultimately need is the coordinates #for slit pixels... resample = value['pixels'] ybin = m.frebin(y, resample) #for x, given that it is the slit width #we need the mean value xmin = value['xmin'] + self.result['x'] xmax = value['xmax'] + self.result['x'] mean = np.mean(np.array([xmin, xmax])) #x = np.zeros(len(y)) + mean xbin = np.zeros(len(ybin)) + mean pixels = [] for a, b in zip(xbin, ybin): pixels.append([a, b]) pixels = np.asanyarray(pixels) sky = self.result['WCS'].wcs_pix2sky(pixels, 1) #sky = self.result['WCS'].toworld(pixels) self.slits[key]['coordinates'] = sky #record x and y in the slit frame self.slits[key]['coordinatesXY'] = pixels self.slits[key]['coordinatesX'] = np.ones(value['pixels']) self.slits[key]['coordinatesY'] = np.arange(value['pixels']) + 1 #RA and DEC of the centre of the slit centre = np.array([[mean, (ymax + ymin) / 2.], ]) sky = self.result['WCS'].wcs_pix2sky(centre, 1) self.slits[key]['RA'] = sky[0][0] self.slits[key]['DEC'] = sky[0][1] if self.debug: print self.slits[key]['RA'], self.slits[key]['DEC']
def _calculateDifference(self): """ Calculates the difference between the derived observed and SDSS spectra. Interpolates the SDSS spectra to the same wavelength scale. .. Warning:: We do not conserve flux here when interpolating. This is because we do not actually interpolate flux but flux density which is per angstrom. """ ms = (self.fitting['SDSSwave'] >= np.min(self.fitting['obsWavelengths'])) &\ (self.fitting['SDSSwave'] <= np.max(self.fitting['obsWavelengths'])) newflux = m.frebin(self.fitting['SDSSflux'][ms], len(self.fitting['obsSpectraConvolved']))#, total=True) self.fitting['spectraRatio'] = self.fitting['obsSpectraConvolved'] / newflux self.fitting['interpFlux'] = newflux
def _calculateDifference(self): """ Calculates the difference between the derived observed and SDSS spectra. Interpolates the SDSS spectra to the same wavelength scale. .. Warning:: We do not conserve flux here when interpolating. This is because we do not actually interpolate flux but flux density which is per angstrom. """ ms = (self.fitting['SDSSwave'] >= np.min(self.fitting['obsWavelengths'])) &\ (self.fitting['SDSSwave'] <= np.max(self.fitting['obsWavelengths'])) newflux = m.frebin( self.fitting['SDSSflux'][ms], len(self.fitting['obsSpectraConvolved'])) #, total=True) self.fitting[ 'spectraRatio'] = self.fitting['obsSpectraConvolved'] / newflux self.fitting['interpFlux'] = newflux
def hrebin(imagefile, newx, newy, output='rebinned.fits', ext=0, total=False): """ Expand or contract a FITS image and update the header. Based on IDL routine hrebin.pro, but removed some functionality :todo: remove the pywcs dependency :param imagefile: name of the FITS file to be rebinned :param newx: size of the new image in the X direction, integer scalar :param newy: size of the new image in the Y direction, integer scalar :param output: Name of the outputfile or None :param ext: the FITS extension of the header and data :return: None or updated img and hdr """ #todo: remove the pywcs dependency #todo: add a routine for exact rebinning, now done with the same fh = pf.open(imagefile) oldhdr = fh[ext].header oldimg = fh[ext].data fh.close() wcs = pywcs.WCS(oldhdr) #old size #xsize = oldhdr['NAXIS1'] #ysize = oldhdr['NAXIS2'] ysize, xsize = oldimg.shape #ratios xratio = newx / float(xsize) yratio = newy / float(ysize) #change in the aspect ratio lambd = yratio / xratio #Ratio of pixel areas pix_ratio = xratio * yratio #chech whether the new size is an exact match exact = (xsize % newx == 0) | (newx % xsize == 0) and\ (ysize % newy == 0) | (newy % ysize == 0) #rebin based on whether the rebinning is exact or not if exact: oldimg = manipulate.frebin(oldimg, newx, newy, total=total) else: oldimg = manipulate.frebin(oldimg, newx, newy, total=total) #start updating the new header oldhdr['NAXIS1'] = int(newx) oldhdr['NAXIS2'] = int(newy) #add comment orig size and new size oldhdr.add_comment('rebinned: original image was %i by %i' % (xsize, ysize)) #Correct the position of the reference pixel. #Note that CRPIX values are given in FORTRAN (first pixel is (1,1)) convention crpix = wcs.wcs.crpix #update astrometry if exact and xratio > 1: crpix1 = (crpix[0] - 1.0) * xratio + 1.0 else: crpix1 = (crpix[0] - 0.5) * xratio + 0.5 if exact and yratio > 1: crpix2 = (crpix[1] - 1.0) * yratio + 1.0 else: crpix2 = (crpix[1] - 0.5) * yratio + 0.5 oldhdr['CRPIX1'] = crpix1 oldhdr['CRPIX2'] = crpix2 #distortion correction, not implemented #oldhdr['CDELT1'] /= xratio #oldhdr['CDELT2'] /= yratio oldhdr['CD1_1'] /= xratio oldhdr['CD1_2'] /= yratio oldhdr['CD2_1'] /= xratio oldhdr['CD2_2'] /= yratio #modify the B scale #oldhdr['BSCALE'] /= pix_ratio #write out a new FITS file hdu = pf.PrimaryHDU(oldimg) hdu.header = oldhdr hdu.header.add_history( 'Updated: %s' % datetime.datetime.isoformat(datetime.datetime.now())) if os.path.isfile(output): os.remove(output) hdu.writeto(output) return oldimg, oldhdr
def hrebin(imagefile, newx, newy, output='rebinned.fits', ext=0, total=False): """ Expand or contract a FITS image and update the header. Based on IDL routine hrebin.pro, but removed some functionality :todo: remove the pywcs dependency :param imagefile: name of the FITS file to be rebinned :param newx: size of the new image in the X direction, integer scalar :param newy: size of the new image in the Y direction, integer scalar :param output: Name of the outputfile or None :param ext: the FITS extension of the header and data :return: None or updated img and hdr """ #todo: remove the pywcs dependency #todo: add a routine for exact rebinning, now done with the same fh = pf.open(imagefile) oldhdr = fh[ext].header oldimg = fh[ext].data fh.close() wcs = pywcs.WCS(oldhdr) #old size #xsize = oldhdr['NAXIS1'] #ysize = oldhdr['NAXIS2'] ysize, xsize = oldimg.shape #ratios xratio = newx / float(xsize) yratio = newy / float(ysize) #change in the aspect ratio lambd = yratio / xratio #Ratio of pixel areas pix_ratio = xratio * yratio #chech whether the new size is an exact match exact = (xsize % newx == 0) | (newx % xsize == 0) and\ (ysize % newy == 0) | (newy % ysize == 0) #rebin based on whether the rebinning is exact or not if exact: oldimg = manipulate.frebin(oldimg, newx, newy, total=total) else: oldimg = manipulate.frebin(oldimg, newx, newy, total=total) #start updating the new header oldhdr['NAXIS1'] = int(newx) oldhdr['NAXIS2'] = int(newy) #add comment orig size and new size oldhdr.add_comment('rebinned: original image was %i by %i' % (xsize, ysize)) #Correct the position of the reference pixel. #Note that CRPIX values are given in FORTRAN (first pixel is (1,1)) convention crpix = wcs.wcs.crpix #update astrometry if exact and xratio > 1: crpix1 = (crpix[0] - 1.0) * xratio + 1.0 else: crpix1 = (crpix[0] - 0.5) * xratio + 0.5 if exact and yratio > 1: crpix2 = (crpix[1] - 1.0) * yratio + 1.0 else: crpix2 = (crpix[1] - 0.5) * yratio + 0.5 oldhdr['CRPIX1'] = crpix1 oldhdr['CRPIX2'] = crpix2 #distortion correction, not implemented #oldhdr['CDELT1'] /= xratio #oldhdr['CDELT2'] /= yratio oldhdr['CD1_1'] /= xratio oldhdr['CD1_2'] /= yratio oldhdr['CD2_1'] /= xratio oldhdr['CD2_2'] /= yratio #modify the B scale #oldhdr['BSCALE'] /= pix_ratio #write out a new FITS file hdu = pf.PrimaryHDU(oldimg) hdu.header = oldhdr hdu.header.add_history('Updated: %s' % datetime.datetime.isoformat(datetime.datetime.now())) if os.path.isfile(output): os.remove(output) hdu.writeto(output) return oldimg, oldhdr
def _fitSlitsToDirectImage(self): """ Fits a slit mask profiles to a direct image to recover the position and orientation of the slitmask. By default the counts are not normalized to a peak count, but this can be controlled using the optional keyword normalize. This should be used for debugging purposes only. I included this part because in the beginning we were missing flux calibration. :Note: This is a very slow algorithm because of the insanely many nested for loops... :todo: rewrite the algorithm. :rtype: dictionary """ #generates a model array from the slit values, takes into account potential #throughput of a slit model = np.array([]) for vals in self.slits.values(): #must rebin/interpolate to the right scale, conserve surface flux new = m.frebin(vals['profile'], int(vals['height']), total=True) model = np.append(model, new) self.fitting[vals['file']] = new if self.normalize: model /= np.max(model) #mask out negative values and especially those equal to zero msk = model > 0.0 self.fitting['model'] = model[msk] self.fitting['mask'] = msk #basic model information mean = np.mean(self.fitting['model']) sig = np.sqrt(np.sum((self.fitting['model'] - mean) ** 2)) diff = self.fitting['model'] - mean self.fitting['sig'] = sig self.fitting['diff'] = diff #generate rotations if self.rotation: rotations = np.arange(-self.fitting['rotation'], self.fitting['rotation'], self.fitting['rotstep']) rotations[(rotations < 1e-8) & (rotations > -1e-8)] = 0.0 #make a copy of the direct image origimage = self.direct['rotatedImage'].copy() else: rotations = [0, ] #x and yranges to cover in the grid xran = range(-self.fitting['xrange'], self.fitting['xrange'], self.fitting['xstep']) yran = range(-self.fitting['yrange'], self.fitting['yrange'], self.fitting['ystep']) #define some helper variables out = [] corrmin = -1e10 cm = 1e30 #loop over a range of rotations, x and y positions #this should be done in some other way than having three nested loops... not good for r in rotations: if self.rotation: if r != 0.0: #d = interpolation.rotate(origimage, r, reshape=False) d, hdrR = modify.hrot2(origimage.copy(), self.direct['rotatedHeader'], r, xc=None, yc=None) else: d = origimage.copy() hdrR = self.direct['rotatedHeader'] else: d = self.direct['rotatedImage'].copy() hdrR = self.direct['rotatedHeader'] #only for debugging purposes if self.normalize: d /= np.max(d) for x in xran: for y in yran: #all slits dirdata = np.array([]) for s in self.slits.values(): #direct image data dirdat = d[s['ymin'] + y:s['ymax'] + y,\ s['xmin'] + x:s['xmax'] + x] #sum the counts inside the slit dirdat = np.sum(dirdat, axis=1) dirdata = np.append(dirdata, dirdat.ravel()) #remove the masked pixels dirdata = dirdata[self.fitting['mask']] #chi**2, p-value chisq = stats.chisquare(dirdata, self.fitting['model']) #chisq = self._chiSquare(dirdata, self.fitting['model'], 1./self.fitting['model']) #correlation coeff mean = np.mean(dirdata) sig = np.sqrt(np.sum((dirdata - mean) ** 2)) diff = dirdata - mean corr = np.sum(self.fitting['diff'] * diff) / self.fitting['sig'] / sig #save info tmp = [r, x, y, chisq[0], chisq[0] / s['pixels'], chisq[1], corr] #tmp = [r, x, y, chisq, chisq / s['pixels'], 0.0, corr] out.append(tmp) if chisq[0] < cm: #if chisq < cm: cm = chisq[0] #cm = chisq minpos = tmp dir = dirdata img = d hdr = hdrR if corr > corrmin: corrmin = corr minpos2 = tmp dir2 = dirdata if self.debug: print r, x, y, chisq[0] / s['pixels'], chisq[1], corr #print r, x, y, chisq / s['pixels'], 0.0, corr #save results self.result['outputs'] = out self.result['minimaPosition'] = minpos self.result['FinalImage'] = img self.result['FinalHeader'] = hdr self.result['WCS'] = pywcs.WCS(hdr) #self.result['WCS'] = wcs.Projection(hdr) #choose the method if 'chi' in self.fitting['method']: self.result['rotation'] = minpos[0] self.result['posangle'] = minpos[0] + self.slits[self.centralFile]['POSANGLE'] self.result['x'] = minpos[1] self.result['y'] = minpos[2] self.result['bestfit'] = dir self.result['xcenter'] = self.direct['xposition'] + minpos[1] self.result['ycenter'] = self.direct['yposition'] + minpos[2] self.result['pvalue'] = minpos[5] elif 'corr' in self.fitting['method']: self.result['rotation'] = minpos2[0] self.result['posangle'] = minpos[0] + self.slits[self.centralFile]['POSANGLE'] self.result['x'] = minpos2[1] self.result['y'] = minpos2[2] self.result['bestfit'] = dir2 self.result['xcenter'] = self.direct['xposition'] + minpos2[1] self.result['ycenter'] = self.direct['yposition'] + minpos2[2] self.result['pvalue'] = minpos2[5] else: raise NotImplementedError, 'This minimization method has not yet been implemented...' if self.debug: print minpos print minpos2