From 8884f7396f2bc70cb1a7ba86e511328966d8cc92 Mon Sep 17 00:00:00 2001 From: Thomas Marquart Date: Mon, 5 Feb 2024 09:53:32 +0100 Subject: [PATCH] add ANDES as copy of CRIRES --- .gitignore | 2 + pyreduce/instruments/andes.json | 59 +++++++++++++++ pyreduce/instruments/andes.py | 102 ++++++++++++++++++++++++++ pyreduce/instruments/common.py | 2 +- pyreduce/instruments/crires_plus.json | 2 +- 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 pyreduce/instruments/andes.json create mode 100644 pyreduce/instruments/andes.py diff --git a/.gitignore b/.gitignore index 56eef78d..659d06d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +DATA __pycache__ *.swp *.swo @@ -8,3 +9,4 @@ cov.xml *.o pyreduce/clib/_slitfunc_2d.c pyreduce/clib/_slitfunc_bd.c +pyreduce_astro.egg* diff --git a/pyreduce/instruments/andes.json b/pyreduce/instruments/andes.json new file mode 100644 index 00000000..415ff406 --- /dev/null +++ b/pyreduce/instruments/andes.json @@ -0,0 +1,59 @@ +{ + "__comment__": "red and middle are in the same fits file, with different extensions, i.e. share the same mode identifier, but have different extensions", + "__instrument__": "ANDES", + "instrument": "INSTRUME", + "id_instrument": "ANDES", + "telescope": "VLT", + "date": "DATE-OBS", + "date_format": "fits", + "id_mode": "ESO INS MODE", + "id_band": "ESO INS WLEN ID", + "id_lamp": "ESO INS1 LAMP? ID", + "modes": ["SL", "IFU"], + "bands": ["UBV", "RIZ", "YJH", "K"], + "settings": ["B", "V", "R", "IZ", "Y", "J", "H", "K"], + "chips": ["det1"], + "extension": "CHIP1.INT1", + "orientation": 0, + "transpose": false, + "prescan_x": 5, + "overscan_x": 5, + "prescan_y": 5, + "overscan_y": 5, + "naxis_x": "NAXIS1", + "naxis_y": "NAXIS2", + "gain": "HIERARCH ESO DET CHIP GAIN", + "readnoise": "HIERARCH ESO DET CHIP RON", + "dark": "HIERARCH ESO DET DIT", + "sky": 0, + "exposure_time": "EXPTIME", + "image_type": "OBJECT", + "category": "HIERARCH ESO DPR CATG", + "ra": "RA", + "dec": "DEC", + "jd": "MJD-OBS", + "longitude": "HIERARCH ESO TEL GEOLON", + "latitude": "HIERARCH ESO TEL GEOLAT", + "altitude": "HIERARCH ESO TEL GEOELEV", + "target": "OBJECT", + "observation_type": "ESO DPR TYPE", + + "kw_bias" : "ESO DPR TYPE", + "kw_flat" : "ESO DPR TYPE", + "kw_curvature": "ESO DPR TYPE", + "kw_scatter": "ESO DPR TYPE", + "kw_orders" : "ESO DPR TYPE", + "kw_wave": "ESO DPR TYPE", + "kw_comb": "ESO DPR TYPE", + "kw_spec": "ESO DPR TYPE", + "id_bias": "DARK", + "id_flat": "FLAT", + "id_orders": "FLAT", + "id_curvature": "WAVE,FPET", + "id_scatter": "FLAT", + "id_wave": "WAVE,UNE", + "id_comb": "WAVE,FPET", + "id_spec": "STAR,*,*" + "id_lamp_wavecal" : "UNe_HCL", + "id_lamp_etalon": "Etalon_Halogen", +} diff --git a/pyreduce/instruments/andes.py b/pyreduce/instruments/andes.py new file mode 100644 index 00000000..d752a61b --- /dev/null +++ b/pyreduce/instruments/andes.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +""" +Handles instrument specific info for the HARPS spectrograph + +Mostly reading data from the header +""" +import logging +import os.path +import re +from itertools import product + +import numpy as np +from astropy.io import fits +from dateutil import parser + +from .common import Instrument, getter, observation_date_to_night +from .filters import Filter + +logger = logging.getLogger(__name__) + + +class ANDES(Instrument): + def __init__(self): + super().__init__() + self.filters["lamp"] = Filter(self.info["id_lamp"]) + self.filters["band"] = Filter(self.info["id_band"]) + self.filters["decker"] = Filter(self.info["id_decker"]) + self.shared += ["band", "decker"] + + def add_header_info(self, header, mode, **kwargs): + """read data from header and add it as REDUCE keyword back to the header""" + # "Normal" stuff is handled by the general version, specific changes to values happen here + # alternatively you can implement all of it here, whatever works + band, decker, detector = self.parse_mode(mode) + header = super().add_header_info(header, band) + info = self.load_info() + + return header + + def get_supported_modes(self): + settings = self.info["settings"] + deckers = self.info["deckers"] + detectors = self.info["chips"] + modes = [ + "_".join([s, d, c]) for s, d, c in product(settings, deckers, detectors) + ] + return modes + + def parse_mode(self, mode): + pattern = r"([YJHKLM]\d{4})(_(Open|pos1|pos2))?_det(\d)" + match = re.match(pattern, mode, flags=re.IGNORECASE) + band = match.group(1).upper() + if match.group(3) is not None: + decker = match.group(3).lower().capitalize() + else: + decker = "Open" + detector = match.group(4) + return band, decker, detector + + def get_expected_values(self, target, night, mode): + expectations = super().get_expected_values(target, night) + band, decker, detector = self.parse_mode(mode) + + for key in expectations.keys(): + if key == "bias": + continue + expectations[key]["band"] = band + expectations[key]["decker"] = decker + + return expectations + + def get_extension(self, header, mode): + band, decker, detector = self.parse_mode(mode) + extension = int(detector) + return extension + + def get_wavecal_filename(self, header, mode, **kwargs): + """Get the filename of the wavelength calibration config file""" + cwd = os.path.dirname(__file__) + fname = "{instrument}_{mode}.npz".format(instrument=self.name, mode=mode) + fname = os.path.join(cwd, "..", "wavecal", fname) + return fname + + def get_mask_filename(self, mode, **kwargs): + i = self.name.lower() + band, decker, detector = self.parse_mode(mode) + + fname = f"mask_{i}_det{detector}.fits.gz" + cwd = os.path.dirname(__file__) + fname = os.path.join(cwd, "..", "masks", fname) + return fname + + def get_wavelength_range(self, header, mode, **kwargs): + wmin = [header["ESO INS WLEN MIN%i" % i] for i in range(1, 11)] + wmax = [header["ESO INS WLEN MAX%i" % i] for i in range(1, 11)] + + wavelength_range = np.array([wmin, wmax]).T + # Invert the order numbering + wavelength_range = wavelength_range[::-1] + # Convert from nm to Angstrom + wavelength_range *= 10 + return wavelength_range diff --git a/pyreduce/instruments/common.py b/pyreduce/instruments/common.py index 67ac9239..3d755d75 100644 --- a/pyreduce/instruments/common.py +++ b/pyreduce/instruments/common.py @@ -103,7 +103,7 @@ def get(self, key, alt=None): class Instrument: """ Abstract parent class for all instruments - Handles the handling of instrument specific information + Handles the instrument specific information """ def __init__(self): diff --git a/pyreduce/instruments/crires_plus.json b/pyreduce/instruments/crires_plus.json index 56fd1524..625f8e9c 100644 --- a/pyreduce/instruments/crires_plus.json +++ b/pyreduce/instruments/crires_plus.json @@ -1,5 +1,5 @@ { - "__comment__": "red and middle are in the same fits file, with different extensions, i.e. share the same mode identifier, but have different extensions", + "__comment__": "The upgraded CRIRES at ESO VLT, a cross-dispersed IR spectrograph for YJHKLM bands.", "__instrument__": "CRIRES_PLUS", "instrument": "INSTRUME", "id_instrument": "CRIRES",