"""
Functions to work with flats.
"""
import os
from glob import glob
from os import path
import astropy.io.fits as pyfits
import numpy as np
from tqdm import tqdm
from . import plotting as pl
from . import utils as u
[docs]
class DarkOpeningError(FileNotFoundError):
pass
[docs]
def open_darks(darkfile):
"""
Opens dark files. Essentially a wrapper around pyfits.getdata that
also includes a descriptive exception if the file doesn't exist.
Inputs:
:darkfile: (str) path to dark to be opened.
Outputs:
:dark: (array) data from darks FITS file.
"""
if darkfile[-4:] != "fits":
raise DarkOpeningError(
"""Currently, SImMER only supports darks in FITS files."""
)
if not path.exists(darkfile):
raise DarkOpeningError(
"""The requested dark file can't be found. Please check that you have a dark
file corresponding to every exposure setting used in your observations and flats."""
)
else:
dark = pyfits.getdata(darkfile, 0)
return dark
[docs]
def flat_driver(raw_dir, reddir, config, inst, plotting_yml=None):
"""Sets up and runs create_flats.
Inputs:
:raw_dir: (string) directory for the raw data
:reddir: (string) directory for the reduced data
:config: (pandas DataFrame) dataframe corresponding to config sheet for data.
:inst: (Instrument object) instrument for which data is being reduced.
:plotting_yml: (string) path to the plotting configuration file.
"""
if plotting_yml:
pl.initialize_plotting(plotting_yml)
_flats = config[config.Object == "flat"]
filts = _flats.Filter.tolist()
#Remove duplicates from list of filters
filts = np.unique(filts)
for filter_name in tqdm(
filts, desc="Running flats", position=0, leave=True
):
flatlist = []
ww = np.where(_flats.Filter == filter_name)
allfiles = _flats.iloc[ww].Filenums.values
for aa in np.arange(len(allfiles)):
for bb in eval(allfiles[aa]):
flatlist.append(bb)
itime = _flats[_flats.Filter == filter_name].ExpTime.values[0]
# darks are matched with flats by exposure time
darkfile = reddir + f"dark_{int(round(itime))}sec.fits"
create_flats(
raw_dir, reddir, flatlist, darkfile, inst, filter_name=filter_name
)
[docs]
def create_flats(
raw_dir, reddir, flatlist, darkfile, inst, filter_name=None, test=False
):
"""Create a flat from a single list of flat files.
Inputs:
:raw_dir: (str) directory where the raw data is stored.
:reddir: (str) directory where the reduced data is stored.
:flatlist: (list) list of integers corresponding to flats.
:inst: (inst object) instrument for which data is being reduced.
:filter_name: (str) filter name given if head['FILT1NAM'] == 'Unknown'
:plotting_yml: (string) path to the plotting configuration file.
:test: (bool) Boolean flag used for testing purposes.
Outputs:
:final_flat: (array) median-filtered flat.
"""
nflats = len(flatlist)
flatfiles = u.make_filelist(raw_dir, flatlist, inst)
#Save flat filenames to label plotting grid
short_flatfiles = flatfiles.copy()
for jj in np.arange(len(flatfiles)):
short_flatfiles[jj] = os.path.basename(flatfiles[jj]).split('.')[0]
flat_array = u.read_imcube(flatfiles)
flat_array = inst.adjust_array(flat_array, nflats)
head = inst.head(flatfiles[0])
filt = inst.filt(nflats, head, filter_name)
if test:
dark = 0.0
else:
dark = open_darks(darkfile)
for i in range(nflats):
flat_array[i, :, :] = flat_array[i, :, :] - dark
flat_array[i, :, :] = flat_array[i, :, :] / np.median(
flat_array[i, :, :]
)
final_flat = np.median(flat_array, axis=0)
final_flat = final_flat / np.median(final_flat)
#CDD change: use a narrow range for flat colorscaling (was -2, 2)
flat_vmin, flat_vmax = np.percentile(flat_array, [1,99])
pl.plot_array(
"intermediate", flat_array, flat_vmin, flat_vmax, reddir, f"flat_cube_{filt}.png", snames=short_flatfiles
)
# CDD update
# head.update('DATAFILE', str(flatlist)) #add all file names
head.set("DATAFILE", str(flatlist)) # add all file names
# end CDD update
hdu = pyfits.PrimaryHDU(final_flat, header=head)
hdu.writeto(
reddir + f"flat_{filt}.fits", overwrite=True, output_verify="ignore"
)
return final_flat