pyreduce-astro 0.6.0__cp311-cp311-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyreduce/__init__.py +67 -0
- pyreduce/__main__.py +106 -0
- pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
- pyreduce/clib/__init__.py +0 -0
- pyreduce/clib/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/build_extract.py +75 -0
- pyreduce/clib/slit_func_2d_xi_zeta_bd.c +1313 -0
- pyreduce/clib/slit_func_2d_xi_zeta_bd.h +55 -0
- pyreduce/clib/slit_func_bd.c +362 -0
- pyreduce/clib/slit_func_bd.h +17 -0
- pyreduce/clipnflip.py +147 -0
- pyreduce/combine_frames.py +855 -0
- pyreduce/configuration.py +186 -0
- pyreduce/continuum_normalization.py +329 -0
- pyreduce/cwrappers.py +404 -0
- pyreduce/datasets.py +231 -0
- pyreduce/echelle.py +413 -0
- pyreduce/estimate_background_scatter.py +129 -0
- pyreduce/extract.py +1361 -0
- pyreduce/extraction_width.py +77 -0
- pyreduce/instruments/__init__.py +0 -0
- pyreduce/instruments/andes.json +61 -0
- pyreduce/instruments/andes.py +102 -0
- pyreduce/instruments/common.json +46 -0
- pyreduce/instruments/common.py +683 -0
- pyreduce/instruments/crires_plus.json +63 -0
- pyreduce/instruments/crires_plus.py +103 -0
- pyreduce/instruments/filters.py +195 -0
- pyreduce/instruments/harpn.json +136 -0
- pyreduce/instruments/harpn.py +201 -0
- pyreduce/instruments/harps.json +155 -0
- pyreduce/instruments/harps.py +310 -0
- pyreduce/instruments/instrument_info.py +140 -0
- pyreduce/instruments/instrument_schema.json +318 -0
- pyreduce/instruments/jwst_miri.json +53 -0
- pyreduce/instruments/jwst_miri.py +29 -0
- pyreduce/instruments/jwst_niriss.json +52 -0
- pyreduce/instruments/jwst_niriss.py +98 -0
- pyreduce/instruments/lick_apf.json +53 -0
- pyreduce/instruments/lick_apf.py +35 -0
- pyreduce/instruments/mcdonald.json +59 -0
- pyreduce/instruments/mcdonald.py +123 -0
- pyreduce/instruments/metis_ifu.json +63 -0
- pyreduce/instruments/metis_ifu.py +45 -0
- pyreduce/instruments/metis_lss.json +65 -0
- pyreduce/instruments/metis_lss.py +45 -0
- pyreduce/instruments/micado.json +53 -0
- pyreduce/instruments/micado.py +45 -0
- pyreduce/instruments/neid.json +51 -0
- pyreduce/instruments/neid.py +154 -0
- pyreduce/instruments/nirspec.json +56 -0
- pyreduce/instruments/nirspec.py +215 -0
- pyreduce/instruments/nte.json +47 -0
- pyreduce/instruments/nte.py +42 -0
- pyreduce/instruments/uves.json +59 -0
- pyreduce/instruments/uves.py +46 -0
- pyreduce/instruments/xshooter.json +66 -0
- pyreduce/instruments/xshooter.py +39 -0
- pyreduce/make_shear.py +606 -0
- pyreduce/masks/mask_crires_plus_det1.fits.gz +0 -0
- pyreduce/masks/mask_crires_plus_det2.fits.gz +0 -0
- pyreduce/masks/mask_crires_plus_det3.fits.gz +0 -0
- pyreduce/masks/mask_ctio_chiron.fits.gz +0 -0
- pyreduce/masks/mask_elodie.fits.gz +0 -0
- pyreduce/masks/mask_feros3.fits.gz +0 -0
- pyreduce/masks/mask_flames_giraffe.fits.gz +0 -0
- pyreduce/masks/mask_harps_blue.fits.gz +0 -0
- pyreduce/masks/mask_harps_red.fits.gz +0 -0
- pyreduce/masks/mask_hds_blue.fits.gz +0 -0
- pyreduce/masks/mask_hds_red.fits.gz +0 -0
- pyreduce/masks/mask_het_hrs_2x5.fits.gz +0 -0
- pyreduce/masks/mask_jwst_miri_lrs_slitless.fits.gz +0 -0
- pyreduce/masks/mask_jwst_niriss_gr700xd.fits.gz +0 -0
- pyreduce/masks/mask_lick_apf_.fits.gz +0 -0
- pyreduce/masks/mask_mcdonald.fits.gz +0 -0
- pyreduce/masks/mask_nes.fits.gz +0 -0
- pyreduce/masks/mask_nirspec_nirspec.fits.gz +0 -0
- pyreduce/masks/mask_sarg.fits.gz +0 -0
- pyreduce/masks/mask_sarg_2x2a.fits.gz +0 -0
- pyreduce/masks/mask_sarg_2x2b.fits.gz +0 -0
- pyreduce/masks/mask_subaru_hds_red.fits.gz +0 -0
- pyreduce/masks/mask_uves_blue.fits.gz +0 -0
- pyreduce/masks/mask_uves_blue_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle_2x2_split.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_uves_red.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_2x2.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_2x2_split.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_xshooter_nir.fits.gz +0 -0
- pyreduce/rectify.py +138 -0
- pyreduce/reduce.py +2205 -0
- pyreduce/settings/settings_ANDES.json +89 -0
- pyreduce/settings/settings_CRIRES_PLUS.json +89 -0
- pyreduce/settings/settings_HARPN.json +73 -0
- pyreduce/settings/settings_HARPS.json +69 -0
- pyreduce/settings/settings_JWST_MIRI.json +55 -0
- pyreduce/settings/settings_JWST_NIRISS.json +55 -0
- pyreduce/settings/settings_LICK_APF.json +62 -0
- pyreduce/settings/settings_MCDONALD.json +58 -0
- pyreduce/settings/settings_METIS_IFU.json +77 -0
- pyreduce/settings/settings_METIS_LSS.json +77 -0
- pyreduce/settings/settings_MICADO.json +78 -0
- pyreduce/settings/settings_NEID.json +73 -0
- pyreduce/settings/settings_NIRSPEC.json +58 -0
- pyreduce/settings/settings_NTE.json +60 -0
- pyreduce/settings/settings_UVES.json +54 -0
- pyreduce/settings/settings_XSHOOTER.json +78 -0
- pyreduce/settings/settings_pyreduce.json +178 -0
- pyreduce/settings/settings_schema.json +827 -0
- pyreduce/tools/__init__.py +0 -0
- pyreduce/tools/combine.py +117 -0
- pyreduce/trace_orders.py +645 -0
- pyreduce/util.py +1288 -0
- pyreduce/wavecal/MICADO_HK_3arcsec_chip5.npz +0 -0
- pyreduce/wavecal/atlas/thar.fits +4946 -13
- pyreduce/wavecal/atlas/thar_list.txt +4172 -0
- pyreduce/wavecal/atlas/une.fits +0 -0
- pyreduce/wavecal/convert.py +38 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det1.npz +0 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det2.npz +0 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det3.npz +0 -0
- pyreduce/wavecal/harpn_harpn_2D.npz +0 -0
- pyreduce/wavecal/harps_blue_2D.npz +0 -0
- pyreduce/wavecal/harps_blue_pol_2D.npz +0 -0
- pyreduce/wavecal/harps_red_2D.npz +0 -0
- pyreduce/wavecal/harps_red_pol_2D.npz +0 -0
- pyreduce/wavecal/mcdonald.npz +0 -0
- pyreduce/wavecal/metis_lss_l_2D.npz +0 -0
- pyreduce/wavecal/metis_lss_m_2D.npz +0 -0
- pyreduce/wavecal/nirspec_K2.npz +0 -0
- pyreduce/wavecal/uves_blue_360nm_2D.npz +0 -0
- pyreduce/wavecal/uves_blue_390nm_2D.npz +0 -0
- pyreduce/wavecal/uves_blue_437nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_2x2_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_565nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_580nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_600nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_665nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_860nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_580nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_600nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_665nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_760nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_860nm_2D.npz +0 -0
- pyreduce/wavecal/xshooter_nir.npz +0 -0
- pyreduce/wavelength_calibration.py +1873 -0
- pyreduce_astro-0.6.0.dist-info/METADATA +114 -0
- pyreduce_astro-0.6.0.dist-info/RECORD +158 -0
- pyreduce_astro-0.6.0.dist-info/WHEEL +4 -0
- pyreduce_astro-0.6.0.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the NEID spectrograph
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
from os.path import dirname, join
|
|
8
|
+
|
|
9
|
+
from .common import Instrument
|
|
10
|
+
from .filters import Filter, InstrumentFilter, NightFilter, ObjectFilter
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NEID(Instrument):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.filters = {
|
|
19
|
+
"instrument": InstrumentFilter(self.info["instrument"]),
|
|
20
|
+
"night": NightFilter(self.info["date"]),
|
|
21
|
+
# "branch": Filter(, regex=True),
|
|
22
|
+
"mode": Filter(
|
|
23
|
+
self.info["instrument_mode"], regex=True, flags=re.IGNORECASE
|
|
24
|
+
),
|
|
25
|
+
"type": Filter(self.info["observation_type"]),
|
|
26
|
+
"target": ObjectFilter(self.info["target"], regex=True),
|
|
27
|
+
}
|
|
28
|
+
self.night = "night"
|
|
29
|
+
self.science = "science"
|
|
30
|
+
self.shared = [
|
|
31
|
+
"instrument",
|
|
32
|
+
"night",
|
|
33
|
+
"mode",
|
|
34
|
+
]
|
|
35
|
+
self.find_closest = [
|
|
36
|
+
"bias",
|
|
37
|
+
"flat",
|
|
38
|
+
"wavecal_master",
|
|
39
|
+
"freq_comb_master",
|
|
40
|
+
"orders",
|
|
41
|
+
"scatter",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
def get_expected_values(self, target, night, mode, fiber):
|
|
45
|
+
"""Determine the default expected values in the headers for a given observation configuration
|
|
46
|
+
|
|
47
|
+
Any parameter may be None, to indicate that all values are allowed
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
target : str
|
|
52
|
+
Name of the star / observation target
|
|
53
|
+
night : str
|
|
54
|
+
Observation night/nights
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
expectations: dict
|
|
58
|
+
Dictionary of expected header values, with one entry per step.
|
|
59
|
+
The entries for each step refer to the filters defined in self.filters
|
|
60
|
+
|
|
61
|
+
Raises
|
|
62
|
+
------
|
|
63
|
+
ValueError
|
|
64
|
+
Invalid combination of parameters
|
|
65
|
+
"""
|
|
66
|
+
if target is not None:
|
|
67
|
+
target = target.replace(" ", r"(?:\s*|-)")
|
|
68
|
+
else:
|
|
69
|
+
target = ".*"
|
|
70
|
+
|
|
71
|
+
id_orddef = "LAMP,DARK,TUN"
|
|
72
|
+
id_spec = "STAR,WAVE"
|
|
73
|
+
|
|
74
|
+
expectations = {
|
|
75
|
+
"flat": {"instrument": "NEID", "night": night, "type": r"LAMP,LAMP,TUN"},
|
|
76
|
+
"orders": {
|
|
77
|
+
"instrument": "NEID",
|
|
78
|
+
"night": night,
|
|
79
|
+
"type": id_orddef,
|
|
80
|
+
},
|
|
81
|
+
"scatter": {
|
|
82
|
+
"instrument": "NEID",
|
|
83
|
+
"night": night,
|
|
84
|
+
"type": id_orddef, # Same as orders or same as flat?
|
|
85
|
+
},
|
|
86
|
+
"wavecal_master": {
|
|
87
|
+
"instrument": "NEID",
|
|
88
|
+
"night": night,
|
|
89
|
+
"type": r"WAVE,WAVE,THAR2",
|
|
90
|
+
},
|
|
91
|
+
"freq_comb_master": {
|
|
92
|
+
"instrument": "NEID",
|
|
93
|
+
"night": night,
|
|
94
|
+
"type": r"WAVE,WAVE,COMB",
|
|
95
|
+
},
|
|
96
|
+
"science": {
|
|
97
|
+
"instrument": "NEID",
|
|
98
|
+
"night": night,
|
|
99
|
+
"mode": mode,
|
|
100
|
+
"type": id_spec,
|
|
101
|
+
"target": target,
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
return expectations
|
|
105
|
+
|
|
106
|
+
def get_extension(self, header, mode):
|
|
107
|
+
extension = super().get_extension(header, mode)
|
|
108
|
+
|
|
109
|
+
return extension
|
|
110
|
+
|
|
111
|
+
def add_header_info(self, header, mode, **kwargs):
|
|
112
|
+
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
113
|
+
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
114
|
+
# alternatively you can implement all of it here, whatever works
|
|
115
|
+
header = super().add_header_info(header, mode)
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
header["e_ra"] /= 15
|
|
119
|
+
header["e_jd"] += header["e_exptim"] / (7200 * 24) + 0.5
|
|
120
|
+
|
|
121
|
+
except:
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
if (
|
|
126
|
+
header["NAXIS"] == 2
|
|
127
|
+
and header["NAXIS1"] == 4296
|
|
128
|
+
and header["NAXIS2"] == 4096
|
|
129
|
+
):
|
|
130
|
+
# both modes are in the same image
|
|
131
|
+
prescan_x = 50
|
|
132
|
+
overscan_x = 50
|
|
133
|
+
naxis_x = 2148
|
|
134
|
+
if mode == "BLUE":
|
|
135
|
+
header["e_xlo"] = prescan_x
|
|
136
|
+
header["e_xhi"] = naxis_x - overscan_x
|
|
137
|
+
elif mode == "RED":
|
|
138
|
+
header["e_xlo"] = naxis_x + prescan_x
|
|
139
|
+
header["e_xhi"] = 2 * naxis_x - overscan_x
|
|
140
|
+
except KeyError:
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
return header
|
|
144
|
+
|
|
145
|
+
def get_wavecal_filename(self, header, mode, **kwargs):
|
|
146
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
147
|
+
cwd = dirname(__file__)
|
|
148
|
+
fname = f"NEID_{mode.lower()}_2D.npz"
|
|
149
|
+
fname = join(cwd, "..", "wavecal", fname)
|
|
150
|
+
return fname
|
|
151
|
+
|
|
152
|
+
def get_wavelength_range(self, header, mode, **kwargs):
|
|
153
|
+
wave_range = super().get_wavelength_range(header, mode, **kwargs)
|
|
154
|
+
return wave_range
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"__instrument__": "NIRSPEC",
|
|
3
|
+
"instrument": "CURRINST",
|
|
4
|
+
"id_instrument": "NIRSPEC",
|
|
5
|
+
"telescope": "Keck",
|
|
6
|
+
"date": "DATE-OBS",
|
|
7
|
+
"date_format": "fits",
|
|
8
|
+
"modes": ["NIRSPEC"],
|
|
9
|
+
"extension": 0,
|
|
10
|
+
"orientation": 0,
|
|
11
|
+
"transpose": false,
|
|
12
|
+
"prescan_x": 5,
|
|
13
|
+
"overscan_x": 5,
|
|
14
|
+
"prescan_y": 0,
|
|
15
|
+
"overscan_y": 0,
|
|
16
|
+
"naxis_x": "NAXIS1",
|
|
17
|
+
"naxis_y": "NAXIS2",
|
|
18
|
+
"gain": "DETGAIN",
|
|
19
|
+
"readnoise": "DETRN",
|
|
20
|
+
"bias": "DETBIAS",
|
|
21
|
+
"dark": 0,
|
|
22
|
+
"sky": 0,
|
|
23
|
+
"air": "AIRMASS",
|
|
24
|
+
"exposure_time": "ITIME",
|
|
25
|
+
"image_type": "OBJECT",
|
|
26
|
+
"ra": "HA",
|
|
27
|
+
"dec": "DEC",
|
|
28
|
+
"jd": "MJD-OBS",
|
|
29
|
+
"longitude": -155.4783333,
|
|
30
|
+
"latitude": 19.8283333,
|
|
31
|
+
"altitude": 4160,
|
|
32
|
+
"target": "OBJECT",
|
|
33
|
+
|
|
34
|
+
"id_neon": "NEON",
|
|
35
|
+
"id_argon": "ARGON",
|
|
36
|
+
"id_krypton": "KRYPTON",
|
|
37
|
+
"id_xenon": "XENON",
|
|
38
|
+
"id_etalon": "ETALON",
|
|
39
|
+
|
|
40
|
+
"kw_bias" : null,
|
|
41
|
+
"kw_flat" : null,
|
|
42
|
+
"kw_curvature": null,
|
|
43
|
+
"kw_scatter": null,
|
|
44
|
+
"kw_orders" : null,
|
|
45
|
+
"kw_wave": null,
|
|
46
|
+
"kw_comb": null,
|
|
47
|
+
"kw_spec": null,
|
|
48
|
+
"id_bias" : "BIAS",
|
|
49
|
+
"id_flat": "FLAT",
|
|
50
|
+
"id_orders": null,
|
|
51
|
+
"id_curvature": null,
|
|
52
|
+
"id_scatter": null,
|
|
53
|
+
"id_wave": null,
|
|
54
|
+
"id_comb": null,
|
|
55
|
+
"id_spec": null
|
|
56
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the UVES spectrograph
|
|
3
|
+
|
|
4
|
+
Mostly reading data from the header
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import glob
|
|
8
|
+
import logging
|
|
9
|
+
import os.path
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from astropy.io import fits
|
|
14
|
+
from dateutil import parser
|
|
15
|
+
from tqdm import tqdm
|
|
16
|
+
|
|
17
|
+
from .common import Instrument, observation_date_to_night
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NIRSPEC(Instrument):
|
|
23
|
+
@staticmethod
|
|
24
|
+
def get_mode(header):
|
|
25
|
+
# TODO figure out the parameters to use for this
|
|
26
|
+
try:
|
|
27
|
+
fil1pos = header["FIL1POS"]
|
|
28
|
+
fil2pos = header["FIL2POS"]
|
|
29
|
+
filter = header["FILNAME"]
|
|
30
|
+
except KeyError:
|
|
31
|
+
return ""
|
|
32
|
+
|
|
33
|
+
# TODO get other settings
|
|
34
|
+
if filter == "NIRSPEC-1":
|
|
35
|
+
raise ValueError
|
|
36
|
+
elif filter == "NIRSPEC-2":
|
|
37
|
+
raise ValueError
|
|
38
|
+
elif filter == "NIRSPEC-4":
|
|
39
|
+
raise ValueError
|
|
40
|
+
elif filter == "NIRSPEC-5":
|
|
41
|
+
raise ValueError
|
|
42
|
+
elif filter == "NIRSPEC-7":
|
|
43
|
+
if fil1pos == 11 and fil2pos == 18:
|
|
44
|
+
setting = "K2"
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError
|
|
47
|
+
else:
|
|
48
|
+
raise ValueError
|
|
49
|
+
|
|
50
|
+
return setting
|
|
51
|
+
|
|
52
|
+
def add_header_info(self, header, mode, **kwargs):
|
|
53
|
+
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
54
|
+
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
55
|
+
# alternatively you can implement all of it here, whatever works
|
|
56
|
+
header = super().add_header_info(header, mode)
|
|
57
|
+
# header["e_setting"] = NIRSPEC.get_mode(header)
|
|
58
|
+
header["EXPTIME"] = header.get("ITIME", 0) * header.get("COADDS", 0)
|
|
59
|
+
return header
|
|
60
|
+
|
|
61
|
+
def sort_files(self, input_dir, target, night, mode, calibration_dir, **kwargs):
|
|
62
|
+
"""
|
|
63
|
+
Sort a set of fits files into different categories
|
|
64
|
+
types are: bias, flat, wavecal, orderdef, spec
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
input_dir : str
|
|
69
|
+
input directory containing the files to sort
|
|
70
|
+
target : str
|
|
71
|
+
name of the target as in the fits headers
|
|
72
|
+
night : str
|
|
73
|
+
observation night, possibly with wildcards
|
|
74
|
+
mode : str
|
|
75
|
+
instrument mode
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
files_per_night : list[dict{str:dict{str:list[str]}}]
|
|
79
|
+
a list of file sets, one entry per night, where each night consists of a dictionary with one entry per setting,
|
|
80
|
+
each fileset has five lists of filenames: "bias", "flat", "order", "wave", "spec", organised in another dict
|
|
81
|
+
nights_out : list[datetime]
|
|
82
|
+
a list of observation times, same order as files_per_night
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
# TODO allow several names for the target?
|
|
86
|
+
|
|
87
|
+
info = self.load_info()
|
|
88
|
+
target = target.casefold()
|
|
89
|
+
instrument = self.__class__.__name__
|
|
90
|
+
|
|
91
|
+
# Try matching with nights
|
|
92
|
+
try:
|
|
93
|
+
night = parser.parse(night).date()
|
|
94
|
+
individual_nights = [night]
|
|
95
|
+
except ValueError:
|
|
96
|
+
# if the input night can't be parsed, use all nights
|
|
97
|
+
# Usually the case if wildcards are involved
|
|
98
|
+
individual_nights = "all"
|
|
99
|
+
|
|
100
|
+
# find all fits files in the input dir(s)
|
|
101
|
+
input_dir = input_dir.format(
|
|
102
|
+
instrument=instrument.upper(), target=target, mode=mode, night=night
|
|
103
|
+
)
|
|
104
|
+
files = glob.glob(input_dir + "/*.fits")
|
|
105
|
+
files += glob.glob(input_dir + "/*.fits.gz")
|
|
106
|
+
files = np.array(files)
|
|
107
|
+
|
|
108
|
+
# Initialize arrays
|
|
109
|
+
# observed object
|
|
110
|
+
ob = np.zeros(len(files), dtype="U20")
|
|
111
|
+
# observed night, parsed into a datetime object
|
|
112
|
+
ni = np.zeros(len(files), dtype=datetime)
|
|
113
|
+
# instrument, used for observation
|
|
114
|
+
it = np.zeros(len(files), dtype="U20")
|
|
115
|
+
|
|
116
|
+
for i, f in enumerate(files):
|
|
117
|
+
with fits.open(f) as hdu:
|
|
118
|
+
h = hdu[0].header
|
|
119
|
+
ob[i] = h.get(info["target"], "")
|
|
120
|
+
ni_tmp = h.get(info["date"], "")
|
|
121
|
+
it[i] = h.get(info["instrument"], "")
|
|
122
|
+
|
|
123
|
+
# Sanitize input
|
|
124
|
+
ni[i] = observation_date_to_night(ni_tmp)
|
|
125
|
+
ob[i] = ob[i].replace("-", "").replace(" ", "").casefold()
|
|
126
|
+
|
|
127
|
+
if isinstance(individual_nights, str) and individual_nights == "all":
|
|
128
|
+
individual_nights = np.unique(ni)
|
|
129
|
+
logger.info(
|
|
130
|
+
"Can't parse night %s, use all %i individual nights instead",
|
|
131
|
+
night,
|
|
132
|
+
len(individual_nights),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
files_per_observation = []
|
|
136
|
+
cache = {}
|
|
137
|
+
|
|
138
|
+
for ind_night in tqdm(individual_nights):
|
|
139
|
+
# Select files for this night, this instrument, this instrument mode
|
|
140
|
+
selection = (ni == ind_night) & (it == instrument) & (ob == target)
|
|
141
|
+
|
|
142
|
+
for file in files[selection]:
|
|
143
|
+
# Read caliblist
|
|
144
|
+
caliblist = file[:-8] + ".caliblist"
|
|
145
|
+
caliblist = np.genfromtxt(
|
|
146
|
+
caliblist, skip_header=8, dtype=str, delimiter=" ", usecols=(0)
|
|
147
|
+
)
|
|
148
|
+
caliblist = np.array(
|
|
149
|
+
[
|
|
150
|
+
os.path.join(input_dir, calibration_dir, c) + ".gz"
|
|
151
|
+
for c in caliblist
|
|
152
|
+
]
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
tp = np.zeros(len(caliblist), dtype="U20")
|
|
156
|
+
for i, c in enumerate(caliblist):
|
|
157
|
+
try:
|
|
158
|
+
tp[i] = cache[c]
|
|
159
|
+
except KeyError:
|
|
160
|
+
h = fits.open(c)[0].header
|
|
161
|
+
if h[info["id_flat"]] == 1:
|
|
162
|
+
tp[i] = "flat"
|
|
163
|
+
elif (
|
|
164
|
+
h[info["id_neon"]] == 1
|
|
165
|
+
or h[info["id_argon"]] == 1
|
|
166
|
+
or h[info["id_krypton"]] == 1
|
|
167
|
+
or h[info["id_xenon"]] == 1
|
|
168
|
+
):
|
|
169
|
+
tp[i] = "wavecal"
|
|
170
|
+
elif h[info["id_etalon"]] == 1:
|
|
171
|
+
tp[i] = "freq_comb"
|
|
172
|
+
else:
|
|
173
|
+
tp[i] = "bias"
|
|
174
|
+
cache[c] = tp[i]
|
|
175
|
+
files_this_observation = {
|
|
176
|
+
"bias": caliblist[tp == "bias"],
|
|
177
|
+
"flat": caliblist[tp == "flat"],
|
|
178
|
+
"orders": caliblist[tp == "flat"],
|
|
179
|
+
"wavecal_master": caliblist[tp == "wavecal"],
|
|
180
|
+
"freq_comb_master": caliblist[tp == "freq_comb"],
|
|
181
|
+
"science": [file],
|
|
182
|
+
}
|
|
183
|
+
files_this_observation["curvature"] = (
|
|
184
|
+
files_this_observation["freq_comb_master"]
|
|
185
|
+
if len(files_this_observation["freq_comb_master"]) != 0
|
|
186
|
+
else files_this_observation["wavecal_master"]
|
|
187
|
+
)
|
|
188
|
+
files_this_observation["scatter"] = files_this_observation["orders"]
|
|
189
|
+
|
|
190
|
+
files_per_observation.append(
|
|
191
|
+
({"night": ind_night, "target": target}, files_this_observation)
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return files_per_observation
|
|
195
|
+
|
|
196
|
+
def get_wavecal_filename(self, header, mode, **kwargs):
|
|
197
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
198
|
+
info = self.load_info()
|
|
199
|
+
if header[info["id_neon"]] == 1:
|
|
200
|
+
pass
|
|
201
|
+
elif header[info["id_argon"]] == 1:
|
|
202
|
+
pass
|
|
203
|
+
elif header[info["id_krypton"]] == 1:
|
|
204
|
+
pass
|
|
205
|
+
elif header[info["id_xenon"]] == 1:
|
|
206
|
+
pass
|
|
207
|
+
else:
|
|
208
|
+
raise ValueError("Wavelength calibration element not recognised")
|
|
209
|
+
|
|
210
|
+
echelle_setting = "K2"
|
|
211
|
+
|
|
212
|
+
cwd = os.path.dirname(__file__)
|
|
213
|
+
fname = f"nirspec_{echelle_setting}.npz"
|
|
214
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
215
|
+
return fname
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"__instrument__": "NTE",
|
|
3
|
+
"instrument": "INSTRUME",
|
|
4
|
+
"id_instrument": "NTE",
|
|
5
|
+
"telescope": "NOT",
|
|
6
|
+
"category": "IMAGECAT",
|
|
7
|
+
"target": "OBJECT",
|
|
8
|
+
"date": "DATE-OBS",
|
|
9
|
+
"date_format" : "fits",
|
|
10
|
+
"modes": ["VIS", "NIR"],
|
|
11
|
+
"extension": 0,
|
|
12
|
+
"orientation": 0,
|
|
13
|
+
"prescan_x": 0,
|
|
14
|
+
"overscan_x": 0,
|
|
15
|
+
"prescan_y": 0,
|
|
16
|
+
"overscan_y": 0,
|
|
17
|
+
"naxis_x": "NAXIS1",
|
|
18
|
+
"naxis_y": "NAXIS2",
|
|
19
|
+
"gain": "GAIN",
|
|
20
|
+
"readnoise": "RDNOISE",
|
|
21
|
+
"dark": "DARK",
|
|
22
|
+
"sky": 0,
|
|
23
|
+
"exposure_time": "EXPTIME",
|
|
24
|
+
"ra": "RA",
|
|
25
|
+
"dec": "DEC",
|
|
26
|
+
"longitude": "LONGITUD",
|
|
27
|
+
"latitude": "LATITUD",
|
|
28
|
+
"altitude": "ELEVAT",
|
|
29
|
+
"kw_bias": "IMAGETYP",
|
|
30
|
+
"kw_dark": "IMAGETYP",
|
|
31
|
+
"kw_flat": "IMAGETYP",
|
|
32
|
+
"kw_curvature": "IMAGETYP",
|
|
33
|
+
"kw_scatter": "IMAGETYP",
|
|
34
|
+
"kw_orders": "IMAGETYP",
|
|
35
|
+
"kw_wave": "IMAGETYP",
|
|
36
|
+
"kw_comb": "IMAGETYP",
|
|
37
|
+
"kw_spec": "IMAGETYP",
|
|
38
|
+
"id_bias": "BIAS",
|
|
39
|
+
"id_dark": "DARK",
|
|
40
|
+
"id_flat": "LAMP,FLAT",
|
|
41
|
+
"id_orders": "LAMP,ORDERDEF",
|
|
42
|
+
"id_curvature": "LAMP,FPE",
|
|
43
|
+
"id_scatter": "LAMP,FLAT",
|
|
44
|
+
"id_wave": "LAMP,WAVE",
|
|
45
|
+
"id_comb": "LAMP,FPE",
|
|
46
|
+
"id_spec": "OBJECT"
|
|
47
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the NTE spectrograph
|
|
3
|
+
|
|
4
|
+
Mostly reading data from the header
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os.path
|
|
9
|
+
|
|
10
|
+
from .common import Instrument
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NTE(Instrument):
|
|
16
|
+
def add_header_info(self, header, mode, **kwargs):
|
|
17
|
+
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
18
|
+
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
19
|
+
# alternatively you can implement all of it here, whatever works
|
|
20
|
+
header = super().add_header_info(header, mode)
|
|
21
|
+
|
|
22
|
+
header["e_ra"] /= 15
|
|
23
|
+
if header["e_jd"] is not None:
|
|
24
|
+
header["e_jd"] += header["e_exptime"] / (7200 * 24) + 0.5
|
|
25
|
+
|
|
26
|
+
return header
|
|
27
|
+
|
|
28
|
+
def get_wavecal_filename(self, header, mode, **kwargs):
|
|
29
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
30
|
+
info = self.load_info()
|
|
31
|
+
specifier = int(header[info["wavecal_specifier"]])
|
|
32
|
+
|
|
33
|
+
cwd = os.path.dirname(__file__)
|
|
34
|
+
fname = "{instrument}_{mode}_{specifier}nm_2D.npz".format(
|
|
35
|
+
instrument="nte", mode=mode.lower(), specifier=specifier
|
|
36
|
+
)
|
|
37
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
38
|
+
return fname
|
|
39
|
+
|
|
40
|
+
def get_wavelength_range(self, header, mode):
|
|
41
|
+
wave = 7 * [7000, 20_000]
|
|
42
|
+
return wave
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"__instrument__": "UVES",
|
|
3
|
+
"instrument": "INSTRUME",
|
|
4
|
+
"id_instrument": "UVES",
|
|
5
|
+
"telescope": "VLT",
|
|
6
|
+
"date": "DATE-OBS",
|
|
7
|
+
"date_format": "fits",
|
|
8
|
+
"modes": ["BLUE", "MIDDLE", "RED"],
|
|
9
|
+
"modes_id": ["blue", "red", "red"],
|
|
10
|
+
"extension": [0, 2, 1],
|
|
11
|
+
"id": [[1, 5], [1, 4], [1, 4]],
|
|
12
|
+
"orientation": [2, 1, 1],
|
|
13
|
+
"transpose" : false,
|
|
14
|
+
"prescan_x": "HIERARCH ESO DET OUT{id[0]} PRSCX",
|
|
15
|
+
"overscan_x": "HIERARCH ESO DET OUT{id[0]} OVSCX",
|
|
16
|
+
"prescan_y": 0,
|
|
17
|
+
"overscan_y": 0,
|
|
18
|
+
"naxis_x": "NAXIS1",
|
|
19
|
+
"naxis_y": "NAXIS2",
|
|
20
|
+
"gain": "HIERARCH ESO DET OUT{id[0]} CONAD",
|
|
21
|
+
"readnoise": "HIERARCH ESO DET OUT{id[0]} RON",
|
|
22
|
+
"dark": "HIERARCH ESO INS DET{id[1]} OFFDRK",
|
|
23
|
+
"sky": "HIERARCH ESO INS DET{id[1]} OFFSKY",
|
|
24
|
+
"exposure_time": "EXPTIME",
|
|
25
|
+
"ra": "RA",
|
|
26
|
+
"dec": "DEC",
|
|
27
|
+
"longitude": "HIERARCH ESO TEL GEOLON",
|
|
28
|
+
"latitude": "HIERARCH ESO TEL GEOLAT",
|
|
29
|
+
"altitude": "HIERARCH ESO TEL GEOELEV",
|
|
30
|
+
|
|
31
|
+
"target": "OBJECT",
|
|
32
|
+
"wavecal_element": "thar",
|
|
33
|
+
|
|
34
|
+
"wavecal_specifier": "ESO INS GRAT2 WLEN",
|
|
35
|
+
"category": "HIERARCH ESO DPR CATG",
|
|
36
|
+
"image_type": "OBJECT",
|
|
37
|
+
|
|
38
|
+
"observation_type": "ESO DPR TYPE",
|
|
39
|
+
"instrument_mode": "ESO INS MODE",
|
|
40
|
+
"instrument_mode_alternative": "ESO TPL NAME",
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
"kw_bias" : "ESO DPR TYPE",
|
|
44
|
+
"kw_flat" : "ESO DPR TYPE",
|
|
45
|
+
"kw_curvature": "OBJECT",
|
|
46
|
+
"kw_scatter": "ESO DPR TYPE",
|
|
47
|
+
"kw_orders" : "ESO DPR TYPE",
|
|
48
|
+
"kw_wave": "OBJECT",
|
|
49
|
+
"kw_comb": null,
|
|
50
|
+
"kw_spec": "ESO DPR TYPE",
|
|
51
|
+
"id_bias" : "BIAS",
|
|
52
|
+
"id_flat": "LAMP,FLAT",
|
|
53
|
+
"id_orders": "LAMP,ORDERDEF",
|
|
54
|
+
"id_curvature": "LAMP,WAVE",
|
|
55
|
+
"id_scatter": "LAMP,ORDERDEF",
|
|
56
|
+
"id_wave": "LAMP,WAVE",
|
|
57
|
+
"id_comb": null,
|
|
58
|
+
"id_spec": "OBJECT,POINT"
|
|
59
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the UVES spectrograph
|
|
3
|
+
|
|
4
|
+
Mostly reading data from the header
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os.path
|
|
9
|
+
|
|
10
|
+
from .common import Instrument
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class UVES(Instrument):
|
|
16
|
+
def add_header_info(self, header, mode, **kwargs):
|
|
17
|
+
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
18
|
+
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
19
|
+
# alternatively you can implement all of it here, whatever works
|
|
20
|
+
header = super().add_header_info(header, mode)
|
|
21
|
+
|
|
22
|
+
header["e_ra"] /= 15
|
|
23
|
+
if header["e_jd"] is not None:
|
|
24
|
+
header["e_jd"] += header["e_exptime"] / (7200 * 24) + 0.5
|
|
25
|
+
|
|
26
|
+
return header
|
|
27
|
+
|
|
28
|
+
def get_wavecal_filename(self, header, mode, **kwargs):
|
|
29
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
30
|
+
info = self.load_info()
|
|
31
|
+
specifier = int(header[info["wavecal_specifier"]])
|
|
32
|
+
|
|
33
|
+
cwd = os.path.dirname(__file__)
|
|
34
|
+
fname = "{instrument}_{mode}_{specifier}nm_2D.npz".format(
|
|
35
|
+
instrument="uves", mode=mode.lower(), specifier=specifier
|
|
36
|
+
)
|
|
37
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
38
|
+
return fname
|
|
39
|
+
|
|
40
|
+
def get_mask_filename(self, mode, **kwargs):
|
|
41
|
+
i = self.name.lower()
|
|
42
|
+
m = mode.lower()
|
|
43
|
+
fname = f"mask_{i}_{m}.fits.gz"
|
|
44
|
+
cwd = os.path.dirname(__file__)
|
|
45
|
+
fname = os.path.join(cwd, "..", "masks", fname)
|
|
46
|
+
return fname
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"__instrument__": "XSHOOTER",
|
|
3
|
+
"instrument": "INSTRUME",
|
|
4
|
+
"id_instrument": "SHOOT|XSHOOTER",
|
|
5
|
+
"telescope": "VLT",
|
|
6
|
+
"date": "MJD-OBS",
|
|
7
|
+
"date_format": "mjd",
|
|
8
|
+
"modes": [
|
|
9
|
+
"UVB",
|
|
10
|
+
"VIS",
|
|
11
|
+
"NIR"
|
|
12
|
+
],
|
|
13
|
+
"kw_modes": "ESO DET CHIP*NAME",
|
|
14
|
+
"id_modes": [
|
|
15
|
+
"E2V CC44-82",
|
|
16
|
+
"MIT/LL CCID-20",
|
|
17
|
+
"Hawaii2RG"
|
|
18
|
+
],
|
|
19
|
+
"extension": 0,
|
|
20
|
+
"orientation": [
|
|
21
|
+
1,
|
|
22
|
+
0,
|
|
23
|
+
0
|
|
24
|
+
],
|
|
25
|
+
"transpose": false,
|
|
26
|
+
"prescan_x": 0,
|
|
27
|
+
"overscan_x": 0,
|
|
28
|
+
"prescan_y": 0,
|
|
29
|
+
"overscan_y": 0,
|
|
30
|
+
"naxis_x": "NAXIS1",
|
|
31
|
+
"naxis_y": "NAXIS2",
|
|
32
|
+
"gain": "HIERARCH ESO DET OUT1 GAIN",
|
|
33
|
+
"readnoise": "HIERARCH ESO DET OUT1 RON",
|
|
34
|
+
"dark": 10,
|
|
35
|
+
"exposure_time": "EXPTIME",
|
|
36
|
+
"ra": "RA",
|
|
37
|
+
"dec": "DEC",
|
|
38
|
+
"jd": "MJD-OBS",
|
|
39
|
+
"longitude": "HIERARCH ESO TEL GEOLON",
|
|
40
|
+
"latitude": "HIERARCH ESO TEL GEOLAT",
|
|
41
|
+
"altitude": "HIERARCH ESO TEL GEOELEV",
|
|
42
|
+
"instrument_mode": "HIERARCH ESO SEQ ARM",
|
|
43
|
+
"observation_type": "HIERARCH ESO DPR TYPE",
|
|
44
|
+
"observation_category": "HIERARCH ESO DPR CATG",
|
|
45
|
+
"target": "HIERARCH ESO OBS TARG NAME",
|
|
46
|
+
"object": "OBJECT",
|
|
47
|
+
"id_dark": "DARK",
|
|
48
|
+
"id_format": "LAMP,FMTCHK",
|
|
49
|
+
"id_tell": "STD,TELLURIC",
|
|
50
|
+
"kw_bias": "HIERARCH ESO DPR TYPE",
|
|
51
|
+
"kw_flat": "HIERARCH ESO DPR TYPE",
|
|
52
|
+
"kw_curvature": "HIERARCH ESO DPR TYPE",
|
|
53
|
+
"kw_scatter": "HIERARCH ESO DPR TYPE",
|
|
54
|
+
"kw_orders": "HIERARCH ESO DPR TYPE",
|
|
55
|
+
"kw_wave": "HIERARCH ESO DPR TYPE",
|
|
56
|
+
"kw_comb": null,
|
|
57
|
+
"kw_spec": "HIERARCH ESO DPR TYPE",
|
|
58
|
+
"id_bias": "DARK",
|
|
59
|
+
"id_flat": "LAMP,FLAT",
|
|
60
|
+
"id_orders": "LAMP,ORDERDEF",
|
|
61
|
+
"id_curvature": "LAMP,WAVE",
|
|
62
|
+
"id_scatter": "LAMP,FLAT",
|
|
63
|
+
"id_wave": "LAMP,WAVE",
|
|
64
|
+
"id_comb": null,
|
|
65
|
+
"id_spec": "OBJECT"
|
|
66
|
+
}
|