"""
Script to determine the sensitivity function for a PypeIt 1D spectrum.
.. include common links, assuming primary doc root is up one directory
.. include:: ../include/links.rst
"""
from IPython import embed
from pypeit.scripts import scriptbase
from pathlib import Path
[docs]
class SensFunc(scriptbase.ScriptBase):
# TODO: Need an option here for multi_spec_det detectors, passed as a list
# of numbers in the SensFunc parset, or as --det 3 7 on the command line
[docs]
@classmethod
def get_parser(cls, width=None):
parser = super().get_parser(description='Compute a sensitivity function', width=width,
formatter=scriptbase.SmartFormatter)
parser.add_argument("spec1dfile", type=str,
help='spec1d file for the standard that will be used to compute '
'the sensitivity function')
parser.add_argument("--algorithm", type=str, default=None, choices=['UVIS', 'IR'],
help="R|Override the default algorithm for computing the sensitivity "
"function. Note that it is not possible to set --algorithm and "
"simultaneously use a .sens file with the --sens_file option. If "
"you are using a .sens file, set the algorithm there via:\n\n"
"F| [sensfunc]\n"
"F| algorithm = IR\n"
"\nThe algorithm options are:\n\n"
"UVIS = Should be used for data with lambda < 7000A. No "
"detailed model of telluric absorption but corrects for "
"atmospheric extinction.\n\n"
"IR = Should be used for data with lambbda > 7000A. Performs "
"joint fit for sensitivity function and telluric absorption "
"using HITRAN models.\n\n")
parser.add_argument("--multi", type=str,
help="R|List of detector numbers to splice together for instruments "
"with multiple detectors arranged in the spectral direction, "
"e.g. --multi = '3,7'. Note that it is not possible to set "
"--multi and simultaneously use a .sens file with the "
"--sens_file option. If you are using a .sens file, set the "
"multi_spec_det param there via:\n\n"
"F| [sensfunc]\n"
"F| multi_spec_det = 3,7\n"
"\n")
parser.add_argument("-o", "--outfile", type=str,
help='Output file for sensitivity function. If not specified, the '
'sensitivity function will be written out to a standard filename '
'in the current working directory, i.e. if the standard spec1d '
'file is named spec1d_b24-Feige66_KASTb_foo.fits the sensfunc '
'will be written to sens_b24-Feige66_KASTb_foo.fits. A QA file '
'will also be written as sens_spec1d_b24-Feige66_KASTb_foo_QA.pdf '
'and a file showing throughput plots to '
'sens_spec1d_b24-Feige66_KASTb_foo_throughput.pdf. The same '
'extensions for QA and throughput will be used if outfile is '
'provided but with .fits trimmed off if it is in the filename.')
parser.add_argument("-s", "--sens_file", type=str,
help='Configuration file with sensitivity function parameters')
parser.add_argument("-f", "--flatfile", type=str,
help="R|Use the flat file for computing the sensitivity "
"function. Note that it is not possible to set --flatfile and "
"simultaneously use a .sens file with the --sens_file option. If "
"you are using a .sens file, set the flatfile there via e.g.:\n\n"
"F| [sensfunc]\n"
"F| flatfile = Calibrations/Flat_A_0_DET01.fits\n\n"
"Where Flat_A_0_DET01.fits is the flat file in your "
"Calibrations directory\n")
parser.add_argument("--debug", default=False, action="store_true",
help="show debug plots?")
parser.add_argument("--par_outfile", default='sensfunc.par',
help="Name of output file to save the parameters used by the fit")
parser.add_argument('-v', '--verbosity', type=int, default=1,
help='Verbosity level between 0 [none] and 2 [all]. Default: 1. '
'Level 2 writes a log with filename sensfunc_YYYYMMDD-HHMM.log')
return parser
[docs]
@staticmethod
def main(args):
"""Executes sensitivity function computation."""
import os
from pypeit import msgs
from pypeit import inputfiles
from pypeit import io
from pypeit.par import pypeitpar
from pypeit import sensfunc
from pypeit.spectrographs.util import load_spectrograph
# Set the verbosity, and create a logfile if verbosity == 2
msgs.set_logfile_and_verbosity('sensfunc', args.verbosity)
# Check parameter inputs
if args.algorithm is not None and args.sens_file is not None:
msgs.error("It is not possible to set --algorithm and simultaneously use a .sens "
"file via the --sens_file option. If you are using a .sens file set the "
"algorithm there via:\n"
"\n"
" [sensfunc]\n"
" algorithm = IR\n"
"\n")
if args.flatfile is not None and args.sens_file is not None:
msgs.error("It is not possible to set --flatfile and simultaneously use a .sens "
"file via the --sens_file option. If you are using a .sens file set the "
"flatfile there via:\n"
"\n"
" [sensfunc]\n"
" flatfile = Calibrations/Flat_A_0_DET01.fits'\n"
"\n")
if args.multi is not None and args.sens_file is not None:
msgs.error("It is not possible to set --multi and simultaneously use a .sens file via "
"the --sens_file option. If you are using a .sens file set the detectors "
"there via:\n"
"\n"
" [sensfunc]\n"
" multi_spec_det = 3,7\n"
"\n")
# Determine the spectrograph and generate the primary FITS header
with io.fits_open(args.spec1dfile) as hdul:
spectrograph = load_spectrograph(hdul[0].header['PYP_SPEC'])
spectrograph_config_par = spectrograph.config_specific_par(hdul)
# Construct a primary FITS header that includes the spectrograph's
# config keys for inclusion in the output sensfunc file
primary_hdr = io.initialize_header()
add_keys = (
['PYP_SPEC', 'DATE-OBS', 'TELESCOP', 'INSTRUME', 'DETECTOR']
+ spectrograph.configuration_keys() + spectrograph.raw_header_cards()
)
for key in add_keys:
if key.upper() in hdul[0].header.keys():
primary_hdr[key.upper()] = hdul[0].header[key.upper()]
# If the .sens file was passed in read it and overwrite default parameters
if args.sens_file is not None:
sensFile = inputfiles.SensFile.from_file(args.sens_file)
# Read sens file
par = pypeitpar.PypeItPar.from_cfg_lines(
cfg_lines=spectrograph_config_par.to_config(),
merge_with=(sensFile.cfg_lines,))
else:
par = pypeitpar.PypeItPar.from_cfg_lines(cfg_lines=spectrograph_config_par.to_config())
# If algorithm was provided override defaults. Note this does undo .sens
# file since they cannot both be passed
if args.algorithm is not None:
par['sensfunc']['algorithm'] = args.algorithm
# If flatfile was provided override defaults. Note this does undo .sens
# file since they cannot both be passed
if args.flatfile is not None:
par['sensfunc']['flatfile'] = args.flatfile
# If multi was set override defaults. Note this does undo .sens file
# since they cannot both be passed
if args.multi is not None:
# parse
multi_spec_det = [int(item) for item in args.multi.split(',')]
par['sensfunc']['multi_spec_det'] = multi_spec_det
# TODO Add parsing of detectors here. If detectors passed from the
# command line, overwrite the parset values read in from the .sens file
# Write the par to disk
msgs.info(f'Writing the parameters to {args.par_outfile}')
par['sensfunc'].to_config(args.par_outfile, section_name='sensfunc', include_descr=False)
# TODO JFH I would like to be able to run only
# par['sensfunc'].to_config('sensfunc.par') but this crashes.
# TODO: KBW - You can do that if you override the
# pypeit.par.parset.ParSet.to_config method in the
# pypeit.par.pypeitpar.SensFuncPar class.
# Parse the output filename
outfile = (os.path.basename(args.spec1dfile)).replace('spec1d','sens') \
if args.outfile is None else args.outfile
# Instantiate the relevant class for the requested algorithm
sensobj = sensfunc.SensFunc.get_instance(args.spec1dfile, outfile, par['sensfunc'],
par_fluxcalib=par['fluxcalib'], debug=args.debug,
chk_version=par['rdx']['chk_version'])
# Generate the sensfunc
sensobj.run()
# Write it out to a file, including the new primary FITS header
sensobj.to_file(outfile, primary_hdr=primary_hdr, overwrite=True)
#TODO JFH Add a show_sensfunc option here and to the sensfunc classes.