def get_slits(slits): ''' this function checks whether the detected slits for the left and right edges are the same length and also that the number of slits detected for each image section is the same. Additionally it checks whether there is an unexpecdedly large deviation in the slit curvature. ''' num_sections = len(slits) # set up the calculation for the number of slits calculated # for each section section = [slits[i] for i in range(0, num_sections)] lengths = [[len(section[j][0]), len(section[j][1])] for j in range(0, len(section))] # first check and see if all the left and right edges are of equal length for k in range(0, len(lengths)): if lengths[k][0] != lengths[k][1]: msg = 'Number of edges for section %i does not match. Try changing your threshold value' % k raise SALTSpecError(msg) for m in range(0, len(lengths)): if lengths[m][0] == 0 or lengths[m][1] == 0: msg = 'No edges detected for section %i. Try changing your threshold value' % k raise SALTSpecError(msg) equal = [] for i in range(0, len(lengths) - 1): if lengths[i][0] != lengths[i + 1][0] or lengths[i][1] != lengths[i + 1][1]: equal.append(False) else: equal.append(True) if (not all(equal)): msg = 'An unequal amount of slits were detected for one of the sections' raise SALTSpecError(msg) # write the slit positions into the defined slit positions format # such that each slit is given by: # [slitnumber, [bottom slit positions], [top slit positions]] allslits = [] for j in range(0, len(slits[0][0][:])): spline_left = [slits[i][0][j] for i in range(0, len(slits))] spline_right = [slits[i][1][j] for i in range(0, len(slits))] allslits.append([j, spline_left, spline_right]) return allslits
def specsky(images, outimages, outpref, method='normal', section=None, function='polynomial', order=2, clobber=True, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # Check the input images infiles = saltio.argunpack('Input', images) # create list of output files outfiles = saltio.listparse('Outfile', outimages, outpref, infiles, '') if method not in ['normal', 'fit']: msg = '%s mode is not supported yet' % method raise SALTSpecError(msg) if section is None: section = saltio.getSection(section) msg = 'This mode is not supported yet' raise SALTSpecError(msg) else: section = saltio.getSection(section) # Identify the lines in each file for img, ofile in zip(infiles, outfiles): log.message('Subtracting sky spectrum in image %s into %s' % (img, ofile)) # open the images hdu = saltio.openfits(img) # sky subtract the array hdu = skysubtract(hdu, method=method, section=section, funct=function, order=order) # write out the image if clobber and os.path.isfile(ofile): saltio.delete(ofile) hdu.writeto(ofile)
def which_ext(hdu, ext_name, debug): ''' this function determines which extension in the fits file is the primary, sci and binary table. an integer is retunred with the position of the ext in the fits file. * hdu is an opened fits file. opened with pyfits.open * ext_name is the name of the wanted extension ''' if debug: print 'determining the fits extension for %s' % ext_name if ext_name == 'PRIMARY': for i in range(0, len(hdu)): if hdu[i].name == 'PRIMARY': pos = i return pos elif ext_name == 'SCI': for i in range(0, len(hdu)): if hdu[i].name == 'SCI': pos = i return pos elif ext_name == 'BINTABLE': for i in range(0, len(hdu)): if hdu[i].name == 'BINTABLE': pos = i return pos else: msg = 'the hdu was not found in the fits header' raise SALTSpecError(msg)
def findsol(xarr, soldict, y_i, caltype, nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slit, xbin, ybin, slitid, function, order): """Find the wavelength solution. Either from a database containing all the solutions adn calculate the best one or calculate based on the model for RSS """ if caltype == 'line': function, order, coef, domain = findlinesol( soldict, y_i, nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slitid, xarr) if function is None: msg = 'No solution matches for %s, %s, %s, %s, %s, %s' % ( instrume, grating, grang, arang, filtername, slitid) raise SALTSpecError(msg) if coef is None: return None order = int(order) ws = WavelengthSolution.WavelengthSolution( xarr, xarr, function=function, order=order) ws.func.func.domain = domain ws.set_coef(coef) w_arr = ws.value(xarr) elif caltype == 'rss': w_arr = calcsol( xarr, y_i, instrume, grating, grang, arang, filtername, slit, xbin, ybin, function, order) else: message = 'SALTRECTIFY--Invalid caltype' raise SALTSpecError(message) return w_arr
def specextract(images, outfile, method='normal', section=None, thresh=3.0, minsize=3.0, outformat='ascii', ext=1, convert=True, clobber=True, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # Check the input images infiles = saltio.argunpack('Input', images) # create list of output files outfiles = saltio.argunpack('outfile', outfile) if method is 'weighted': msg = 'This mode is not supported yet' raise SALTSpecError(msg) section = saltio.checkfornone(section) if section is not None: sections = saltio.getSection(section, iraf_format=False) section = [] for i in range(0, len(sections), 2): section.append((sections[i], sections[i + 1])) # Identify the lines in each file for img, ofile in zip(infiles, outfiles): log.message('\nExtracting spectrum in image %s to %s' % (img, ofile), with_header=False, with_stdout=verbose) # open the images hdu = saltio.openfits(img) ap_list = extract(hdu, ext=ext, method=method, section=section, minsize=minsize, thresh=thresh, convert=convert) # write the spectra out if ap_list: write_extract(ofile.strip(), ap_list, outformat=outformat, clobber=clobber) log.message('', with_header=False, with_stdout=verbose)
def speccal(specfile, outfile, calfile, extfile, airmass=None, exptime=None, clobber=True, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # read in the specfile and create a spectrum object obs_spectra = st.readspectrum(specfile, error=True, ftype='ascii') # read in the std file and convert from magnitudes to fnu # then convert it to fwave (ergs/s/cm2/A) cal_spectra = st.readspectrum(calfile, error=False, ftype='ascii') # read in the extinction file (leave in magnitudes) ext_spectra = st.readspectrum(extfile, error=False, ftype='ascii') # determine the airmass if not specified if saltio.checkfornone(airmass) is None: message = 'Airmass was not supplied' raise SALTSpecError(message) # determine the exptime if not specified if saltio.checkfornone(airmass) is None: message = 'Exposure Time was not supplied' raise SALTSpecError(message) # calculate the calibrated spectra log.message('Calculating the calibration curve for %s' % specfile) error = False try: if obs_spectra.var is not None: error = True except: error = False flux_spectra = calfunc( obs_spectra, cal_spectra, ext_spectra, airmass, exptime, error) # write the spectra out st.writespectrum(flux_spectra, outfile, ftype='ascii', error=error)
def createoutputxaxis(wstart, wend, nw): """Create the output array for the axis for the data to be transformed onto. """ try: warr = np.linspace(wstart, wend, num=nw) except Exception as e: msg = 'No output array was create because %s' % e raise SALTSpecError(msg) return warr
def write_extract(ofile, ap_list, outformat='ascii', fvar=None, clobber=False): """Write out to either a txt file or fits file depending on the extension of ofile """ if outformat == 'FITS': write_extract_fits(ofile, ap_list, clobber) elif outformat == 'ascii': write_extract_text(ofile, ap_list, clobber) else: msg = '%s is not a supported output format' % outformat raise SALTSpecError(msg) return
def createbadpixel(inhdu, bphdu, sci_ext, bp_ext): """Create the bad pixel hdu from a bad pixel hdu""" if bphdu is None: data = inhdu[sci_ext].data * 0.0 else: infile = inhdu._HDUList__file.name bpfile = bphdu._HDUList__file.name for k in ['INSTRUME', 'CCDSUM', 'NAXIS1', 'NAXIS2']: if not saltkey.compare( inhdu[sci_ext], bphdu[sci_ext], k, infile, bpfile): message = '%s and %s are not the same %s' % (infile, bpfile, k) raise SALTSpecError(message) data = bphdu[sci_ext].data.copy() header = inhdu[sci_ext].header.copy() header['EXTVER'] = bp_ext header.update('SCIEXT', sci_ext, comment='Extension of science frame') return pyfits.ImageHDU(data=data, header=header, name='BPM')
def read_slits_from_ds9(simg, order=1): ''' This function reads the input from an outputslitfile if it specified as a ds9 region file. It will read the slit positions return an array that resembles the split_pos array that is passed to the extract_slits function and the order of the spline to be fitted. ''' # read in the slit file slitfile = open(simg, 'r') slits = slitfile.read() slitfile.close() # throw an error if the positions aren't in image format if slits.count('image') == 0: msg = "Please use 'image' for format when saving ds9 region file" raise SALTSpecError(msg) slits = slits.split('\n') # loop through and create all slits allslits = [] alltext = [] i = 0 for line in slits: if line.startswith('#') or line.startswith('global'): pass elif line.count('box'): line = line.split('#') if len(line) == 2: rtext = line[1].replace(' text={', '') rtext = rtext.replace('}', '') alltext.append(rtext.split(',')) line = line[0].replace('box(', '') line = line.replace(')', '') col = line.split(',') slitnum = i left_edge = [int(float(col[1]) - 0.5 * float(col[3]))] right_edge = [int(float(col[1]) + 0.5 * float(col[3]))] allslits.append([slitnum, left_edge, right_edge]) i += 1 return order, allslits, alltext
def specprepare(images, outimages, outpref, badpixelimage='', clobber=True, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # Check the input images infiles = saltio.argunpack('Input', images) # create list of output files outfiles = saltio.listparse('Outfile', outimages, outpref, infiles, '') # verify that the input and output lists are the same length saltio.comparelists(infiles, outfiles, 'Input', 'output') # open the badpixel image if saltio.checkfornone(badpixelimage) is None: badpixelstruct = None else: try: badpixelstruct = saltio.openfits(badpixelimage) except saltio.SaltIOError as e: msg = 'badpixel image must be specificied\n %s' % e raise SALTSpecError(msg) # open each raw image file for img, oimg, in zip(infiles, outfiles): # open the fits file struct = saltio.openfits(img) # prepare file struct = prepare(struct, badpixelstruct) # write FITS file saltio.writefits(struct, oimg, clobber=clobber) saltio.closefits(struct) message = 'SPECPREPARE -- %s => %s' % (img, oimg) log.message(message)
def specarcstraighten(images, outfile, function='poly', order=3, rstep=1, rstart='middlerow', nrows=1, y1=None, y2=None, sigma=5, sections=3, niter=5, startext=0, clobber=False, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # Check the input images infiles = saltio.argunpack('Input', images) # create list of output files outfiles = saltio.argunpack('Output', outfile) # Identify the lines in each file for img, ofile in zip(infiles, outfiles): # open the image hdu = saltio.openfits(img) # get the basic information about the spectrograph dateobs = saltkey.get('DATE-OBS', hdu[0]) try: utctime = saltkey.get('UTC-OBS', hdu[0]) except SaltError: utctime = saltkey.get('TIME-OBS', hdu[0]) instrume = saltkey.get('INSTRUME', hdu[0]).strip() grating = saltkey.get('GRATING', hdu[0]).strip() grang = saltkey.get('GR-ANGLE', hdu[0]) grasteps = saltkey.get('GRTILT', hdu[0]) arang = saltkey.get('AR-ANGLE', hdu[0]) arsteps = saltkey.get('CAMANG', hdu[0]) rssfilter = saltkey.get('FILTER', hdu[0]) specmode = saltkey.get('OBSMODE', hdu[0]) masktype = saltkey.get('MASKTYP', hdu[0]).strip().upper() slitname = saltkey.get('MASKID', hdu[0]) xbin, ybin = saltkey.ccdbin(hdu[0], img) for i in range(startext, len(hdu)): if hdu[i].name == 'SCI': log.message('Proccessing extension %i in %s' % (i, img)) # things that will change for each slit if masktype == 'LONGSLIT': slit = st.getslitsize(slitname) objid = None #elif masktype == 'MOS': #slit = 1.5 # slit=saltkey.get('SLIT', hdu[i]) # set up the x and y positions #miny = hdu[i].header['MINY'] #maxy = hdu[i].header['MAXY'] #ras = hdu[i].header['SLIT_RA'] #des = hdu[i].header['SLIT_DEC'] #objid = hdu[i].header['SLITNAME'] # Check the perfomance of masks at different PA #rac = hdu[0].header['MASK_RA'] #dec = hdu[0].header['MASK_DEC'] #pac = hdu[0].header['PA'] else: msg = '%s is not a currently supported masktype' % masktype raise SALTSpecError(msg) if instrume not in ['PFIS', 'RSS']: msg = '%s is not a currently supported instrument' % instrume raise SALTSpecError(msg) # set up the data for the source try: data = hdu[i].data except Exception as e: message = 'Unable to read in data array in %s because %s' % ( img, e) raise SALTSpecError(message) # set up the center row if rstart == 'middlerow': ystart = int(0.5 * len(data)) else: ystart = rstart # set up the xarr array based on the image xarr = np.arange(len(data[ystart]), dtype='int64') # calculate the transformation ImageSolution = arcstraight(data, xarr, ystart, function=function, order=order, rstep=rstep, y1=y1, y2=y2, sigma=sigma, sections=sections, niter=niter, log=log, verbose=verbose) if outfile and len(ImageSolution): writeIS(ImageSolution, outfile, dateobs=dateobs, utctime=utctime, instrume=instrume, grating=grating, grang=grang, grasteps=grasteps, arsteps=arsteps, arang=arang, rfilter=rssfilter, slit=slit, xbin=xbin, ybin=ybin, objid=objid, filename=img, log=log, verbose=verbose)
def extract(hdu, ext=1, method='normal', section=[], minsize=3.0, thresh=3.0, convert=True): """For a given image, extract a 1D spectra from the image and write the spectra to the output file """ ap_list = [] i = ext if hdu[i].name == 'SCI': # set up the data, variance, and bad pixel frames # first step is to find the region to extract data_arr = hdu[i].data try: var_arr = hdu[hdu[i].header['VAREXT']].data except: var_arr = None try: bpm_arr = hdu[hdu[i].header['BPMEXT']].data except: bpm_arr = None var_arr = None bpm_arr = None xarr = np.arange(len(data_arr[0])) # convert using the WCS information try: w0 = saltkey.get('CRVAL1', hdu[i]) dw = saltkey.get('CD1_1', hdu[i]) except Exception as e: msg = 'Error on Ext %i: %s' % (i, e) raise SALTSpecError(msg) warr = w0 + dw * xarr # convert from air to vacuum if convert: warr = Spectrum.air2vac(warr) # set up the sections in case of findobj if section is None: section = findobj.findObjects(data_arr, method='median', specaxis=1, minsize=minsize, thresh=thresh, niter=5) # extract all of the regions for sec in section: ap = apext.apext(warr, data_arr, ivar=var_arr) y1, y2 = sec ap.flatten(y1, y2) ap_list.append(ap) return ap_list
def wavemap(hdu, soldict, caltype='line', function='poly', order=3, blank=0, nearest=False, array_only=False, clobber=True, log=None, verbose=True): """Read in an image and a set of wavlength solutions. Calculate the best wavelength solution for a given dataset and then apply that data set to the image return """ # set up the time of the observation dateobs = saltkey.get('DATE-OBS', hdu[0]) utctime = saltkey.get('TIME-OBS', hdu[0]) exptime = saltkey.get('EXPTIME', hdu[0]) instrume = saltkey.get('INSTRUME', hdu[0]).strip() grating = saltkey.get('GRATING', hdu[0]).strip() if caltype == 'line': grang = saltkey.get('GRTILT', hdu[0]) arang = saltkey.get('CAMANG', hdu[0]) else: grang = saltkey.get('GR-ANGLE', hdu[0]) arang = saltkey.get('AR-ANGLE', hdu[0]) filtername = saltkey.get('FILTER', hdu[0]).strip() slitname = saltkey.get('MASKID', hdu[0]) slit = st.getslitsize(slitname) xbin, ybin = saltkey.ccdbin(hdu[0]) timeobs = sr.enterdatetime('%s %s' % (dateobs, utctime)) # check to see if there is more than one solution if caltype == 'line': if len(soldict) == 1: sol = soldict.keys()[0] slitid = None if not sr.matchobservations(soldict[sol], instrume, grating, grang, arang, filtername, slitid): msg = 'Observations do not match setup for transformation but using the solution anyway' if log: log.warning(msg) for i in range(1, len(hdu)): if hdu[i].name == 'SCI': if log: log.message('Correcting extension %i' % i) istart = int(0.5 * len(hdu[i].data)) # open up the data # set up the xarr and initial wavlength solution xarr = np.arange(len(hdu[i].data[istart]), dtype='int64') # get the slitid try: slitid = saltkey.get('SLITNAME', hdu[i]) except: slitid = None #check to see if wavext is already there and if so, then check update #that for the transformation from xshift to wavelength if saltkey.found('WAVEXT', hdu[i]): w_ext = saltkey.get('WAVEXT', hdu[i]) - 1 wavemap = hdu[w_ext].data function, order, coef = sr.findlinesol( soldict, istart, nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slitid, xarr) ws = WavelengthSolution.WavelengthSolution(xarr, xarr, function=function, order=order) ws.set_coef(coef) for j in range(len(hdu[i].data)): wavemap[j, :] = ws.value(wavemap[j, :]) if array_only: return wavemap hdu[w_ext].data = wavemap continue # set up a wavelength solution -- still in here for testing MOS data try: w_arr = sr.findsol(xarr, soldict, istart, caltype, nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slit, xbin, ybin, slitid, function, order) except SALTSpecError as e: if slitid: msg = 'SLITID %s: %s' % (slitid, e) if log: log.warning(msg) continue else: raise SALTSpecError(e) if w_arr is None: w_arr = sr.findsol(xarr, soldict, istart, 'rss', nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slit, xbin, ybin, slitid, function, order) # for each line in the data, determine the wavelength solution # for a given line in the image wavemap = np.zeros_like(hdu[i].data) for j in range(len(hdu[i].data)): # find the wavelength solution for the data w_arr = sr.findsol(xarr, soldict, j, caltype, nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slit, xbin, ybin, slitid, function, order) if w_arr is not None: wavemap[j, :] = w_arr if array_only: return wavemap # write out the oimg hduwav = fits.ImageHDU(data=wavemap, header=hdu[i].header, name='WAV') hdu.append(hduwav) saltkey.new('WAVEXT', len(hdu) - 1, 'Extension for Wavelength Map', hdu[i]) return hdu
def rectify(hdu, soldict, caltype='line', function='poly', order=3, inttype='interp', w1=None, w2=None, dw=None, nw=None, blank=0, pixscale=0.0, time_interp=False, conserve=False, nearest=False, clobber=True, log=None, verbose=True): """Read in an image and a set of wavlength solutions. Calculate the best wavelength solution for a given dataset and then apply that data set to the image return """ # set the basic values set_w1 = (w1 is None) set_w2 = (w2 is None) set_dw = (dw is None) set_nw = (nw is None) # set up the time of the observation dateobs = saltkey.get('DATE-OBS', hdu[0]) utctime = saltkey.get('TIME-OBS', hdu[0]) exptime = saltkey.get('EXPTIME', hdu[0]) instrume = saltkey.get('INSTRUME', hdu[0]).strip() grating = saltkey.get('GRATING', hdu[0]).strip() if caltype == 'line': grang = saltkey.get('GRTILT', hdu[0]) arang = saltkey.get('CAMANG', hdu[0]) else: grang = saltkey.get('GR-ANGLE', hdu[0]) arang = saltkey.get('AR-ANGLE', hdu[0]) filtername = saltkey.get('FILTER', hdu[0]).strip() slitname = saltkey.get('MASKID', hdu[0]) slit = st.getslitsize(slitname) xbin, ybin = saltkey.ccdbin(hdu[0]) timeobs = enterdatetime('%s %s' % (dateobs, utctime)) # check to see if there is more than one solution if caltype == 'line': if len(soldict) == 1: sol = soldict.keys()[0] slitid = None if not matchobservations( soldict[sol], instrume, grating, grang, arang, filtername, slitid): msg = 'Observations do not match setup for transformation but using the solution anyway' if log: log.warning(msg) for i in range(1, len(hdu)): if hdu[i].name == 'SCI': if log: log.message('Correcting extension %i' % i) istart = int(0.5 * len(hdu[i].data)) # open up the data # set up the xarr and initial wavlength solution xarr = np.arange(len(hdu[i].data[istart]), dtype='int64') # get the slitid try: slitid = saltkey.get('SLITNAME', hdu[i]) except: slitid = None # set up a wavelength solution try: w_arr = findsol(xarr, soldict, istart, caltype, nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slit, xbin, ybin, slitid, function, order) except SALTSpecError as e: if slitid: msg = 'SLITID %s: %s' % (slitid, e) if log: log.warning(msg) continue else: raise SALTSpecError(e) if w_arr is None: w_arr = findsol(xarr, soldict, istart, 'rss', nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slit, xbin, ybin, slitid, function, order) # set up the output x-axis if set_w1: w1 = w_arr.min() if set_w2: w2 = w_arr.max() if set_nw: nw = len(xarr) if set_dw: dw = float(w2 - w1) / nw nw_arr = createoutputxaxis(w1, w2, nw) # setup the VARIANCE and BPM frames if saltkey.found('VAREXT', hdu[i]): varext = saltkey.get('VAREXT', hdu[i]) else: varext = None # setup the BPM frames if saltkey.found('BPMEXT', hdu[i]): bpmext = saltkey.get('BPMEXT', hdu[i]) else: bpmext = None # for each line in the data, determine the wavelength solution # for a given line in the image for j in range(len(hdu[i].data)): # find the wavelength solution for the data w_arr = findsol(xarr, soldict, j, caltype, nearest, timeobs, exptime, instrume, grating, grang, arang, filtername, slit, xbin, ybin, slitid, function, order) # apply that wavelength solution to the data if w_arr is not None: try: hdu[i].data[ j, :] = st.interpolate( nw_arr, w_arr, hdu[i].data[ j, :], inttype, left=blank, right=blank) except Exception as e: hdu[i].data[j, :] = hdu[i].data[j, :] * 0.0 + blank msg = 'In row %i, solution cannot be found due to %s' % ( i, e) # correct the variance frame if varext: try: hdu[varext].data[ j, :] = st.interpolate( nw_arr, w_arr, hdu[varext].data[ j, :], inttype, left=blank, right=blank) except Exception as e: msg = 'In row %i, solution cannot be found due to %s' % ( i, e) # correct the BPM frame if bpmext: try: hdu[bpmext].data[ j, :] = st.interpolate( nw_arr, w_arr, hdu[bpmext].data[ j, :], inttype, left=blank, right=blank) except Exception as e: msg = 'In row %i, solution cannot be found due to %s' % ( i, e) else: hdu[i].data[j, :] = hdu[i].data[j, :] * 0.0 + blank if conserve: hdu[i].data = hdu[i].data / dw if varext: hdu[varext].data = hdu[varext].data / dw # Add WCS information saltkey.new('CTYPE1', 'LAMBDA', 'Coordinate Type', hdu[i]) saltkey.new('CTYPE2', 'PIXEL', 'Coordinate Type', hdu[i]) saltkey.new( 'CD1_1', dw, 'WCS: Wavelength Dispersion in angstrom/pixel', hdu[i]) saltkey.new('CD2_1', 0.0, 'WCS: ', hdu[i]) saltkey.new('CD1_2', 0.0, 'WCS: ', hdu[i]) saltkey.new('CD2_2', ybin * pixscale, 'WCS: ', hdu[i]) saltkey.new('CRPIX1', 0.0, 'WCS: X Reference pixel', hdu[i]) saltkey.new('CRPIX2', 0.0, 'WCS: Y Reference pixel', hdu[i]) saltkey.new('CRVAL1', w1, 'WCS: X Reference pixel', hdu[i]) saltkey.new('CRVAL2', 0.0, 'WCS: Y Reference pixel', hdu[i]) saltkey.new('CDELT1', 1.0, 'WCS: X pixel size', hdu[i]) saltkey.new('CDELT2', 1.0, 'WCS: Y pixel size', hdu[i]) saltkey.new('DC-FLAG', 0, 'Dispesion Corrected image', hdu[i]) return hdu
def specsens(specfile, outfile, stdfile, extfile, airmass=None, exptime=None, stdzp=3.68e-20, function='polynomial', order=3, thresh=3, niter=5, fitter='gaussian', clobber=True, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # read in the specfile and create a spectrum object obs_spectra = st.readspectrum(specfile.strip(), error=True, ftype='ascii') # smooth the observed spectrum # read in the std file and convert from magnitudes to fnu # then convert it to fwave (ergs/s/cm2/A) std_spectra = st.readspectrum(stdfile.strip(), error=False, ftype='ascii') std_spectra.flux = Spectrum.magtoflux(std_spectra.flux, stdzp) std_spectra.flux = Spectrum.fnutofwave(std_spectra.wavelength, std_spectra.flux) # Get the typical bandpass of the standard star, std_bandpass = np.diff(std_spectra.wavelength).mean() # Smooth the observed spectrum to that bandpass obs_spectra.flux = st.boxcar_smooth(obs_spectra, std_bandpass) # read in the extinction file (leave in magnitudes) ext_spectra = st.readspectrum(extfile.strip(), error=False, ftype='ascii') # determine the airmass if not specified if saltio.checkfornone(airmass) is None: message = 'Airmass was not supplied' raise SALTSpecError(message) # determine the exptime if not specified if saltio.checkfornone(exptime) is None: message = 'Exposure Time was not supplied' raise SALTSpecError(message) # calculate the calibrated spectra log.message('Calculating the calibration curve for %s' % specfile) cal_spectra = sensfunc(obs_spectra, std_spectra, ext_spectra, airmass, exptime) # plot(cal_spectra.wavelength, cal_spectra.flux * std_spectra.flux) # fit the spectra--first take a first cut of the spectra # using the median absolute deviation to throw away bad points cmed = np.median(cal_spectra.flux) cmad = saltstat.mad(cal_spectra.flux) mask = (abs(cal_spectra.flux - cmed) < thresh * cmad) mask = np.logical_and(mask, (cal_spectra.flux > 0)) # now fit the data # Fit using a gaussian process. if fitter == 'gaussian': from sklearn.gaussian_process import GaussianProcess #Instanciate a Gaussian Process model dy = obs_spectra.var[mask]**0.5 dy /= obs_spectra.flux[mask] / cal_spectra.flux[mask] y = cal_spectra.flux[mask] gp = GaussianProcess(corr='squared_exponential', theta0=1e-2, thetaL=1e-4, thetaU=0.1, nugget=(dy / y)**2.0) X = np.atleast_2d(cal_spectra.wavelength[mask]).T # Fit to data using Maximum Likelihood Estimation of the parameters gp.fit(X, y) x = np.atleast_2d(cal_spectra.wavelength).T # Make the prediction on the meshed x-axis (ask for MSE as well) y_pred = gp.predict(x) cal_spectra.flux = y_pred else: fit = interfit(cal_spectra.wavelength[mask], cal_spectra.flux[mask], function=function, order=order, thresh=thresh, niter=niter) fit.interfit() cal_spectra.flux = fit(cal_spectra.wavelength) # write the spectra out st.writespectrum(cal_spectra, outfile, ftype='ascii')
def specslitnormalize(images, outimages, outpref, response=None, response_output=None, order=2, conv=1e-2, niter=20, startext=0, clobber=False, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # Check the input images infiles = saltio.argunpack('Input', images) # create list of output files outfiles = saltio.listparse('Outfile', outimages, outpref, infiles, '') # read in the response function response = saltio.checkfornone(response) if response: log.message('Loading response from %s' % response) response = readresponse(response) # Identify the lines in each file for img, ofile in zip(infiles, outfiles): # open the image hdu = saltio.openfits(img) for i in range(startext, len(hdu)): if hdu[i].name == 'SCI': log.message('Normalizing extension %i in %s' % (i, img)) # things that will change for each slit # set up the data for the source try: data = hdu[i].data except Exception as e: message = \ 'Unable to read in data array in %s because %s' % \ (img, e) raise SALTSpecError(message) if response is None: response = create_response(data, spatial_axis=1, order=order, conv=conv, niter=niter) if response_output: write_response(response, clobber=clobber) else: # add a check that the response is the same shape as # the data if len(response) != data.shape[0]: raise SALTSpecError( 'Length of response function does not equal size of image array' ) # correct the data data = data / response # correct the variance frame if saltkey.found('VAREXT', hdu[i]): vhdu = saltkey.get('VAREXT', hdu[i]) hdu[vhdu].data = hdu[vhdu].data / response saltio.writefits(hdu, ofile, clobber=clobber)
def specidentify(images, linelist, outfile, guesstype='rss', guessfile='', automethod='Matchlines', function='poly', order=3, rstep=100, rstart='middlerow', mdiff=5, thresh=3, niter=5, smooth=0, subback=0, inter=True, startext=0, clobber=False, textcolor='black', preprocess=False, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # set up the variables infiles = [] outfiles = [] # Check the input images infiles = saltio.argunpack('Input', images) # create list of output files outfiles = saltio.argunpack('Output', outfile) # open the line lists slines, sfluxes = st.readlinelist(linelist) # Identify the lines in each file for img, ofile in zip(infiles, outfiles): # open the image hdu = saltio.openfits(img) # get the basic information about the spectrograph dateobs = saltkey.get('DATE-OBS', hdu[0]) try: utctime = saltkey.get('UTC-OBS', hdu[0]) except SaltError: utctime = saltkey.get('TIME-OBS', hdu[0]) instrume = saltkey.get('INSTRUME', hdu[0]).strip() grating = saltkey.get('GRATING', hdu[0]).strip() grang = saltkey.get('GR-ANGLE', hdu[0]) grasteps = saltkey.get('GRTILT', hdu[0]) arang = saltkey.get('AR-ANGLE', hdu[0]) arsteps = saltkey.get('CAMANG', hdu[0]) rssfilter = saltkey.get('FILTER', hdu[0]) specmode = saltkey.get('OBSMODE', hdu[0]) masktype = saltkey.get('MASKTYP', hdu[0]).strip().upper() slitname = saltkey.get('MASKID', hdu[0]) xbin, ybin = saltkey.ccdbin(hdu[0], img) for i in range(startext, len(hdu)): if hdu[i].name == 'SCI': log.message('Proccessing extension %i in %s' % (i, img)) # things that will change for each slit if masktype == 'LONGSLIT': slit = st.getslitsize(slitname) xpos = -0.2666 ypos = 0.0117 objid = None elif masktype == 'MOS': slit = 1. #slit=saltkey.get('SLIT', hdu[i]) # set up the x and y positions miny = hdu[i].header['MINY'] maxy = hdu[i].header['MAXY'] ras = hdu[i].header['SLIT_RA'] des = hdu[i].header['SLIT_DEC'] objid = hdu[i].header['SLITNAME'] # TODO: Check the perfomance of masks at different PA rac = hdu[0].header['MASK_RA'] dec = hdu[0].header['MASK_DEC'] pac = hdu[0].header['PA'] # these are hard wired at the moment xpixscale = 0.1267 * xbin ypixscale = 0.1267 * ybin cx = int(3162 / xbin) cy = int(2050 / ybin) x, y = mt.convert_fromsky(ras, des, rac, dec, xpixscale=xpixscale, ypixscale=ypixscale, position_angle=-pac, ccd_cx=cx, ccd_cy=cy) xpos = 0.015 * 2 * (cx - x[0]) ypos = 0.0117 else: msg = '%s is not a currently supported masktype' % masktype raise SALTSpecError(msg) if instrume not in ['PFIS', 'RSS']: msg = '%s is not a currently supported instrument' % instrume raise SALTSpecError(msg) # create RSS Model rss = RSSModel.RSSModel(grating_name=grating.strip(), gratang=grang, camang=arang, slit=slit, xbin=xbin, ybin=ybin, xpos=xpos, ypos=ypos) res = 1e7 * rss.calc_resolelement(rss.alpha(), -rss.beta()) dres = res / 10.0 wcen = 1e7 * rss.calc_centralwavelength() R = rss.calc_resolution(wcen / 1e7, rss.alpha(), -rss.beta()) logmsg = '\nGrating\tGR-ANGLE\tAR-ANGLE\tSlit\tWCEN\tR\n' logmsg += '%s\t%8.3f\t%8.3f\t%4.2f\t%6.2f\t%4f\n' % ( grating, grang, arang, slit, wcen, R) if log: log.message(logmsg, with_header=False) # set up the data for the source try: data = hdu[i].data except Exception, e: message = 'Unable to read in data array in %s because %s' % ( img, e) raise SALTSpecError(message) # set up the center row if rstart == 'middlerow': ystart = int(0.5 * len(data)) else: ystart = int(rstart) rss.gamma = 0.0 if masktype == 'MOS': rss.gamma = 180.0 / math.pi * math.atan( (y * rss.detector.pix_size * rss.detector.ybin - 0.5 * rss.detector.find_height()) / rss.camera.focallength) # set up the xarr array based on the image xarr = np.arange(len(data[ystart]), dtype='int64') # get the guess for the wavelength solution if guesstype == 'rss': # set up the rss model ws = st.useRSSModel(xarr, rss, function=function, order=order, gamma=rss.gamma) if function in ['legendre', 'chebyshev']: ws.func.func.domain = [xarr.min(), xarr.max()] elif guesstype == 'file': soldict = {} soldict = readsolascii(guessfile, soldict) timeobs = enterdatetime('%s %s' % (dateobs, utctime)) exptime = saltkey.get('EXPTIME', hdu[0]) filtername = saltkey.get('FILTER', hdu[0]).strip() try: slitid = saltkey.get('SLITNAME', hdu[i]) except: slitid = None function, order, coef, domain = findlinesol(soldict, ystart, True, timeobs, exptime, instrume, grating, grang, arang, filtername, slitid, xarr=xarr) ws = WavelengthSolution.WavelengthSolution( xarr, xarr, function=function, order=order) ws.func.func.domain = domain ws.set_coef(coef) else: raise SALTSpecError( 'This guesstype is not currently supported') # identify the spectral lines ImageSolution = identify(data, slines, sfluxes, xarr, ystart, ws=ws, function=function, order=order, rstep=rstep, mdiff=mdiff, thresh=thresh, niter=niter, method=automethod, res=res, dres=dres, smooth=smooth, inter=inter, filename=img, subback=0, textcolor=textcolor, preprocess=preprocess, log=log, verbose=verbose) if outfile and len(ImageSolution): writeIS(ImageSolution, outfile, dateobs=dateobs, utctime=utctime, instrume=instrume, grating=grating, grang=grang, grasteps=grasteps, arsteps=arsteps, arang=arang, rfilter=rssfilter, slit=slit, xbin=xbin, ybin=ybin, objid=objid, filename=img, log=log, verbose=verbose)
def prepare(struct, badpixelstruct): """Prepare a structure for spectroscopic processing """ # identify instrument infile = struct._HDUList__file.name nextend = len(struct) if badpixelstruct is not None: bpfile = badpixelstruct._HDUList__file.name # Check that the image is the same number of extensions as the # badpixelmap if len(struct) != len(badpixelstruct): message = '%s and %s are not the same length' % (infile, bpfile) raise SALTSpecError(message) # Add variance frames and bad pixel maps # If no variance frame exists, add the file at the end of the # extensions. If a variance file does exist, do nothing # Add a keyword to the Science extension indication what # extension the variance from is on j = 0 for i in range(nextend): if struct[i].size() > 0 and struct[i].name == 'SCI': # Check to see if the variance frame exists try: key = struct[i].header['VAREXT'] except: try: hdu = createvariance(struct[i], i, nextend + j) struct[i].header.update( 'VAREXT', nextend + j, comment='Extension for Variance Frame') struct.append(hdu) j = j + 1 except Exception as e: message = 'Could not create variance extension in ext %i of %s because %s' \ % (i, infile, e) raise SALTSpecError(message) # Check to see if the BPM frame exists try: key = struct[1].header['BPMEXT'] except: try: hdu = createbadpixel( struct, badpixelstruct, i, nextend + j) except Exception as e: message = 'Could not create bad pixel extension in ext %i of %s because %s' \ % (i, infile, e) raise SALTSpecError(message) if (1): struct[i].header.update( 'BPMEXT', nextend + j, comment='Extension for Bad Pixel Mask') struct.append(hdu) j = j + 1 return struct
def specslit(image, outimage, outpref, exttype='auto', slitfile='', outputslitfile='', regprefix='', sections=3, width=25, sigma=2.2, thres=6, order=3, padding=5, yoffset=0, inter=False, clobber=True, logfile='salt.log', verbose=True): with logging(logfile, debug) as log: # check all the input and make sure that all the input needed is provided # by the user # read the image or image list and check if each in the list exist infiles = saltio.argunpack('Input', image) # unpack the outfiles outfiles = saltio.listparse('Outimages', outimage, outpref, infiles, '') # from the extraction type, check whether the input file is specified. # if the slitfile parameter is specified then use the slit files for # the extraction. if the extraction type is auto then use image for the # detection and the slit extraction if exttype == 'rsmt' or exttype == 'fits' or exttype == 'ascii' or exttype == 'ds9': slitfiles = saltio.argunpack('Slitfile', slitfile) if len(slitfiles) == 1: slitfiles = slitfiles * len(infiles) saltio.comparelists(infiles, slitfiles, 'image', 'slitfile') elif exttype == 'auto': slitfiles = infiles log.message( 'Extraction type is AUTO. Slit detection will be done from image' ) # read in if an optional ascii file is requested if len(outputslitfile) > 0: outslitfiles = saltio.argunpack('Outslitfiles', outputslitfile) saltio.comparelists(infiles, outslitfiles, 'image', 'outputslitfile') else: outslitfiles = [''] * len(infiles) # check if the width and sigma parameters were specified. # default is 25 and 2.2 if width < 10.: msg = 'The width parameter needs be a value larger than 10' raise SALTSpecError(msg) if sigma < 0.0: msg = 'Sigma must be greater than zero' raise SaltSpecError(msg) # check the treshold parameter. this needs to be specified by the user if thres <= 0.0: msg = 'Threshold must be greater than zero' raise SaltSpecError(msg) # check to make sure that the sections are greater than the order if sections <= order: msg = 'Number of sections must be greater than the order for the spline fit' raise SaltSpecError(msg) # run through each of the images and extract the slits for img, oimg, sfile, oslit in zip(infiles, outfiles, slitfiles, outslitfiles): log.message('Proccessing image %s' % img) # open the image struct = saltio.openfits(img) ylen, xlen = struct[1].data.shape xbin, ybin = saltkey.ccdbin(struct[0], img) # setup the VARIANCE and BPM frames if saltkey.found('VAREXT', struct[1]): varext = saltkey.get('VAREXT', struct[1]) varlist = [] else: varext = None # setup the BPM frames if saltkey.found('BPMEXT', struct[1]): bpmext = saltkey.get('BPMEXT', struct[1]) bpmlist = [] else: bpmext = None # open the slit definition file or identify the slits in the image slitmask = None ycheck = False if exttype == 'rsmt': log.message('Using slits from %s' % sfile) if yoffset is None: yoffset = 0 ycheck = True slitmask = mt.read_slitmask_from_xml(sfile) xpos = -0.3066 ypos = 0.0117 cx = int(xlen / 2.0) cy = int(ylen / 2.0) + ypos / 0.015 / ybin + yoffset order, slit_positions = mt.convert_slits_from_mask( slitmask, order=1, xbin=xbin, ybin=ybin, pix_scale=0.1267, cx=cx, cy=cy) sections = 1 elif exttype == 'fits': log.message('Using slits from %s' % sfile) order, slit_positions = read_slits_from_fits(sfile) elif exttype == 'ascii': log.message('Using slits from %s' % sfile) order, slit_positions = mt.read_slits_from_ascii(sfile) elif exttype == 'ds9': log.message('Using slits from %s' % sfile) order, slit_positions, slitmask = mt.read_slits_from_ds9( sfile, order=order) slitmask = None sections = 1 elif exttype == 'auto': log.message('Identifying slits in %s' % img) # identify the slits in the image order, slit_positions = identify_slits(struct[1].data, order, sections, width, sigma, thres) # write out the slit identifications if ofile has been supplied if oslit: log.message('Writing slit positions to %s' % oslit) mt.write_outputslitfile(slit_positions, oslit, order) if ycheck: slit_positions, dy = check_ypos(slit_positions, struct[1].data) log.message('Using an offset of {}'.format(dy)) # extract the slits spline_x = mt.divide_image(struct[1].data, sections) spline_x = 0.5 * (np.array(spline_x[:-1]) + np.array(spline_x[1:])) extracted_spectra, spline_positions = mt.extract_slits( slit_positions, spline_x, struct[1].data, order=order, padding=padding) if varext: extracted_var, var_positions = mt.extract_slits( slit_positions, spline_x, struct[varext].data, order=order, padding=padding) if bpmext: extracted_bpm, bpm_positions = mt.extract_slits( slit_positions, spline_x, struct[bpmext].data, order=order, padding=padding) # write out the data to the new array # create the new file hdulist = fits.HDUList([struct[0]]) # log the extracted spectra if needed log.message('', with_stdout=verbose) # setup output ds9 file if regprefix: regout = open( regprefix + os.path.basename(img).strip('.fits') + '.reg', 'w') regout.write('# Region file format: DS9 version 4.1\n') regout.write('# Filename: %s\n' % img) regout.write( 'global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1\nphysical\n' ) # add each imglist = [] nslits = len(spline_positions) for i in range(nslits): y1 = spline_positions[i][0].min() y2 = spline_positions[i][1].max() msg = 'Extracted Spectra %i between %i to %i' % (i + 1, y1, y2) # log.message(msg, with_header=False, with_stdout=verbose) sdu = fits.ImageHDU(extracted_spectra[i], header=struct[1].header) if varext: vdu = fits.ImageHDU(extracted_var[i], header=struct[varext].header) sdu.header['VAREXT'] = i + nslits + 1 varlist.append(vdu) if bpmext: bdu = fits.ImageHDU(extracted_bpm[i], header=struct[bpmext].header) sdu.header['BPMEXT'] = i + 2 * nslits + 1 bpmlist.append(bdu) imglist.append(sdu) # add in some additional keywords imglist[i].header['MINY'] = (y1, 'Lower Y value in original image') imglist[i].header['MAXY'] = (y2, 'Upper Y value in original image') if regprefix: xsize = struct[1].data.shape[1] xsize = int(0.5 * xsize) rtext = '' if slitmask: # rtext='%s, %8.7f, %8.7f, %3.2f' % (slitmask.slitlets.data[i]['name'], slitmask.slitlets.data[i]['targ_ra'], slitmask.slitlets.data[i]['targ_dec'], slitmask.slitlets.data[i]['slit_width']) pass regout.write('box(%i,%i, %i, %i) #text={%s}\n' % (xsize, 0.5 * (y1 + y2), 2 * xsize, y2 - y1, rtext)) # add slit information if slitmask: imglist[i].header['SLITNAME'] = ( slitmask.slitlets.data[i]['name'], 'Slit Name') imglist[i].header['SLIT_RA'] = ( slitmask.slitlets.data[i]['targ_ra'], 'Slit RA') imglist[i].header['SLIT_DEC'] = ( slitmask.slitlets.data[i]['targ_dec'], 'Slit DEC') imglist[i].header['SLIT'] = ( slitmask.slitlets.data[i]['slit_width'], 'Slit Width') # add to the hdulist hdulist += imglist if varext: hdulist += varlist if bpmext: hdulist += bpmlist # write the slit positions to the header # create the binary table HDU that contains the split positions tbhdu = mt.slits_HDUtable(slit_positions, order) bintable_hdr = tbhdu.header # add the extname parameter to the extension tbhdu.header['EXTNAME'] = 'BINTABLE' # add the extname parameter to the extension hdulist[0].header['SLITEXT'] = len(hdulist) hdulist.append(tbhdu) # add addition header information about the mask if slitmask: hdulist[0].header['MASKNAME'] = (slitmask.mask_name, 'SlitMask Name') hdulist[0].header['MASK_RA'] = (slitmask.center_ra, 'SlitMask RA') hdulist[0].header['MASK_DEC'] = (slitmask.center_dec, 'SlitMask DEC') hdulist[0].header['MASK_PA'] = (slitmask.position_angle, 'SlitMask Position Angle') # write out the image saltio.writefits(hdulist, oimg, clobber)