# Copyright(c) 2015 Association of Universities for Research in Astronomy, Inc.
# by James E.H. Turner.
import os.path
from ndmapper import config, ndprocess_defaults
from ndmapper.data import FileName
from ndmapper.libutils import map_API_enum
from ndmapper.iraf_task import run_task
__all__ = ['CAL_DEPS', 'gemini_iraf_helper', 'clean_pixels']
# Default cal dependencies are defined by instrument mode, with just an empty
# placeholder dict at this level:
CAL_DEPS = {'target' : []}
[docs]def gemini_iraf_helper():
"""
Define a few common default values for Gemini IRAF tasks in one place
(just to avoid more repetition of static values than necessary) and
convert some Python defaults to be more appropriate for the IRAF tasks.
Returns
-------
dict
Common parameter values suitable for passing to Gemini IRAF tasks.
"""
# Enable both VAR & DQ if either is specified, since most Gemini tasks
# can't control them individually:
if config['use_uncert'] or config['use_flags']:
vardq = True
else:
vardq = False
vals = {'gmosdata' : 'gmos$data/',
'key_airmass' : 'AIRMASS',
'key_biassec' : 'BIASSEC',
'key_ccdsec' : 'CCDSEC',
'key_ccdsum' : 'CCDSUM',
'key_datasec' : 'DATASEC',
'key_detsec' : 'DETSEC',
'key_exptime' : 'EXPTIME',
'key_gain' : 'GAIN',
'key_ron' : 'RDNOISE',
'mask_table_name' : 'MDF',
'observatory' : 'default',
'vardq' : vardq,
'verbose' : True
}
return vals
@ndprocess_defaults
[docs]def clean_pixels(inputs, out_names=None, method='global', grow=1.5,
bitmask=65535, axis=1, order=None, low_reject=3.,
high_reject=2.3, iterations=5, reprocess=None, interact=None):
"""
Replace each pixel whose corresponding `flags` value matches one or more
non-zero bits of the `bitmask` with a (locally- or globally-) interpolated
estimate of its true, uncontaminated value.
This currently works on 2D images.
Parameters
----------
inputs : DataFileList or DataFile
Input images, containing extracted, row-stacked fibre spectra with
linearized wavelength co-ordinates.
out_names : `str`-like or list of `str`-like, optional
Names of output images, each containing a 1D spectrum. If None
(default), the names of the DataFile instances returned will be
constructed from those of the corresponding input files, prefixed
with 'p'.
method : {'local', 'global'}
Method to use for interpolating good data to generate replacement
values; either 'local' interpolation along the narrowest dimension of
each contiguous bad region using IRAF's ``proto.fixpix`` (with
``linterp=INDEF`` and ``cinterp=INDEF``) or a 'global' fit to each row
or column using ``fit1d``. The default is 'global'.
grow : float, optional
The radius in pixels (default 1.5) by which to expand the rejection of
regions matching the ``bitmask`` in the ``flags`` array when
generating replacement values. This does not cause additional pixels
to be replaced, it merely avoids basing replacement values on the
immediately-surrounding pixels, where those are also contaminated at
a lower level.
bitmask : int, optional
The bit-wise OR of ``flags`` bits used to trigger pixel replacement.
The default of 65535 causes all pixels with DQ > 0 in the input to be
replaced in the output, while bitmask=0 would copy the input unchanged,
with intermediate values used to reject some defects but not others
(eg. 9 flags cosmic rays (8) and detector defects (1) in Gemini IRAF).
axis : {0, 1}
The image axis (Python convention) along which to fit 1D Chebyshev
models when using method 'global' (default 1, ie. rows).
order : int or None, optional
The order of the 1D Chebyshev fits when using method 'global'. With the
default of None, a value is selected automatically by 'gemfix'.
low_reject, high_reject : int, optional
Lower and upper thresholds, in standard deviations, for rejection of
nominally-good pixels (in addition to those excluded by the ``flags``
array) when performing 'global' fits, to help avoid any residual
contamination when generating replacement values. These default to
3.0 and 2.3, respectively (avoiding the statistically high values
associated with common defects such as cosmic rays more aggressively
than low ones).
iterations : int, optional
The number of pixel rejection and re-fitting iterations to perform to
obtain a final fit when using the 'global' method (default 5).
See "help gemfix" in IRAF for more detailed information.
Returns
-------
outimages : DataFileList
The 1D spectra produced by gfapsum.
Package 'config' options
------------------------
reprocess : bool or None
Re-generate and overwrite any existing output files on disk or skip
processing and re-use existing results, where available? The default
of None instead raises an exception where outputs already exist
(requiring the user to delete them explicitly). The processing is
always performed for outputs that aren't already available.
interact : bool
Enable interactive fitting for the 'global' method (default False)?
This may be overridden by the step's own "interact" parameter.
"""
# Use default prefix if output filename unspecified. We have to do some
# extra work here, normally done by run_task, to allow for the IRAF bug
# fix below to behave according to the reprocess flag.
prefix = 'p'
if not out_names:
out_names = [FileName(indf, prefix=prefix) for indf in inputs]
elif len(out_names) != len(inputs):
raise ValueError('inputs & out_names have unmatched lengths')
# Map some Python API values to equivalent IRAF ones:
method = map_API_enum('method', method, \
{'local' : 'fixpix', 'global' : 'fit1d'})
axis = map_API_enum('axis', axis, {0 : 2, 1 : 1})
order = order if order else 0
# Get a few common Gemini IRAF defaults.:
gemvars = gemini_iraf_helper()
# Keep copies of any input MDF tables, because an IRAF bug mangles them
# when running gemfix, causing subsequent steps to fail (eg. with only one
# of the two slits getting processed).
mdfs = {}
for indf, outname in zip(inputs, out_names):
filename = indf.filename.orig
mdfs[filename] = None
if reprocess or not os.path.exists(str(outname)):
for tp in indf._tables: # to do: add/use a public API
if str(tp.label).upper() == 'MDF':
mdfs[filename] = tp.table
break
# Use a simple summation, at least for now, as getting the rejection etc.
# right with the level of contrast involved can be tricky.
result = run_task(
'gemini.gemtools.gemfix',
inputs={'inimages' : inputs}, outputs={'outimages' : out_names},
prefix=prefix, suffix=None, comb_in=False, MEF_ext=False,
path_param=None, reprocess=reprocess, method=method, grow=grow,
bitmask=bitmask, axis=axis, order=order, low_reject=low_reject,
high_reject=high_reject, niterate=iterations, fl_inter=interact,
verbose=gemvars['verbose']
)
outlist = result['outimages']
# Restore the original MDF extensions to the output files, if applicable:
for outdf in outlist:
filename = outdf.filename.orig
mdf = mdfs[filename]
if mdf:
for tp in outdf._tables:
if str(tp.label).upper() == 'MDF':
tp.table = mdfs[filename]
outdf.save()
break
return outlist