Source code for potpyri.instruments.MMIRS

"""MMIRS/MMT instrument configuration and reduction parameters."""
from potpyri._version import __version__

import os
import ccdproc
import numpy as np

import astropy.units as u
from astropy.io import fits
from astropy.time import Time
from astropy.nddata import CCDData

# Internal dependency
from . import instrument

[docs] class MMIRS(instrument.Instrument): """MMIRS at MMT: NIR imaging with flat calibration.""" def __init__(self): self.version = __version__ self.name = 'MMIRS' # Extensions for keeping track of general metadata and WCS self.raw_header_ext = 1 self.wcs_extension = 1 # Detector specific characteristics self.pixscale = 0.202 self.saturation = 46000.0 self.min_exptime = 0.1 # Run dark/bias/flat calibration? self.dark = True self.bias = False self.flat = True # Parameters for handling calibration files # Run rejection on possible CR pixels in bias self.cr_bias = True # Extend header to first file extension for purposes of sort_files self.extend_header = False # How to combine images during stacking self.stack_method = 'median' self.wavelength = 'NIR' self.gain = [1.02, 1.08] self.rdnoise = [3.9,4.2,3.6,3.6] self.datasec = ['[1055:3024,217:3911]','[1067:3033,217:3911]'] self.biassec = ['[1:1054,1:4112]','[3034:4096,1:4112]'] # Keywords for selecting files from Sort_files object # This allows a single file type to be used for multiple purposes (e.g., for # generating a flat-field image from science exposures) self.filetype_keywords = {'SCIENCE':'SCIENCE', 'FLAT':'SCIENCE,FLAT', 'DARK':'DARK','BIAS':'BIAS'} # Header keywords self.target_keyword = 'OBJECT' self.exptime_keyword = 'EXPTIME' self.filter_keyword = 'FILTER' self.mjd_keyword = 'MJD' self.bin_keyword = 'CCDSUM' self.amp_keyword = 'NAMPS' # File sorting keywords self.science_keywords = ['OBSMODE','APTYPE'] self.science_values = ['imaging','open'] self.flat_keywords = [] self.flat_values = [] self.bias_keywords = [] self.bias_values = [] self.dark_keywords = ['OBJECT'] self.dark_values = ['ark'] self.spec_keywords = ['OBSMODE','APERTURE'] self.spec_values = ['spectral','open'] self.bad_keywords = ['MOSID'] self.bad_values = ['closed'] self.detrend = False self.catalog_zp = '2MASS' self.out_size = 2500 # Get a unique image number that can be derived only from the file header
[docs] def get_number(self, hdr): datestr = hdr['DATE-OBS'] elap = Time(datestr)-Time('1980-01-01') elap = int(np.round(elap.to(u.second).value)) return(elap)
[docs] def raw_format(self, proc): return('*.fits')
[docs] def get_rdnoise(self, hdr): return(hdr['RDNOISE'])
[docs] def get_gain(self, hdr): return(hdr['GAIN'])
[docs] def get_time(self, hdr): return Time(hdr['DATE-OBS']).mjd
# Not currently used - need better implementation of slope measurement to # account for jump detection, bias, etc.
[docs] def measure_slope(self, hdu): header = hdu[self.raw_header_ext].header # Get header names to know how many up the ramp samples there are names = [h.name for h in hdu] # Get data shape from first image EXT for creating slope frame data_shape = hdu['IM1'].data.shape all_data = [] all_times = [] num_reads = 0 for i in np.arange(len(hdu)): ext=f'IM{i+1}' if ext in names: all_data.append(hdu[ext].data) all_times.append(self.get_exptime(hdu[ext].header)) num_reads+=1 all_data = np.array(all_data) all_times = np.array(all_times) # Run a linear regression on 3D array all_data with respect to all_times A = np.vstack([all_times, np.ones(len(all_times))]).T data = np.linalg.lstsq(A, all_data.reshape(num_reads, -1), rcond=None) # Get slope data slope = data[0][0].reshape(*data_shape) slope = slope.astype(np.float32) slope = slope * np.max(all_times) return(slope)
# Takes filename and outputs CCDData object with raw image in units of e-
[docs] def import_image(self, filename, amp, log=None): hdu = fits.open(filename) header = hdu[self.raw_header_ext].header slope = hdu[1].data for key in hdu[1].header: try: header[key] = hdu[1].header[key] except ValueError: continue for key in ['BZERO','BSCALE','BITPIX']: if key in header.keys(): del header[key] # Multiply by GAIN and apply RDNOISE raw = CCDData(slope, header=header, unit=u.adu) red = ccdproc.ccd_process(raw, gain=self.get_gain(header)*u.electron/u.adu, readnoise=self.get_rdnoise(header)*u.electron) # Overscan correct red = ccdproc.subtract_overscan(red, overscan=red[0:4,:], overscan_axis=0) # Trim image red = ccdproc.ccd_process(red, trim=header['DATASEC']) # Make sure to take out *SEC keywords for key in ['DATASEC','CCDSEC','DETSEC','TRIMSEC','DETSIZE']: if key in red.header.keys(): del red.header[key] # Apply saturation saturate = self.saturation * self.get_gain(header) red.header['SATURATE'] = saturate # Re-apply data to mask red.mask = red.data > saturate return(red)