def isEmpty(self, file): if not os.path.exists(file): return True fname = IO.list_file_to_strings(file) if len(fname): return False else: return True
def isEmpty(self,file): if not os.path.exists(file): return True fname = IO.list_file_to_strings(file) if len(fname): return False else: return True
def printMaskAndBand(self): offsetfile = self.offsetFiles[0] fname = IO.list_file_to_strings(offsetfile) if os.path.isabs(fname[0]): path = fname[0] else: path = os.path.join(fname_to_path(fname[0]), fname[0]) hdulist = pf.open(path) header = hdulist[0].header self.maskName = header['maskname'] self.band = header['filter'] self.addLine("maskname = '"+str(self.maskName)+"'") self.addLine("band = '"+str(self.band)+"'") self.addLine("")
def printMaskAndBand(self): offsetfile = self.offsetFiles[0] fname = IO.list_file_to_strings(offsetfile) if os.path.isabs(fname[0]): path = fname[0] else: path = os.path.join(fname_to_path(fname[0]), fname[0]) hdulist = pf.open(path) header = hdulist[0].header self.maskName = header['maskname'] self.band = header['filter'] self.addLine("maskname = '" + str(self.maskName) + "'") self.addLine("band = '" + str(self.band) + "'") self.addLine("")
def handle_flats(flatlist, maskname, band, options, extension=None, edgeThreshold=450, lampOffList=None, longslit=None): ''' handle_flats is the primary entry point to the Flats module. handle_flats takes a list of individual exposure FITS files and creates: 1. A CRR, dark subtracted, pixel-response flat file. 2. A set of polynomials that mark the edges of a slit Inputs: flatlist: maskname: The name of a mask band: A string indicating the bandceil Outputs: file {maskname}/flat_2d_{band}.fits -- pixel response flat file {maskname}/edges.np ''' tick = time.time() # Check bpos = np.ones(92) * -1 #Retrieve the list of files to use for flat creation. flatlist = IO.list_file_to_strings(flatlist) # Print the filenames to Standard-out for flat in flatlist: info(str(flat)) #Determine if flat files headers are in agreement for fname in flatlist: hdr, dat, bs = IO.readmosfits(fname, options, extension=extension) try: bs0 except: bs0 = bs if np.any(bs0.pos != bs.pos): print "bs0: " + str(bs0.pos) + " bs: " + str(bs.pos) error("Barset do not seem to match") raise Exception("Barsets do not seem to match") if hdr["filter"] != band: error("Filter name %s does not match header filter name " "%s in file %s" % (band, hdr["filter"], fname)) raise Exception("Filter name %s does not match header filter name " "%s in file %s" % (band, hdr["filter"], fname)) for i in xrange(len(bpos)): b = hdr["B{0:02d}POS".format(i + 1)] if bpos[i] == -1: bpos[i] = b else: if bpos[i] != b: error("Bar positions are not all the same in " "this set of flat files") raise Exception("Bar positions are not all the same in " "this set of flat files") bs = bs0 # Imcombine the lamps ON flats info("Attempting to combine previous files") combine(flatlist, maskname, band, options) # Imcombine the lamps OFF flats and subtract the off from the On sets if lampOffList != None: #Retrieve the list of files to use for flat creation. lampOffList = IO.list_file_to_strings(lampOffList) # Print the filenames to Standard-out for flat in lampOffList: info(str(flat)) print "Attempting to combine Lamps off data" combine(lampOffList, maskname, band, options, lampsOff=True) combine_off_on(maskname, band, options) debug("Combined '%s' to '%s'" % (flatlist, maskname)) info("Comgined to '%s'" % (maskname)) path = "combflat_2d_%s.fits" % band (header, data) = IO.readfits(path, use_bpm=True) info("Flat written to %s" % path) # Edge Trace if bs.long_slit: info("Long slit mode recognized") info("Central row position: " + str(longslit["row_position"])) info("Upper and lower limits: " + str(longslit["yrange"][0]) + " " + str(longslit["yrange"][1])) results = find_longslit_edges(data, header, bs, options, edgeThreshold=edgeThreshold, longslit=longslit) elif bs.long2pos_slit: info("Long2pos mode recognized") results = find_long2pos_edges(data, header, bs, options, edgeThreshold=edgeThreshold, longslit=longslit) else: results = find_and_fit_edges(data, header, bs, options, edgeThreshold=edgeThreshold) results[-1]["maskname"] = maskname results[-1]["band"] = band np.save("slit-edges_{0}".format(band), results) save_ds9_edges(results, options) # Generate Flat out = "pixelflat_2d_%s.fits" % (band) if lampOffList != None: make_pixel_flat(data, results, options, out, flatlist, lampsOff=True) else: make_pixel_flat(data, results, options, out, flatlist, lampsOff=False) info("Pixel flat took {0:6.4} s".format(time.time() - tick))
def handle_flats(flatlist, maskname, band, options, extension=None,edgeThreshold=450,lampOffList=None,longslit=None): ''' handle_flats is the primary entry point to the Flats module. handle_flats takes a list of individual exposure FITS files and creates: 1. A CRR, dark subtracted, pixel-response flat file. 2. A set of polynomials that mark the edges of a slit Inputs: flatlist: maskname: The name of a mask band: A string indicating the bandceil Outputs: file {maskname}/flat_2d_{band}.fits -- pixel response flat file {maskname}/edges.np ''' tick = time.time() # Check bpos = np.ones(92) * -1 #Retrieve the list of files to use for flat creation. flatlist = IO.list_file_to_strings(flatlist) # Print the filenames to Standard-out for flat in flatlist: info(str(flat)) #Determine if flat files headers are in agreement for fname in flatlist: hdr, dat, bs = IO.readmosfits(fname, options, extension=extension) try: bs0 except: bs0 = bs if np.any(bs0.pos != bs.pos): print "bs0: "+str(bs0.pos)+" bs: "+str(bs.pos) error("Barset do not seem to match") raise Exception("Barsets do not seem to match") if hdr["filter"] != band: error ("Filter name %s does not match header filter name " "%s in file %s" % (band, hdr["filter"], fname)) raise Exception("Filter name %s does not match header filter name " "%s in file %s" % (band, hdr["filter"], fname)) for i in xrange(len(bpos)): b = hdr["B{0:02d}POS".format(i+1)] if bpos[i] == -1: bpos[i] = b else: if bpos[i] != b: error("Bar positions are not all the same in " "this set of flat files") raise Exception("Bar positions are not all the same in " "this set of flat files") bs = bs0 # Imcombine the lamps ON flats info("Attempting to combine previous files") combine(flatlist, maskname, band, options) # Imcombine the lamps OFF flats and subtract the off from the On sets if lampOffList != None: #Retrieve the list of files to use for flat creation. lampOffList = IO.list_file_to_strings(lampOffList) # Print the filenames to Standard-out for flat in lampOffList: info(str(flat)) print "Attempting to combine Lamps off data" combine(lampOffList, maskname, band, options, lampsOff=True) combine_off_on( maskname, band, options) debug("Combined '%s' to '%s'" % (flatlist, maskname)) info("Comgined to '%s'" % (maskname)) path = "combflat_2d_%s.fits" % band (header, data) = IO.readfits(path, use_bpm=True) info("Flat written to %s" % path) # Edge Trace if bs.long_slit: info( "Long slit mode recognized") info( "Central row position: "+str(longslit["row_position"])) info( "Upper and lower limits: "+str(longslit["yrange"][0])+" "+str(longslit["yrange"][1])) results = find_longslit_edges(data,header, bs, options, edgeThreshold=edgeThreshold, longslit=longslit) elif bs.long2pos_slit: info( "Long2pos mode recognized") results = find_long2pos_edges(data,header, bs, options, edgeThreshold=edgeThreshold, longslit=longslit) else: results = find_and_fit_edges(data, header, bs, options,edgeThreshold=edgeThreshold) results[-1]["maskname"] = maskname results[-1]["band"] = band np.save("slit-edges_{0}".format(band), results) save_ds9_edges(results, options) # Generate Flat out = "pixelflat_2d_%s.fits" % (band) if lampOffList != None: make_pixel_flat(data, results, options, out, flatlist, lampsOff=True) else: make_pixel_flat(data, results, options, out, flatlist, lampsOff=False) info( "Pixel flat took {0:6.4} s".format(time.time()-tick))
def handle_background(filelist, wavename, maskname, band_name, options, shifts=None, plan=None, extension=None): ''' Perform difference imaging and subtract residual background. The plan looks something like: [['A', 'B']] In this case, the number of output files is equal to the length of the list (1). If you choose to use an ABA'B' pattern then the plan will be: [["A", "B"], ["A'", "B'"]] the background subtraction code will make and handle two files, "A-B" and "A'-B'". ''' global header, bs, edges, data, Var, itime, lam, sky_sub_out, sky_model_out, band band = band_name flatname = "pixelflat_2d_%s.fits" % band_name hdr, flat = IO.readfits("pixelflat_2d_%s.fits" % (band_name), options) if np.abs(np.median(flat) - 1) > 0.1: raise Exception("Flat seems poorly behaved.") ''' This next section of the code figures out the observing plan and then deals with the bookeeping of sending the plan to the background subtracter. ''' hdrs = [] epss = {} vars = {} bss = [] times = {} Nframes = [] i = 0 header = pf.Header() for i in xrange(len(filelist)): fl = filelist[i] files = IO.list_file_to_strings(fl) print "Combining" if shifts is None: shift = None else: shift = shifts[i] hdr, electron, var, bs, time, Nframe = imcombine(files, maskname, options, flat, outname="%s.fits" % (fl), shifts=shift, extension=extension) hdrs.append(hdr) header = merge_headers(header, hdr) epss[hdr['FRAMEID']] = electron/time vars[hdr['FRAMEID']] = var times[hdr['FRAMEID']] = time bss.append(bs) Nframes.append(Nframe) positions = {} i = 0 for h in hdrs: positions[h['FRAMEID']] = i i += 1 posnames = set(positions.keys()) if plan is None: plan = guess_plan_from_positions(posnames) num_outputs = len(plan) edges, meta = IO.load_edges(maskname, band, options) lam = IO.readfits(wavename, options) bs = bss[0] for i in xrange(num_outputs): posname0 = plan[i][0] posname1 = plan[i][1] print "Handling %s - %s" % (posname0, posname1) data = epss[posname0] - epss[posname1] Var = vars[posname0] + vars[posname1] itime = np.mean([times[posname0], times[posname1]], axis=0) p = Pool() solutions = p.map(background_subtract_helper, xrange(len(bs.ssl))) p.close() write_outputs(solutions, itime, header, maskname, band, plan[i], options)
def go(maskname, band, filenames, wavefile, wavoptions, longoptions, use_flat=False): ''' The go command is the main entry point into this module. Inputs: maskname: String of the mask name band: String of 'Y', 'J', 'H', or 'K' filenames: List of filenames to reduce wavefile: String of path to FITS file with the wavelength solution wavoptions: The Wavelength Options dictionary longoptions: Dictionary containing: {'yrange': The pixel range to extract over 'row_position': The row to solve the initial wavelength solution on} use_flat: Boolean False [default] means to use no flat field Boolean True means to divide by the pixelflat ''' wavename = Wavelength.filelist_to_wavename(filenames, band, maskname, wavoptions).rstrip(".fits") print "Wavefile: {0}".format(wavefile) lamhdr, lamdat = IO.readfits(wavefile) positions = [] objname = None for listfile in filenames: fnames = IO.list_file_to_strings(listfile) if len(fnames) != 1: raise Exception("I currently expect only one file per position. Remove multiple entries and try again") header, data, bs = IO.readmosfits(fnames[0], wavoptions) if objname is None: objname = header["object"] if objname != header["object"]: print ("Trying to combine longslit stack of object {0} " "with object {1}".format(objname, header["object"])) print("{0:18s} {1:30s} {2:2s} {3:4.1f}".format(file, header["object"], header["frameid"], header["yoffset"])) positions.append([fnames[0], header, data, bs]) print("{0:2g} nod positions found. Producing stacked difference" \ " image.".format(len(positions))) for i in xrange(len(positions)-1): A = positions[i] B = positions[i+1] print("----------- -----".format(A,B)) dname, varname = imdiff(A, B, maskname, band, header, wavoptions) if use_flat: apply_flat(dname, maskname, band) apply_flat(varname, maskname, band) rectify(dname, lamdat, A, B, maskname, band, wavoptions, longoptions) rectify(varname, lamdat, A, B, maskname, band, wavoptions, longoptions) print dname dname, vname = imdiff(B, A, maskname, band, header, wavoptions) if use_flat: apply_flat(dname, maskname, band) apply_flat(vname, maskname, band) rectify(dname, lamdat, B, A, maskname, band, wavoptions, longoptions) rectify(vname, lamdat, B, A, maskname, band, wavoptions, longoptions) if False: fname = os.path.join(path, wavename + ".fits") B = IO.readfits(fname) B = [fname, B[0], B[1]] for i in xrange(len(positions)): A = positions[i] imdiff(A, B, maskname, band, wavoptions) rectify(path, dname, lamdat, A, B, maskname, band, wavoptions, longoptions) imdiff(B, A, maskname, band, wavoptions) rectify(path, dname, lamdat, B, A, maskname, band, wavoptions, longoptions)
def printWavelengthFit(self): if self.type is 'longslit' or self.type is 'long2pos': addLongSlit = ",longslit=longslit" else: addLongSlit = "" if self.type is 'slitmask' or self.type is 'longslit': self.useNeon = False self.useArgon = False # determine is Argon and Neon files contain data for K bands if self.isEmpty('Ar.txt') is False and self.band is 'K': self.useArgon = True if self.isEmpty('Ne.txt') is False and self.band is 'K': self.useNeon = True self.addLine( "Wavelength.imcombine(obsfiles, maskname, band, waveops)") if self.useArgon: self.addLine( "Wavelength.imcombine('Ar.txt', maskname, band, waveops)") if self.useNeon: self.addLine( "Wavelength.imcombine('Ne.txt', maskname, band, waveops)") self.addLine( "Wavelength.fit_lambda_interactively(maskname, band, obsfiles,waveops" + addLongSlit + ", noninteractive=noninteractiveflag)") if self.useArgon: self.addLine( "Wavelength.apply_interactive(maskname, band, waveops, apply=obsfiles, to='Ar.txt', argon=True)" ) if self.useNeon: self.addLine( "Wavelength.apply_interactive(maskname, band, waveops, apply=obsfiles, to='Ne.txt', neon=True)" ) self.addLine( "Wavelength.fit_lambda(maskname, band, obsfiles, obsfiles,waveops" + addLongSlit + ")") if self.useArgon and self.useNeon: self.addLine( "Wavelength.fit_lambda(maskname, band, 'Ne.txt', 'Ne.txt',waveops, wavenames2='Ar.txt'" + addLongSlit + ")") if self.useArgon and not self.useNeon: self.addLine( "Wavelength.fit_lambda(maskname, band, 'Ar.txt', 'Ar.txt',waveops" + addLongSlit + ")") if self.useNeon and not self.useArgon: self.addLine( "Wavelength.fit_lambda(maskname, band, 'Ne.txt', 'Ne.txt',waveops" + addLongSlit + ")") if self.useNeon or self.useArgon: self.addLine("LROI = [[21000,22800]]*1") if self.useNeon: self.addLine( "LROIs = Wavelength.check_wavelength_roi(maskname, band, obsfiles, 'Ne.txt', LROI, waveops)" ) if self.useArgon and not self.useNeon: self.addLine( "LROIs = Wavelength.check_wavelength_roi(maskname, band, obsfiles, 'Ar.txt', LROI, waveops)" ) self.addLine( "Wavelength.apply_lambda_simple(maskname, band, obsfiles, waveops" + addLongSlit + ")") if self.useArgon and self.useNeon: self.addLine( "Wavelength.apply_lambda_sky_and_arc(maskname, band, obsfiles, 'Ne.txt', LROIs, waveops)" ) if self.useArgon and not self.useNeon: self.addLine( "Wavelength.apply_lambda_sky_and_arc(maskname, band, obsfiles, 'Ar.txt', LROIs, waveops)" ) if self.useNeon and not self.useArgon: self.addLine( "Wavelength.apply_lambda_sky_and_arc(maskname, band, obsfiles, 'Ne.txt', LROIs, waveops)" ) # determine waveleng name files = IO.list_file_to_strings(self.offsetFiles) if self.useNeon: neon_files = IO.list_file_to_strings('Ne.txt') self.waveName = "merged_lambda_solution_" + str( Wavelength.filelist_to_wavename( files, self.band, self.maskName, "")).rstrip(".fits") + "_and_" + str( Wavelength.filelist_to_wavename( neon_files, self.band, self.maskName, "")) elif self.useArgon and not self.useNeon: argon_files = IO.list_file_to_strings('Ar.txt') self.waveName = "merged_lambda_solution_" + str( Wavelength.filelist_to_wavename( files, self.band, self.maskName, "")).rstrip(".fits") + "_and_" + str( Wavelength.filelist_to_wavename( argon_files, self.band, self.maskName, "")) else: self.waveName = "lambda_solution_" + str( Wavelength.filelist_to_wavename(files, self.band, self.maskName, "")) if self.type is 'long2pos' or self.type is 'long2pos_specphot': calibWith = "" if self.isEmpty('Ar.txt') is False: self.addLine("argon = ['Ar.txt']") calibWith = "argon" waveFiles = IO.list_file_to_strings('Ar.txt') if self.isEmpty('Ne.txt') is False: self.addLine("neon = ['Ne.txt']") calibWith = "neon" waveFiles = IO.list_file_to_strings('Ne.txt') if calibWith: # we have either Argon, or Neon, or both, so we can use arcs for the reduction self.addLine("Wavelength.imcombine(" + str(calibWith) + ", maskname, band, waveops)") self.addLine( "Wavelength.fit_lambda_interactively(maskname, band, " + str(calibWith) + ",waveops,longslit=longslit, " + str(calibWith) + "=True, noninteractive=noninteractiveflag)") self.addLine("Wavelength.fit_lambda(maskname, band, " + str(calibWith) + "," + str(calibWith) + ",waveops,longslit=longslit)") self.addLine( "Wavelength.apply_lambda_simple(maskname, band, " + str(calibWith) + ", waveops, longslit=longslit, smooth=True)") self.waveName = "lambda_solution_" + str( Wavelength.filelist_to_wavename(waveFiles, self.band, self.maskName, "")) else: # we have no arcs. For the time being, we can try with sky lines but this only works with long2pos specphot print "#####################################################################################################" print "WARNING: There are no arc calibration files" print " The pipeline will try to use sky lines but this only works if the observation is long enough" print " and if you are only using long2pos. It will NOT work on long2pos_specphot" print " Please contact the MosfireDRP team to obtain a standard wavelength solution" print "#####################################################################################################" self.addLine( "obsfiles = obsfiles_posAnarrow + obsfiles_posCnarrow") self.addLine( "Wavelength.imcombine(obsfiles, maskname, band, waveops)") self.addLine( "Wavelength.fit_lambda_interactively(maskname, band, obsfiles ,waveops,longslit=longslit, noninteractive=noninteractiveflag)" ) self.addLine( "Wavelength.fit_lambda(maskname, band, obsfiles,obsfiles ,waveops,longslit=longslit)" ) self.addLine( "Wavelength.apply_lambda_simple(maskname, band, obsfiles, waveops, longslit=longslit, smooth=True)" ) files = IO.list_file_to_strings(self.offsetFiles) self.waveName = "lambda_solution_" + str( Wavelength.filelist_to_wavename(files, self.band, self.maskName, "")) self.addLine("") self.addLine("Wavelength_file = '" + str(self.waveName) + "'") self.addLine("")
def handle_background(filelist, wavename, maskname, band_name, options, shifts=None, plan=None, extension=None): ''' Perform difference imaging and subtract residual background. The plan looks something like: [['A', 'B']] In this case, the number of output files is equal to the length of the list (1). If you choose to use an ABA'B' pattern then the plan will be: [["A", "B"], ["A'", "B'"]] the background subtraction code will make and handle two files, "A-B" and "A'-B'". ''' global header, bs, edges, data, Var, itime, lam, sky_sub_out, sky_model_out, band band = band_name flatname = "pixelflat_2d_%s.fits" % band_name hdr, flat = IO.readfits("pixelflat_2d_%s.fits" % (band_name), options) if np.abs(np.median(flat) - 1) > 0.1: raise Exception("Flat seems poorly behaved.") ''' This next section of the code figures out the observing plan and then deals with the bookeeping of sending the plan to the background subtracter. ''' hdrs = [] epss = {} vars = {} bss = [] times = {} Nframes = [] i = 0 header = pf.Header() for i in xrange(len(filelist)): fl = filelist[i] files = IO.list_file_to_strings(fl) print "Combining" if shifts is None: shift = None else: shift = shifts[i] hdr, electron, var, bs, time, Nframe = imcombine(files, maskname, options, flat, outname="%s.fits" % (fl), shifts=shift, extension=extension) hdrs.append(hdr) header = merge_headers(header, hdr) epss[hdr['FRAMEID']] = electron / time vars[hdr['FRAMEID']] = var times[hdr['FRAMEID']] = time bss.append(bs) Nframes.append(Nframe) positions = {} i = 0 for h in hdrs: positions[h['FRAMEID']] = i i += 1 posnames = set(positions.keys()) if plan is None: plan = guess_plan_from_positions(posnames) num_outputs = len(plan) edges, meta = IO.load_edges(maskname, band, options) lam = IO.readfits(wavename, options) bs = bss[0] for i in xrange(num_outputs): posname0 = plan[i][0] posname1 = plan[i][1] print "Handling %s - %s" % (posname0, posname1) data = epss[posname0] - epss[posname1] Var = vars[posname0] + vars[posname1] itime = np.mean([times[posname0], times[posname1]], axis=0) p = Pool() solutions = p.map(background_subtract_helper, xrange(len(bs.ssl))) p.close() write_outputs(solutions, itime, header, maskname, band, plan[i], options)
def handle_rectification(maskname, in_files, wavename, band_pass, files, options, commissioning_shift=3.0, target='default'): '''Handle slit rectification and coaddition. Args: maskname: The mask name string in_files: List of stacked spectra in electron per second. Will look like ['electrons_Offset_1.5.txt.fits', 'electrons_Offset_-1.5.txt.fits'] wavename: path (relative or full) to the wavelength stack file, string band_pass: Band pass name, string barset_file: Path to a mosfire fits file containing the full set of FITS extensions for the barset. It can be any file in the list of science files. Returns: None Writes files: [maskname]_[band]_[object name]_eps.fits -- The rectified, background subtracted, stacked eps spectrum [maskname]_[band]_[object name]_sig.fits -- Rectified, background subtracted, stacked weight spectrum (STD/itime) [maskname]_[band]_[object_name]_itime.fits Rectified, CRR stacked integration time spectrum [maskname]_[band]_[object_name]_snrs.fits Rectified signal to noise spectrum ''' global edges, dats, vars, itimes, shifts, lambdas, band, fidl, all_shifts band = band_pass dlambda = Wavelength.grating_results(band) hpp = Filters.hpp[band] fidl = np.arange(hpp[0], hpp[1], dlambda) lambdas = IO.readfits(wavename, options) if np.any(lambdas[1].data < 0) or np.any(lambdas[1].data > 29000): info("***********WARNING ***********") info("The file {0} may not be a wavelength file.".format(wavename)) info("Check before proceeding.") info("***********WARNING ***********") edges, meta = IO.load_edges(maskname, band, options) shifts = [] posnames = [] postoshift = {} for file in in_files: info(":: "+str(file)) II = IO.read_drpfits(maskname, file, options) off = np.array((II[0]["decoff"], II[0]["raoff"]),dtype=np.float64) if "yoffset" in II[0]: off = -II[0]["yoffset"] else: # Deal with data taken during commissioning if II[0]["frameid"] == 'A': off = 0.0 else: off = commissioning_shift try: off0 except: off0 = off shift = off - off0 shifts.append(shift) posnames.append(II[0]["frameid"]) postoshift[II[0]['frameid']] = shift info("Position {0} shift: {1:2.2f} as".format(off, shift)) # this is to deal with cases in which we want to rectify one single file if len(set(posnames)) is 1: plans = [['A']] else: plans = Background.guess_plan_from_positions(set(posnames)) all_shifts = [] for plan in plans: to_append = [] for pos in plan: to_append.append(postoshift[pos]) all_shifts.append(to_append) # Reverse the elements in all_shifts to deal with an inversion all_shifts.reverse() theBPM = IO.badpixelmask() all_solutions = [] cntr = 0 if target is 'default': outname = maskname else: outname = target for plan in plans: if len(plan) is 1: p0 = 'A' p1 = 'B' else: p0 = plan[0].replace("'", "p") p1 = plan[1].replace("'", "p") suffix = "%s-%s" % (p0,p1) info("Handling plan %s" % suffix) fname = "bsub_{0}_{1}_{2}.fits".format(outname,band,suffix) EPS = IO.read_drpfits(maskname, fname, options) EPS[1] = np.ma.masked_array(EPS[1], theBPM, fill_value=0) fname = "var_{0}_{1}_{2}.fits".format(outname, band, suffix) VAR = IO.read_drpfits(maskname, fname, options) VAR[1] = np.ma.masked_array(VAR[1], theBPM, fill_value=np.inf) fname = "itime_{0}_{1}_{2}.fits".format(outname, band, suffix) ITIME = IO.read_drpfits(maskname, fname, options) ITIME[1] = np.ma.masked_array(ITIME[1], theBPM, fill_value=0) dats = EPS vars = VAR itimes = ITIME EPS[0]["ORIGFILE"] = fname tock = time.time() sols = range(len(edges)-1,-1,-1) shifts = all_shifts[cntr] cntr += 1 p = Pool() solutions = p.map(handle_rectification_helper, sols) p.close() all_solutions.append(solutions) tick = time.time() info("-----> Mask took %i. Writing to disk." % (tick-tock)) output = np.zeros((1, len(fidl))) snrs = np.zeros((1, len(fidl))) sdout= np.zeros((1, len(fidl))) itout= np.zeros((1, len(fidl))) # the barset [bs] is used for determining object position files = IO.list_file_to_strings(files) info("Using "+str(files[0])+" for slit configuration.") x, x, bs = IO.readmosfits(files[0], options) for i_slit in xrange(len(solutions)): solution = all_solutions[0][i_slit] header = EPS[0].copy() obj = header['OBJECT'] target_name = bs.ssl[-(i_slit+1)]['Target_Name'] header['OBJECT'] = target_name pixel_dist = np.float(bs.ssl[-(i_slit+1)]['Target_to_center_of_slit_distance'])/0.18 pixel_dist -= solution['offset'] ll = solution["lambda"] header["wat0_001"] = "system=world" header["wat1_001"] = "wtype=linear" header["wat2_001"] = "wtype=linear" header["dispaxis"] = 1 header["dclog1"] = "Transform" header["dc-flag"] = 0 header["ctype1"] = "AWAV" header["cunit1"] = "Angstrom" header["crval1"] = ll[0] header["crval2"] = -solution["eps_img"].shape[0]/2 - pixel_dist header["crpix1"] = 1 header["crpix2"] = 1 #remove redundant CDELTi due to wavelength issues with ds9 #see: https://github.com/Keck-DataReductionPipelines/MosfireDRP/issues/44 #header["cdelt1"] = 1 #header["cdelt2"] = 1 header["cname1"] = "angstrom" header["cname2"] = "pixel" header["cd1_1"] = ll[1]-ll[0] header["cd1_2"] = 0 header["cd2_1"] = 0 header["cd2_2"] = 1 try: header["BARYCORR"]= (lambdas[0]['BARYCORR'],lambdas[0].comments['BARYCORR']) except KeyError: warning( "Barycentric corrections not applied to the wavelength solution") pass S = output.shape img = solution["eps_img"] std = solution["sd_img"] tms = solution["itime_img"] for i_solution in xrange(1,len(all_solutions)): info("Combining solution %i" %i_solution) solution = all_solutions[i_solution][i_slit] img += solution["eps_img"] std += solution["sd_img"] tms += solution["itime_img"] #print "adding in quadrature" output = np.append(output, img, 0) output = np.append(output, np.nan*np.zeros((3,S[1])), 0) snrs = np.append(snrs, img*tms/std, 0) snrs = np.append(snrs, np.nan*np.zeros((3,S[1])), 0) sdout = np.append(sdout, std, 0) sdout = np.append(sdout, np.nan*np.zeros((3,S[1])), 0) itout = np.append(itout, tms, 0) itout = np.append(itout, np.nan*np.zeros((3,S[1])), 0) header['bunit'] = ('electron/second', 'electron power') IO.writefits(img, maskname, "{0}_{1}_{2}_eps.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header['bunit'] = ('electron/second', 'sigma/itime') IO.writefits(std/tms, maskname, "{0}_{1}_{2}_sig.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header['bunit'] = ('second', 'exposure time') IO.writefits(tms, maskname, "{0}_{1}_{2}_itime.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header['bunit'] = ('', 'SNR') IO.writefits(img*tms/std, maskname, "{0}_{1}_{2}_snrs.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header = EPS[0].copy() header["wat0_001"] = "system=world" header["wat1_001"] = "wtype=linear" header["wat2_001"] = "wtype=linear" header["dispaxis"] = 1 header["dclog1"] = "Transform" header["dc-flag"] = 0 header["ctype1"] = "AWAV" header["cunit1"] = ("Angstrom", 'Start wavelength') header["crval1"] = ll[0] header["crval2"] = 1 header["crpix1"] = 1 header["crpix2"] = 1 #remove redundant CDELTi due to wavelength issues with ds9 #see: https://github.com/Keck-DataReductionPipelines/MosfireDRP/issues/44 #header["cdelt1"] = 1 #header["cdelt2"] = 1 header["cname1"] = "angstrom" header["cname2"] = "pixel" header["cd1_1"] = (ll[1]-ll[0], 'Angstrom/pixel') header["cd1_2"] = 0 header["cd2_1"] = 0 header["cd2_2"] = 1 try: header["BARYCORR"]= (lambdas[0]['BARYCORR'],lambdas[0].comments['BARYCORR']) except KeyError: warning( "Barycentric corrections not applied to the wavelength solution") pass header["bunit"] = "ELECTRONS/SECOND" info("############ Final reduced file: {0}_{1}_eps.fits".format(outname,band)) IO.writefits(output, maskname, "{0}_{1}_eps.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False) header["bunit"] = "" IO.writefits(snrs, maskname, "{0}_{1}_snrs.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False) header["bunit"] = "ELECTRONS/SECOND" IO.writefits(sdout/itout, maskname, "{0}_{1}_sig.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False) header["bunit"] = "SECOND" IO.writefits(itout, maskname, "{0}_{1}_itime.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False)
def isEmpty(self,file): fname = IO.list_file_to_strings(file) if len(fname): return False else: return True
def go(maskname, band, filenames, wavefile, wavoptions, longoptions, use_flat=False): ''' The go command is the main entry point into this module. Inputs: maskname: String of the mask name band: String of 'Y', 'J', 'H', or 'K' filenames: List of filenames to reduce wavefile: String of path to FITS file with the wavelength solution wavoptions: The Wavelength Options dictionary longoptions: Dictionary containing: {'yrange': The pixel range to extract over 'row_position': The row to solve the initial wavelength solution on} use_flat: Boolean False [default] means to use no flat field Boolean True means to divide by the pixelflat ''' wavename = Wavelength.filelist_to_wavename(filenames, band, maskname, wavoptions).rstrip(".fits") print "Wavefile: {0}".format(wavefile) lamhdr, lamdat = IO.readfits(wavefile) positions = [] objname = None for listfile in filenames: fnames = IO.list_file_to_strings(listfile) if len(fnames) != 1: raise Exception( "I currently expect only one file per position. Remove multiple entries and try again" ) header, data, bs = IO.readmosfits(fnames[0], wavoptions) if objname is None: objname = header["object"] if objname != header["object"]: print( "Trying to combine longslit stack of object {0} " "with object {1}".format(objname, header["object"])) print("{0:18s} {1:30s} {2:2s} {3:4.1f}".format(file, header["object"], header["frameid"], header["yoffset"])) positions.append([fnames[0], header, data, bs]) print("{0:2g} nod positions found. Producing stacked difference" \ " image.".format(len(positions))) for i in xrange(len(positions) - 1): A = positions[i] B = positions[i + 1] print("----------- -----".format(A, B)) dname, varname = imdiff(A, B, maskname, band, header, wavoptions) if use_flat: apply_flat(dname, maskname, band) apply_flat(varname, maskname, band) rectify(dname, lamdat, A, B, maskname, band, wavoptions, longoptions) rectify(varname, lamdat, A, B, maskname, band, wavoptions, longoptions) print dname dname, vname = imdiff(B, A, maskname, band, header, wavoptions) if use_flat: apply_flat(dname, maskname, band) apply_flat(vname, maskname, band) rectify(dname, lamdat, B, A, maskname, band, wavoptions, longoptions) rectify(vname, lamdat, B, A, maskname, band, wavoptions, longoptions) if False: fname = os.path.join(path, wavename + ".fits") B = IO.readfits(fname) B = [fname, B[0], B[1]] for i in xrange(len(positions)): A = positions[i] imdiff(A, B, maskname, band, wavoptions) rectify(path, dname, lamdat, A, B, maskname, band, wavoptions, longoptions) imdiff(B, A, maskname, band, wavoptions) rectify(path, dname, lamdat, B, A, maskname, band, wavoptions, longoptions)
def printWavelengthFit(self): if self.type is 'longslit' or self.type is 'long2pos': addLongSlit = ",longslit=longslit" else: addLongSlit = "" if self.type is 'slitmask' or self.type is 'longslit': self.useNeon = False self.useArgon = False # determine is Argon and Neon files contain data for K bands if self.isEmpty('Ar.txt') is False and self.band is 'K': self.useArgon = True if self.isEmpty('Ne.txt') is False and self.band is 'K': self.useNeon = True self.addLine("Wavelength.imcombine(obsfiles, maskname, band, waveops)") if self.useArgon: self.addLine("Wavelength.imcombine('Ar.txt', maskname, band, waveops)") if self.useNeon: self.addLine("Wavelength.imcombine('Ne.txt', maskname, band, waveops)") self.addLine("Wavelength.fit_lambda_interactively(maskname, band, obsfiles,waveops"+addLongSlit+", bypass=bypassflag)") if self.useArgon: self.addLine("Wavelength.apply_interactive(maskname, band, waveops, apply=obsfiles, to='Ar.txt', argon=True)") if self.useNeon: self.addLine("Wavelength.apply_interactive(maskname, band, waveops, apply=obsfiles, to='Ne.txt', neon=True)") self.addLine("Wavelength.fit_lambda(maskname, band, obsfiles, obsfiles,waveops"+addLongSlit+")") if self.useArgon and self.useNeon: self.addLine("Wavelength.fit_lambda(maskname, band, 'Ne.txt', 'Ne.txt',waveops, wavenames2='Ar.txt'"+addLongSlit+")") if self.useArgon and not self.useNeon: self.addLine("Wavelength.fit_lambda(maskname, band, 'Ar.txt', 'Ar.txt',waveops"+addLongSlit+")") if self.useNeon and not self.useArgon: self.addLine("Wavelength.fit_lambda(maskname, band, 'Ne.txt', 'Ne.txt',waveops"+addLongSlit+")") if self.useNeon or self.useArgon: self.addLine("LROI = [[21000,22800]]*1") if self.useNeon: self.addLine("LROIs = Wavelength.check_wavelength_roi(maskname, band, obsfiles, 'Ne.txt', LROI, waveops)") if self.useArgon and not self.useNeon: self.addLine("LROIs = Wavelength.check_wavelength_roi(maskname, band, obsfiles, 'Ar.txt', LROI, waveops)") self.addLine("Wavelength.apply_lambda_simple(maskname, band, obsfiles, waveops"+addLongSlit+")") if self.useArgon and self.useNeon: self.addLine("Wavelength.apply_lambda_sky_and_arc(maskname, band, obsfiles, 'Ne.txt', LROIs, waveops)") if self.useArgon and not self.useNeon: self.addLine("Wavelength.apply_lambda_sky_and_arc(maskname, band, obsfiles, 'Ar.txt', LROIs, waveops)") if self.useNeon and not self.useArgon: self.addLine("Wavelength.apply_lambda_sky_and_arc(maskname, band, obsfiles, 'Ne.txt', LROIs, waveops)") # determine waveleng name files = IO.list_file_to_strings(self.offsetFiles) if self.useNeon: neon_files = IO.list_file_to_strings('Ne.txt') self.waveName = "merged_lambda_solution_"+str(Wavelength.filelist_to_wavename(files, self.band, self.maskName,"")).rstrip(".fits")+"_and_"+str(Wavelength.filelist_to_wavename(neon_files, self.band, self.maskName,"")) elif self.useArgon and not self.useNeon: argon_files = IO.list_file_to_strings('Ar.txt') self.waveName = "merged_lambda_solution_"+str(Wavelength.filelist_to_wavename(files, self.band, self.maskName,"")).rstrip(".fits")+"_and_"+str(Wavelength.filelist_to_wavename(argon_files, self.band, self.maskName,"")) else: self.waveName = "lambda_solution_"+str(Wavelength.filelist_to_wavename(files, self.band, self.maskName,"")) if self.type is 'long2pos' or self.type is 'long2pos_specphot': calibWith = "" if self.isEmpty('Ar.txt') is False: self.addLine("argon = ['Ar.txt']") calibWith = "argon" waveFiles = IO.list_file_to_strings('Ar.txt') if self.isEmpty('Ne.txt') is False: self.addLine("neon = ['Ne.txt']") calibWith = "neon" waveFiles = IO.list_file_to_strings('Ne.txt') if calibWith: # we have either Argon, or Neon, or both, so we can use arcs for the reduction self.addLine("Wavelength.imcombine("+str(calibWith)+", maskname, band, waveops)") self.addLine("Wavelength.fit_lambda_interactively(maskname, band, "+str(calibWith)+",waveops,longslit=longslit, "+str(calibWith)+"=True, bypass=bypassflag)") self.addLine("Wavelength.fit_lambda(maskname, band, "+str(calibWith)+","+str(calibWith)+",waveops,longslit=longslit)") self.addLine("Wavelength.apply_lambda_simple(maskname, band, "+str(calibWith)+", waveops, longslit=longslit, smooth=True)") self.waveName = "lambda_solution_"+str(Wavelength.filelist_to_wavename(waveFiles, self.band, self.maskName,"")) else: # we have no arcs. For the time being, we can try with sky lines but this only works with long2pos specphot print "#####################################################################################################" print "WARNING: There are no arc calibration files" print " The pipeline will try to use sky lines but this only works if the observation is long enough" print " and if you are only using long2pos. It will NOT work on long2pos_specphot" print " Please contact the MosfireDRP team to obtain a standard wavelength solution" print "#####################################################################################################" self.addLine("obsfiles = obsfiles_posAnarrow + obsfiles_posCnarrow") self.addLine("Wavelength.imcombine(obsfiles, maskname, band, waveops)") self.addLine("Wavelength.fit_lambda_interactively(maskname, band, obsfiles ,waveops,longslit=longslit, bypass=bypassflag)") self.addLine("Wavelength.fit_lambda(maskname, band, obsfiles,obsfiles ,waveops,longslit=longslit)") self.addLine("Wavelength.apply_lambda_simple(maskname, band, obsfiles, waveops, longslit=longslit, smooth=True)") files = IO.list_file_to_strings(self.offsetFiles) self.waveName = "lambda_solution_"+str(Wavelength.filelist_to_wavename(files, self.band, self.maskName,"")) self.addLine("") self.addLine("Wavelength_file = '"+str(self.waveName)+"'") self.addLine("Wavelength.bary_corr(obsfiles, Wavelength_file, maskname, band, waveops)") self.addLine("")
def isEmpty(self, file): fname = IO.list_file_to_strings(file) if len(fname): return False else: return True
def handle_flats(flatlist, maskname, band, options, extension=None): ''' handle_flats is the primary entry point to the Flats module. handle_flats takes a list of individual exposure FITS files and creates: 1. A CRR, dark subtracted, pixel-response flat file. 2. A set of polynomials that mark the edges of a slit Inputs: flatlist: Either a string of an input file or a list of file names maskname: The name of a mask band: A string indicating the bandceil Outputs: file {maskname}/flat_2d_{band}.fits -- pixel response flat file {maskname}/edges.np ''' tick = time.time() # Check bpos = np.ones(92) * -1 flatlist = IO.list_file_to_strings(flatlist) print flatlist for fname in flatlist: hdr, dat, bs = IO.readmosfits(fname, options, extension=extension) try: bs0 except: bs0 = bs if np.any(bs0.pos != bs.pos): raise Exception("Barsets do not seem to match") if hdr["filter"] != band: print ("Filter name %s does not match header filter name " "%s in file %s" % (band, hdr["filter"], fname)) for i in xrange(len(bpos)): b = hdr["B{0:02d}POS".format(i+1)] if bpos[i] == -1: bpos[i] = b else: if bpos[i] != b: raise Exception("Bar positions are not all the same in " "this set of flat files") bs = bs0 # Imcombine if True: print "Attempting to combine: ", flatlist combine(flatlist, maskname, band, options) print "Combined '%s' to '%s'" % (flatlist, maskname) path = "combflat_2d_%s.fits" % band (header, data) = IO.readfits(path, use_bpm=True) print "Flat written to %s" % path # Edge Trace results = find_and_fit_edges(data, header, bs, options) results[-1]["maskname"] = maskname results[-1]["band"] = band np.save("slit-edges_{0}".format(band), results) save_ds9_edges(results, options) # Generate Flat out = "pixelflat_2d_%s.fits" % (band) make_pixel_flat(data, results, options, out, flatlist) print "Pixel flat took {0:6.4} s".format(time.time()-tick)
def handle_rectification(maskname, in_files, wavename, band_pass, files, options, commissioning_shift=3.0, target='default', plan=None): '''Handle slit rectification and coaddition. Args: maskname: The mask name string in_files: List of stacked spectra in electron per second. Will look like ['electrons_Offset_1.5.txt.fits', 'electrons_Offset_-1.5.txt.fits'] wavename: path (relative or full) to the wavelength stack file, string band_pass: Band pass name, string barset_file: Path to a mosfire fits file containing the full set of FITS extensions for the barset. It can be any file in the list of science files. Returns: None Writes files: [maskname]_[band]_[object name]_eps.fits -- The rectified, background subtracted, stacked eps spectrum [maskname]_[band]_[object name]_sig.fits -- Rectified, background subtracted, stacked weight spectrum (STD/itime) [maskname]_[band]_[object_name]_itime.fits Rectified, CRR stacked integration time spectrum [maskname]_[band]_[object_name]_snrs.fits Rectified signal to noise spectrum ''' global edges, dats, vars, itimes, shifts, lambdas, band, fidl, all_shifts band = band_pass dlambda = Wavelength.grating_results(band) hpp = Filters.hpp[band] fidl = np.arange(hpp[0], hpp[1], dlambda) lambdas = IO.readfits(wavename, options) if np.any(lambdas[1].data < 0) or np.any(lambdas[1].data > 29000): info("***********WARNING ***********") info("The file {0} may not be a wavelength file.".format(wavename)) info("Check before proceeding.") info("***********WARNING ***********") edges, meta = IO.load_edges(maskname, band, options) shifts = [] posnames = [] postoshift = {} for file in in_files: info(":: "+str(file)) II = IO.read_drpfits(maskname, file, options) off = np.array((II[0]["decoff"], II[0]["raoff"]),dtype=np.float64) if "yoffset" in II[0]: off = -II[0]["yoffset"] else: # Deal with data taken during commissioning if II[0]["frameid"] == 'A': off = 0.0 else: off = commissioning_shift try: off0 except: off0 = off shift = off - off0 shifts.append(shift) posnames.append(II[0]["frameid"]) postoshift[II[0]['frameid']] = shift info("Position {0} shift: {1:2.2f} as".format(off, shift)) # this is to deal with cases in which we want to rectify one single file if len(set(posnames)) is 1: plans = [['A']] else: if plan is None: plans = Background.guess_plan_from_positions(set(posnames)) else: plans = plan all_shifts = [] for myplan in plans: to_append = [] for pos in myplan: to_append.append(postoshift[pos]) all_shifts.append(to_append) # Reverse the elements in all_shifts to deal with an inversion all_shifts.reverse() theBPM = IO.badpixelmask() all_solutions = [] cntr = 0 if target is 'default': outname = maskname else: outname = target for plan in plans: if len(plan) is 1: p0 = 'A' p1 = 'B' else: p0 = plan[0].replace("'", "p") p1 = plan[1].replace("'", "p") suffix = "%s-%s" % (p0,p1) info("Handling plan %s" % suffix) fname = "bsub_{0}_{1}_{2}.fits".format(outname,band,suffix) EPS = IO.read_drpfits(maskname, fname, options) EPS[1] = np.ma.masked_array(EPS[1], theBPM, fill_value=0) fname = "var_{0}_{1}_{2}.fits".format(outname, band, suffix) VAR = IO.read_drpfits(maskname, fname, options) VAR[1] = np.ma.masked_array(VAR[1], theBPM, fill_value=np.inf) fname = "itime_{0}_{1}_{2}.fits".format(outname, band, suffix) ITIME = IO.read_drpfits(maskname, fname, options) ITIME[1] = np.ma.masked_array(ITIME[1], theBPM, fill_value=0) dats = EPS vars = VAR itimes = ITIME EPS[0]["ORIGFILE"] = fname tock = time.time() sols = list(range(len(edges)-1,-1,-1)) shifts = all_shifts[cntr] cntr += 1 p = Pool() solutions = p.map(handle_rectification_helper, sols) p.close() all_solutions.append(solutions) tick = time.time() info("-----> Mask took %i. Writing to disk." % (tick-tock)) output = np.zeros((1, len(fidl))) snrs = np.zeros((1, len(fidl))) sdout= np.zeros((1, len(fidl))) itout= np.zeros((1, len(fidl))) # the barset [bs] is used for determining object position files = IO.list_file_to_strings(files) info("Using "+str(files[0])+" for slit configuration.") x, x, bs = IO.readmosfits(files[0], options) for i_slit in range(len(solutions)): solution = all_solutions[0][i_slit] header = EPS[0].copy() obj = header['OBJECT'] #Again some weirdness with Longslit target names try: target_name = str(bs.ssl[-(i_slit+1)]['Target_Name'], 'utf-8') except TypeError: target_name = bs.ssl[-(i_slit+1)]['Target_Name'] header['OBJECT'] = target_name pixel_dist = np.float(bs.ssl[-(i_slit+1)]['Target_to_center_of_slit_distance'])/0.18 pixel_dist -= solution['offset'] ll = solution["lambda"] header["wat0_001"] = "system=world" header["wat1_001"] = "wtype=linear" header["wat2_001"] = "wtype=linear" header["dispaxis"] = 1 header["dclog1"] = "Transform" header["dc-flag"] = 0 header["ctype1"] = "AWAV" header["cunit1"] = "Angstrom" header["crval1"] = ll[0] header["crval2"] = -solution["eps_img"].shape[0]//2 - pixel_dist header["crpix1"] = 1 header["crpix2"] = 1 header["cdelt1"] = ll[1]-ll[0] header["cdelt2"] = 1 header["cname1"] = "angstrom" header["cname2"] = "pixel" header["cd1_1"] = ll[1]-ll[0] header["cd1_2"] = 0 header["cd2_1"] = 0 header["cd2_2"] = 1 S = output.shape img = solution["eps_img"] std = solution["sd_img"] tms = solution["itime_img"] for i_solution in range(1,len(all_solutions)): info("Combining solution %i" %i_solution) solution = all_solutions[i_solution][i_slit] img += solution["eps_img"] std += solution["sd_img"] tms += solution["itime_img"] output = np.append(output, img, 0) output = np.append(output, np.nan*np.zeros((3,S[1])), 0) snrs = np.append(snrs, img*tms/std, 0) snrs = np.append(snrs, np.nan*np.zeros((3,S[1])), 0) sdout = np.append(sdout, std, 0) sdout = np.append(sdout, np.nan*np.zeros((3,S[1])), 0) itout = np.append(itout, tms, 0) itout = np.append(itout, np.nan*np.zeros((3,S[1])), 0) header['bunit'] = ('electron/second', 'electron power') IO.writefits(img, maskname, "{0}_{1}_{2}_eps.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header['bunit'] = ('electron/second', 'sigma/itime') IO.writefits(std/tms, maskname, "{0}_{1}_{2}_sig.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header['bunit'] = ('second', 'exposure time') IO.writefits(tms, maskname, "{0}_{1}_{2}_itime.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header['bunit'] = ('', 'SNR') IO.writefits(img*tms/std, maskname, "{0}_{1}_{2}_snrs.fits".format(outname, band, target_name), options, overwrite=True, header=header, lossy_compress=False) header = EPS[0].copy() header["wat0_001"] = "system=world" header["wat1_001"] = "wtype=linear" header["wat2_001"] = "wtype=linear" header["dispaxis"] = 1 header["dclog1"] = "Transform" header["dc-flag"] = 0 header["ctype1"] = "AWAV" header["cunit1"] = ("Angstrom", 'Start wavelength') header["crval1"] = ll[0] header["crval2"] = 1 header["crpix1"] = 1 header["crpix2"] = 1 header["cdelt1"] = (ll[1]-ll[0], 'Angstrom/pixel') header["cdelt2"] = 1 header["cname1"] = "angstrom" header["cname2"] = "pixel" header["cd1_1"] = (ll[1]-ll[0], 'Angstrom/pixel') header["cd1_2"] = 0 header["cd2_1"] = 0 header["cd2_2"] = 1 header["bunit"] = "ELECTRONS/SECOND" info("############ Final reduced file: {0}_{1}_eps.fits".format(outname,band)) IO.writefits(output, maskname, "{0}_{1}_eps.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False) header["bunit"] = "" IO.writefits(snrs, maskname, "{0}_{1}_snrs.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False) header["bunit"] = "ELECTRONS/SECOND" IO.writefits(sdout/itout, maskname, "{0}_{1}_sig.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False) header["bunit"] = "SECOND" IO.writefits(itout, maskname, "{0}_{1}_itime.fits".format(outname, band), options, overwrite=True, header=header, lossy_compress=False)