pyreduce-astro 0.7a4__cp314-cp314-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 +322 -0
- pyreduce/cli.py +342 -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.cp312-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp314-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.cp312-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp314-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_2d.cp312-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp314-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp312-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp314-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 +861 -0
- pyreduce/configuration.py +191 -0
- pyreduce/continuum_normalization.py +329 -0
- pyreduce/cwrappers.py +404 -0
- pyreduce/datasets.py +238 -0
- pyreduce/echelle.py +413 -0
- pyreduce/estimate_background_scatter.py +130 -0
- pyreduce/extract.py +1362 -0
- pyreduce/extraction_width.py +77 -0
- pyreduce/instruments/__init__.py +0 -0
- pyreduce/instruments/aj.py +9 -0
- pyreduce/instruments/aj.yaml +51 -0
- pyreduce/instruments/andes.py +102 -0
- pyreduce/instruments/andes.yaml +72 -0
- pyreduce/instruments/common.py +711 -0
- pyreduce/instruments/common.yaml +57 -0
- pyreduce/instruments/crires_plus.py +103 -0
- pyreduce/instruments/crires_plus.yaml +101 -0
- pyreduce/instruments/filters.py +195 -0
- pyreduce/instruments/harpn.py +203 -0
- pyreduce/instruments/harpn.yaml +140 -0
- pyreduce/instruments/harps.py +312 -0
- pyreduce/instruments/harps.yaml +144 -0
- pyreduce/instruments/instrument_info.py +140 -0
- pyreduce/instruments/jwst_miri.py +29 -0
- pyreduce/instruments/jwst_miri.yaml +53 -0
- pyreduce/instruments/jwst_niriss.py +98 -0
- pyreduce/instruments/jwst_niriss.yaml +60 -0
- pyreduce/instruments/lick_apf.py +35 -0
- pyreduce/instruments/lick_apf.yaml +60 -0
- pyreduce/instruments/mcdonald.py +123 -0
- pyreduce/instruments/mcdonald.yaml +56 -0
- pyreduce/instruments/metis_ifu.py +45 -0
- pyreduce/instruments/metis_ifu.yaml +62 -0
- pyreduce/instruments/metis_lss.py +45 -0
- pyreduce/instruments/metis_lss.yaml +62 -0
- pyreduce/instruments/micado.py +45 -0
- pyreduce/instruments/micado.yaml +62 -0
- pyreduce/instruments/models.py +257 -0
- pyreduce/instruments/neid.py +156 -0
- pyreduce/instruments/neid.yaml +61 -0
- pyreduce/instruments/nirspec.py +215 -0
- pyreduce/instruments/nirspec.yaml +63 -0
- pyreduce/instruments/nte.py +42 -0
- pyreduce/instruments/nte.yaml +55 -0
- pyreduce/instruments/uves.py +46 -0
- pyreduce/instruments/uves.yaml +65 -0
- pyreduce/instruments/xshooter.py +39 -0
- pyreduce/instruments/xshooter.yaml +63 -0
- pyreduce/make_shear.py +607 -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/pipeline.py +619 -0
- pyreduce/rectify.py +138 -0
- pyreduce/reduce.py +2065 -0
- pyreduce/settings/settings_AJ.json +19 -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 +184 -0
- pyreduce/settings/settings_schema.json +850 -0
- pyreduce/tools/__init__.py +0 -0
- pyreduce/tools/combine.py +117 -0
- pyreduce/trace.py +979 -0
- pyreduce/util.py +1366 -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 +1871 -0
- pyreduce_astro-0.7a4.dist-info/METADATA +106 -0
- pyreduce_astro-0.7a4.dist-info/RECORD +182 -0
- pyreduce_astro-0.7a4.dist-info/WHEEL +4 -0
- pyreduce_astro-0.7a4.dist-info/entry_points.txt +2 -0
- pyreduce_astro-0.7a4.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interface for all instrument specific information
|
|
3
|
+
The actual info is contained in the instruments/{name}.py modules/classes, which are all subclasses of "common"
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import importlib
|
|
7
|
+
|
|
8
|
+
from .common import Instrument
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def load_instrument(instrument) -> Instrument:
|
|
12
|
+
"""Load an python instrument module
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
instrument : str
|
|
17
|
+
name of the instrument
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
instrument : Instrument
|
|
22
|
+
Instance of the {instrument} class
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# TODO: Loading arbitrary modules is probably bad style
|
|
26
|
+
# from instruments import uves, harps
|
|
27
|
+
# instruments = {"uves": uves.UVES, "harps": harps.HARPS}
|
|
28
|
+
# instrument = instruments[instrument.lower()]
|
|
29
|
+
# instrument = instrument()
|
|
30
|
+
if instrument is None:
|
|
31
|
+
instrument = "common"
|
|
32
|
+
|
|
33
|
+
fname = f".instruments.{instrument.lower()}"
|
|
34
|
+
lib = importlib.import_module(fname, package="pyreduce")
|
|
35
|
+
instrument = getattr(lib, instrument.upper())
|
|
36
|
+
instrument = instrument()
|
|
37
|
+
|
|
38
|
+
return instrument
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_instrument_info(instrument):
|
|
42
|
+
"""Load instrument specific information
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
instrument : str
|
|
47
|
+
Name of the instrument
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
dict{str:obj}
|
|
52
|
+
Dictionary with information
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
instrument = load_instrument(instrument)
|
|
56
|
+
return instrument.info
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def sort_files(input_dir, target, night, instrument, arm, **kwargs):
|
|
60
|
+
"""Sort a list of files into different categories and discard files that are not used
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
input_dir : str
|
|
65
|
+
directory containing all files (with tags for target, night, and instrument)
|
|
66
|
+
target : str
|
|
67
|
+
observation target name, as found in the files
|
|
68
|
+
night : str
|
|
69
|
+
observation night of interest, as found in the files
|
|
70
|
+
instrument : str
|
|
71
|
+
instrument name
|
|
72
|
+
arm : str
|
|
73
|
+
instrument arm (e.g. BLUE/RED for HARPS)
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
biaslist : list(str)
|
|
78
|
+
list of bias files
|
|
79
|
+
flatlist : list(str)
|
|
80
|
+
list of flat field files
|
|
81
|
+
wavelist : list(str)
|
|
82
|
+
list of wavelength calibration files
|
|
83
|
+
orderlist : list(str)
|
|
84
|
+
list of order definition files (for order tracing)
|
|
85
|
+
speclist : list(str)
|
|
86
|
+
list of science files, i.e. observations
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
instrument = load_instrument(instrument)
|
|
90
|
+
return instrument.sort_files(input_dir, target, night, arm, **kwargs)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_supported_arms(instrument):
|
|
94
|
+
instrument = load_instrument(instrument)
|
|
95
|
+
return instrument.get_supported_arms()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def arminfo(header, instrument, arm, **kwargs):
|
|
99
|
+
"""Add instrument specific information to a header/dict
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
header : fits.header, dict
|
|
104
|
+
header to add information to
|
|
105
|
+
instrument : str
|
|
106
|
+
instrument name
|
|
107
|
+
arm : str
|
|
108
|
+
instrument arm (e.g. BLUE/RED for HARPS)
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
header
|
|
113
|
+
header with added information
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
instrument = load_instrument(instrument)
|
|
117
|
+
header = instrument.add_header_info(header, arm, **kwargs)
|
|
118
|
+
return header
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_wavecal_filename(header, instrument, arm, **kwargs):
|
|
122
|
+
"""Get the filename of the pre-existing wavelength solution for the current settings
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
header : fits.header, dict
|
|
127
|
+
header of the wavelength calibration file
|
|
128
|
+
instrument : str
|
|
129
|
+
instrument name
|
|
130
|
+
arm : str
|
|
131
|
+
instrument arm (e.g. BLUE/RED for HARPS)
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
filename : str
|
|
136
|
+
wavelength solution file
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
instrument = load_instrument(instrument)
|
|
140
|
+
return instrument.get_wavecal_filename(header, arm, **kwargs)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the HARPS 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 JWST_MIRI(Instrument):
|
|
16
|
+
def add_header_info(self, header, arm, **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, arm)
|
|
21
|
+
self.load_info()
|
|
22
|
+
return header
|
|
23
|
+
|
|
24
|
+
def get_wavecal_filename(self, header, arm, **kwargs):
|
|
25
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
26
|
+
cwd = os.path.dirname(__file__)
|
|
27
|
+
fname = "{instrument}_{arm}_2D.npz".format(instrument="harps", arm=arm)
|
|
28
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
29
|
+
return fname
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# JWST MIRI instrument configuration
|
|
2
|
+
|
|
3
|
+
__instrument__: JWST_MIRI
|
|
4
|
+
instrument: INSTRUME
|
|
5
|
+
id_instrument: MIRI
|
|
6
|
+
telescope: JWST
|
|
7
|
+
|
|
8
|
+
date: DATE-OBS
|
|
9
|
+
date_format: fits
|
|
10
|
+
time: TIME-OBS
|
|
11
|
+
|
|
12
|
+
target: OBJECT
|
|
13
|
+
instrument_mode: EXP_TYPE
|
|
14
|
+
arms: [LRS_SLITLESS]
|
|
15
|
+
arms_id: [MIR_LRS-SLITLESS]
|
|
16
|
+
extension: [SCI]
|
|
17
|
+
orientation: 3
|
|
18
|
+
transpose: false
|
|
19
|
+
|
|
20
|
+
overscan_x: 5
|
|
21
|
+
prescan_x: 15
|
|
22
|
+
prescan_y: 0
|
|
23
|
+
overscan_y: 0
|
|
24
|
+
naxis_x: SUBSIZE1
|
|
25
|
+
naxis_y: SUBSIZE2
|
|
26
|
+
nints: NINTS
|
|
27
|
+
ngroups: NGROUPS
|
|
28
|
+
|
|
29
|
+
gain: GAINCF
|
|
30
|
+
readnoise: RDNOISE
|
|
31
|
+
ra: RA_V1
|
|
32
|
+
dec: DEC_V1
|
|
33
|
+
pa: PA_V3
|
|
34
|
+
exposure_time: EXPTIME
|
|
35
|
+
|
|
36
|
+
# File classification keywords and patterns
|
|
37
|
+
kw_bias: null
|
|
38
|
+
kw_flat: null
|
|
39
|
+
kw_curvature: null
|
|
40
|
+
kw_scatter: null
|
|
41
|
+
kw_orders: null
|
|
42
|
+
kw_wave: null
|
|
43
|
+
kw_comb: null
|
|
44
|
+
kw_spec: FILETYPE
|
|
45
|
+
|
|
46
|
+
id_bias: null
|
|
47
|
+
id_flat: null
|
|
48
|
+
id_orders: null
|
|
49
|
+
id_curvature: null
|
|
50
|
+
id_scatter: null
|
|
51
|
+
id_wave: null
|
|
52
|
+
id_comb: null
|
|
53
|
+
id_spec: ILLUMINATION
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the HARPS spectrograph
|
|
3
|
+
|
|
4
|
+
Mostly reading data from the header
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os.path
|
|
9
|
+
|
|
10
|
+
from astropy import units as q
|
|
11
|
+
from astropy.io import fits
|
|
12
|
+
from astropy.time import Time
|
|
13
|
+
|
|
14
|
+
from .common import Instrument
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class JWST_NIRISS(Instrument):
|
|
20
|
+
def add_header_info(self, header, arm, **kwargs):
|
|
21
|
+
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
22
|
+
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
23
|
+
# alternatively you can implement all of it here, whatever works
|
|
24
|
+
header = super().add_header_info(header, arm)
|
|
25
|
+
self.load_info()
|
|
26
|
+
|
|
27
|
+
# TODO: this references some files, I dont know where they should be
|
|
28
|
+
header["e_gain"] = 1.61
|
|
29
|
+
header["e_readn"] = 18.32
|
|
30
|
+
header["e_dark"] = 0.0257
|
|
31
|
+
|
|
32
|
+
# total exposure time
|
|
33
|
+
header["exptime"] = header.get("TFRAME", 0)
|
|
34
|
+
|
|
35
|
+
return header
|
|
36
|
+
|
|
37
|
+
def split_observation(self, fname, arm):
|
|
38
|
+
hdu = fits.open(fname)
|
|
39
|
+
dirname = os.path.dirname(fname)
|
|
40
|
+
fname = os.path.basename(fname)
|
|
41
|
+
|
|
42
|
+
header = hdu[0].header
|
|
43
|
+
if "mjd-obs" not in header:
|
|
44
|
+
if len(header["DATE-OBS"]) <= 10:
|
|
45
|
+
time = header["DATE-OBS"] + "T" + header["TIME-OBS"]
|
|
46
|
+
else:
|
|
47
|
+
time = header["DATE-OBS"]
|
|
48
|
+
header["MJD-OBS"] = Time(time).mjd
|
|
49
|
+
|
|
50
|
+
header2 = fits.Header()
|
|
51
|
+
header2["EXTNAME"] = "SCI"
|
|
52
|
+
shape = hdu["SCI"].data.shape
|
|
53
|
+
|
|
54
|
+
nframes = shape[0]
|
|
55
|
+
ngroups = shape[1]
|
|
56
|
+
|
|
57
|
+
if nframes == 1 and ngroups == 1:
|
|
58
|
+
return [os.path.join(dirname, fname)]
|
|
59
|
+
|
|
60
|
+
data = hdu["SCI"].data.reshape((-1, *shape[-2:]))
|
|
61
|
+
bias = data[0]
|
|
62
|
+
primary = fits.PrimaryHDU(header=header)
|
|
63
|
+
files = []
|
|
64
|
+
os.makedirs(os.path.join(dirname, "split"), exist_ok=True)
|
|
65
|
+
for i in range(1, nframes * ngroups):
|
|
66
|
+
data[i] - bias
|
|
67
|
+
bias = data[i]
|
|
68
|
+
fname_this = os.path.join(dirname, "split", f"pyreduce_{i}_{fname}")
|
|
69
|
+
|
|
70
|
+
header["MJD-OBS"] += header["TFRAME"] * q.s.to(q.day)
|
|
71
|
+
secondary = fits.ImageHDU(data=data, header=header2)
|
|
72
|
+
hdu_this = fits.HDUList([primary, secondary])
|
|
73
|
+
hdu_this.writeto(fname_this, overwrite=True)
|
|
74
|
+
files.append(fname_this)
|
|
75
|
+
hdu.close()
|
|
76
|
+
return files
|
|
77
|
+
|
|
78
|
+
def sort_files(self, input_dir, target, night, arm, allow_calibration_only=False):
|
|
79
|
+
files = super().sort_files(
|
|
80
|
+
input_dir,
|
|
81
|
+
target,
|
|
82
|
+
night,
|
|
83
|
+
arm,
|
|
84
|
+
allow_calibration_only=allow_calibration_only,
|
|
85
|
+
)
|
|
86
|
+
for i, (_k, file) in enumerate(files):
|
|
87
|
+
files_split = []
|
|
88
|
+
for f in file["science"]:
|
|
89
|
+
files_split += self.split_observation(f, arm)
|
|
90
|
+
files[i][1]["science"] = files_split
|
|
91
|
+
return files
|
|
92
|
+
|
|
93
|
+
def get_wavecal_filename(self, header, arm, **kwargs):
|
|
94
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
95
|
+
cwd = os.path.dirname(__file__)
|
|
96
|
+
fname = "{instrument}_{arm}_2D.npz".format(instrument="harps", arm=arm)
|
|
97
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
98
|
+
return fname
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# JWST NIRISS instrument configuration
|
|
2
|
+
# Note: red and middle are in the same fits file, with different extensions,
|
|
3
|
+
# i.e. share the same mode identifier, but have different extensions
|
|
4
|
+
|
|
5
|
+
__instrument__: JWST_NIRISS
|
|
6
|
+
instrument: INSTRUME
|
|
7
|
+
id_instrument: NIRISS
|
|
8
|
+
telescope: JWST
|
|
9
|
+
|
|
10
|
+
date: DATE-OBS
|
|
11
|
+
date_format: iso
|
|
12
|
+
|
|
13
|
+
instrument_mode: PUPIL
|
|
14
|
+
arms: [GR700XD]
|
|
15
|
+
arms_id: [GR700XD]
|
|
16
|
+
extension: [SCI]
|
|
17
|
+
orientation: 0
|
|
18
|
+
transpose: false
|
|
19
|
+
|
|
20
|
+
prescan_y: 0
|
|
21
|
+
overscan_y: 4
|
|
22
|
+
prescan_x: 3
|
|
23
|
+
overscan_x: 4
|
|
24
|
+
naxis_x: NAXIS1
|
|
25
|
+
naxis_y: NAXIS2
|
|
26
|
+
|
|
27
|
+
gain: R_GAIN
|
|
28
|
+
readnoise: R_READNO
|
|
29
|
+
exposure_time: EXPTIME
|
|
30
|
+
|
|
31
|
+
image_type: OBJECT
|
|
32
|
+
ra: TARG_RA
|
|
33
|
+
dec: TARG_DEC
|
|
34
|
+
"position.x": JWST_X
|
|
35
|
+
"position.y": JWST_Y
|
|
36
|
+
"position.z": JWST_Z
|
|
37
|
+
"position.vx": JWST_DX
|
|
38
|
+
"position.vy": JWST_DY
|
|
39
|
+
"position.vz": JWST_DZ
|
|
40
|
+
target: TARGNAME
|
|
41
|
+
observation_type: FILETYPE
|
|
42
|
+
|
|
43
|
+
# File classification keywords and patterns
|
|
44
|
+
kw_bias: null
|
|
45
|
+
kw_flat: FILENAME
|
|
46
|
+
kw_curvature: null
|
|
47
|
+
kw_scatter: FILENAME
|
|
48
|
+
kw_orders: FILENAME
|
|
49
|
+
kw_wave: null
|
|
50
|
+
kw_comb: null
|
|
51
|
+
kw_spec: INSTRUME
|
|
52
|
+
|
|
53
|
+
id_bias: null
|
|
54
|
+
id_flat: flat.fits
|
|
55
|
+
id_orders: flat.fits
|
|
56
|
+
id_curvature: null
|
|
57
|
+
id_scatter: flat.fits
|
|
58
|
+
id_wave: null
|
|
59
|
+
id_comb: null
|
|
60
|
+
id_spec: NIRISS
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the HARPS 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 LICK_APF(Instrument):
|
|
16
|
+
def add_header_info(self, header, arm, **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, arm)
|
|
21
|
+
self.load_info()
|
|
22
|
+
|
|
23
|
+
# pos = EarthLocation.of_site("Lick Observatory")
|
|
24
|
+
# header["e_obslon"] = pos.lon.to_value("deg")
|
|
25
|
+
# header["e_obslat"] = pos.lat.to_value("deg")
|
|
26
|
+
# header["e_obsalt"] = pos.height.to_value("m")
|
|
27
|
+
|
|
28
|
+
return header
|
|
29
|
+
|
|
30
|
+
def get_wavecal_filename(self, header, arm, **kwargs):
|
|
31
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
32
|
+
cwd = os.path.dirname(__file__)
|
|
33
|
+
fname = "lick_apf_2D.npz"
|
|
34
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
35
|
+
return fname
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# LICK_APF instrument configuration
|
|
2
|
+
# Note: red and middle are in the same fits file, with different extensions,
|
|
3
|
+
# i.e. share the same mode identifier, but have different extensions
|
|
4
|
+
|
|
5
|
+
__instrument__: LICK_APF
|
|
6
|
+
instrument: version
|
|
7
|
+
id_instrument: apf
|
|
8
|
+
telescope: Lick
|
|
9
|
+
|
|
10
|
+
date: DATE
|
|
11
|
+
date_format: fits
|
|
12
|
+
|
|
13
|
+
arms: [""]
|
|
14
|
+
arms_id: [""]
|
|
15
|
+
extension: [0]
|
|
16
|
+
orientation: 3
|
|
17
|
+
transpose: false
|
|
18
|
+
|
|
19
|
+
prescan_y: TCPR1
|
|
20
|
+
overscan_y: TCPR2
|
|
21
|
+
prescan_x: PPRERD
|
|
22
|
+
overscan_x: PPRERD
|
|
23
|
+
naxis_x: NAXIS1
|
|
24
|
+
naxis_y: NAXIS2
|
|
25
|
+
|
|
26
|
+
gain: GAIN
|
|
27
|
+
readnoise: 0
|
|
28
|
+
dark: 0
|
|
29
|
+
sky: 0
|
|
30
|
+
exposure_time: EXPTIME
|
|
31
|
+
|
|
32
|
+
category: ""
|
|
33
|
+
ra: RA
|
|
34
|
+
dec: DEC
|
|
35
|
+
longitude: -121.63667
|
|
36
|
+
latitude: 37.34334
|
|
37
|
+
altitude: 1290
|
|
38
|
+
target: OBJECT
|
|
39
|
+
observation_type: OBSTYPE
|
|
40
|
+
|
|
41
|
+
# File classification keywords and patterns
|
|
42
|
+
kw_mode: null
|
|
43
|
+
kw_bias: OBSTYPE
|
|
44
|
+
kw_flat: OBJECT
|
|
45
|
+
kw_curvature: OBJECT
|
|
46
|
+
kw_scatter: OBJECT
|
|
47
|
+
kw_orders: OBJECT
|
|
48
|
+
kw_wave: OBJECT
|
|
49
|
+
kw_comb: null
|
|
50
|
+
kw_spec: OBSTYPE
|
|
51
|
+
|
|
52
|
+
id_mode: null
|
|
53
|
+
id_bias: DARK
|
|
54
|
+
id_flat: WideFlat
|
|
55
|
+
id_orders: NarrowFlat
|
|
56
|
+
id_curvature: ThAr
|
|
57
|
+
id_scatter: NarrowFlat
|
|
58
|
+
id_wave: ThAr
|
|
59
|
+
id_comb: null
|
|
60
|
+
id_spec: OBJECT
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the HARPS spectrograph
|
|
3
|
+
|
|
4
|
+
Mostly reading data from the header
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import os.path
|
|
9
|
+
import re
|
|
10
|
+
|
|
11
|
+
from astropy.time import Time
|
|
12
|
+
|
|
13
|
+
from .common import Instrument, getter
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MCDONALD(Instrument):
|
|
19
|
+
def __init__(self):
|
|
20
|
+
super().__init__()
|
|
21
|
+
# The date is a combination of the two values
|
|
22
|
+
kw = f"{{{self.info['date']}}}T{{{self.info['universal_time']}}}"
|
|
23
|
+
self.filters["night"].keyword = kw
|
|
24
|
+
|
|
25
|
+
def _convert_time_deg(self, v):
|
|
26
|
+
v = [float(s) for s in v.split(":")]
|
|
27
|
+
v = v[0] + v[1] / 60 + v[2] / 3600
|
|
28
|
+
return v
|
|
29
|
+
|
|
30
|
+
def add_header_info(self, header, arm, **kwargs):
|
|
31
|
+
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
32
|
+
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
33
|
+
# alternatively you can implement all of it here, whatever works
|
|
34
|
+
|
|
35
|
+
header = super().add_header_info(header, arm, **kwargs)
|
|
36
|
+
info = self.info # Use cached info dict
|
|
37
|
+
get = getter(header, info, arm)
|
|
38
|
+
|
|
39
|
+
header["e_orient"] = get("orientation", 0)
|
|
40
|
+
# As per IDL rotate if orient is 4 or larger and transpose is undefined
|
|
41
|
+
# the image is transposed
|
|
42
|
+
header["e_transpose"] = get("transpose", (header["e_orient"] % 8 >= 4))
|
|
43
|
+
|
|
44
|
+
trimsec = get("trimsec")
|
|
45
|
+
|
|
46
|
+
if trimsec is not None:
|
|
47
|
+
pattern = r"\[(\d*?):(\d*?),(\d*?):(\d*?)\]"
|
|
48
|
+
res = re.match(pattern, trimsec)
|
|
49
|
+
prescan_x = int(res[1]) + 1
|
|
50
|
+
overscan_x = int(res[2])
|
|
51
|
+
prescan_y = int(res[3])
|
|
52
|
+
overscan_y = int(res[4])
|
|
53
|
+
else:
|
|
54
|
+
prescan_x = 2
|
|
55
|
+
overscan_x = 2048
|
|
56
|
+
prescan_y = 2
|
|
57
|
+
overscan_y = 2047
|
|
58
|
+
|
|
59
|
+
header["e_xlo"] = prescan_x
|
|
60
|
+
header["e_xhi"] = overscan_x
|
|
61
|
+
|
|
62
|
+
header["e_ylo"] = prescan_y
|
|
63
|
+
header["e_yhi"] = overscan_y
|
|
64
|
+
|
|
65
|
+
amp = get("amplifier")
|
|
66
|
+
gain = info["gain"].format(amplifier=amp)
|
|
67
|
+
readnoise = info["readnoise"].format(amplifier=amp)
|
|
68
|
+
|
|
69
|
+
header["e_gain"] = get(gain, 1)
|
|
70
|
+
header["e_readn"] = get(readnoise, 0)
|
|
71
|
+
header["e_exptim"] = get("exposure_time", 0)
|
|
72
|
+
|
|
73
|
+
header["e_sky"] = get("sky", 0)
|
|
74
|
+
header["e_drk"] = get("dark", 0)
|
|
75
|
+
header["e_backg"] = header["e_gain"] * (header["e_drk"] + header["e_sky"])
|
|
76
|
+
|
|
77
|
+
header["e_imtype"] = get("observation_type")
|
|
78
|
+
header["e_ctg"] = get("observation_type")
|
|
79
|
+
|
|
80
|
+
obs_date = get("date")
|
|
81
|
+
ut = get("universal_time")
|
|
82
|
+
if obs_date is not None and ut is not None:
|
|
83
|
+
obs_date = f"{obs_date}T{ut}"
|
|
84
|
+
|
|
85
|
+
dark_time = get("dark_time")
|
|
86
|
+
ra = get("ra")
|
|
87
|
+
dec = get("dec")
|
|
88
|
+
|
|
89
|
+
if ra is not None:
|
|
90
|
+
ra = self._convert_time_deg(ra)
|
|
91
|
+
if dec is not None:
|
|
92
|
+
dec = self._convert_time_deg(dec)
|
|
93
|
+
if dark_time is not None:
|
|
94
|
+
tmid = dark_time / 2
|
|
95
|
+
else:
|
|
96
|
+
tmid = 0
|
|
97
|
+
if obs_date is not None:
|
|
98
|
+
jd = Time(obs_date).mjd + tmid + 0.5
|
|
99
|
+
else:
|
|
100
|
+
jd = 0
|
|
101
|
+
|
|
102
|
+
header["e_ra"] = ra
|
|
103
|
+
header["e_dec"] = dec
|
|
104
|
+
header["e_jd"] = jd
|
|
105
|
+
|
|
106
|
+
header["e_obslon"] = self._convert_time_deg(info["longitude"])
|
|
107
|
+
header["e_obslat"] = self._convert_time_deg(info["latitude"])
|
|
108
|
+
header["e_obsalt"] = info["altitude"]
|
|
109
|
+
|
|
110
|
+
return header
|
|
111
|
+
|
|
112
|
+
def get_wavecal_filename(self, header, arm, **kwargs):
|
|
113
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
114
|
+
cwd = os.path.dirname(__file__)
|
|
115
|
+
fname = "mcdonald.npz"
|
|
116
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
117
|
+
return fname
|
|
118
|
+
|
|
119
|
+
def get_mask_filename(self, arm, **kwargs):
|
|
120
|
+
fname = "mask_mcdonald.fits.gz"
|
|
121
|
+
cwd = os.path.dirname(__file__)
|
|
122
|
+
fname = os.path.join(cwd, "..", "masks", fname)
|
|
123
|
+
return fname
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# MCDONALD instrument configuration
|
|
2
|
+
|
|
3
|
+
__instrument__: MCDONALD
|
|
4
|
+
instrument: OBSERVAT
|
|
5
|
+
id_instrument: MCDONALD
|
|
6
|
+
telescope: OBSERVAT
|
|
7
|
+
|
|
8
|
+
date: DATE-OBS
|
|
9
|
+
|
|
10
|
+
arms: [CS21, CS23, TS21, TS23]
|
|
11
|
+
id_arm: [cs21-e2, cs23-e2, ts21-e2, ts23-e2]
|
|
12
|
+
extension: 0
|
|
13
|
+
orientation: 0
|
|
14
|
+
transpose: false
|
|
15
|
+
|
|
16
|
+
trimsec: TRIMSEC
|
|
17
|
+
naxis_x: NAXIS1
|
|
18
|
+
naxis_y: NAXIS2
|
|
19
|
+
amplifier: AMPLIFIE
|
|
20
|
+
gain: "GAIN{amplifier}"
|
|
21
|
+
readnoise: "RDNOISE{amplifier}"
|
|
22
|
+
dark: 1
|
|
23
|
+
sky: 0
|
|
24
|
+
dark_time: DARKTIME
|
|
25
|
+
exposure_time: EXPTIME
|
|
26
|
+
|
|
27
|
+
observation_type: IMAGETYP
|
|
28
|
+
category: IMAGETYP
|
|
29
|
+
ra: RA
|
|
30
|
+
dec: DEC
|
|
31
|
+
universal_time: UT
|
|
32
|
+
longitude: "104:1:18"
|
|
33
|
+
latitude: "30:40:18"
|
|
34
|
+
altitude: 2075
|
|
35
|
+
target: OBJECT
|
|
36
|
+
instrument_mode: INSTRUME
|
|
37
|
+
|
|
38
|
+
# File classification keywords and patterns
|
|
39
|
+
kw_arm: INSTRUME
|
|
40
|
+
kw_bias: OBJECT
|
|
41
|
+
kw_flat: IMAGETYP
|
|
42
|
+
kw_curvature: OBJECT
|
|
43
|
+
kw_scatter: IMAGETYP
|
|
44
|
+
kw_orders: IMAGETYP
|
|
45
|
+
kw_wave: OBJECT
|
|
46
|
+
kw_comb: null
|
|
47
|
+
kw_spec: IMAGETYP
|
|
48
|
+
|
|
49
|
+
id_bias: bias
|
|
50
|
+
id_flat: flat
|
|
51
|
+
id_orders: flat
|
|
52
|
+
id_curvature: ThAr
|
|
53
|
+
id_scatter: flat
|
|
54
|
+
id_wave: ThAr
|
|
55
|
+
id_comb: null
|
|
56
|
+
id_spec: object
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the METIS_IFU IFU 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 METIS_IFU(Instrument):
|
|
16
|
+
def add_header_info(self, header, arm, **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, arm)
|
|
21
|
+
|
|
22
|
+
# header["e_backg"] = (
|
|
23
|
+
# header["e_readn"] + header["e_exptime"] * header["e_drk"] / 3600
|
|
24
|
+
# )
|
|
25
|
+
#
|
|
26
|
+
# header["e_ra"] /= 15
|
|
27
|
+
# if header["e_jd"] is not None:
|
|
28
|
+
# header["e_jd"] += header["e_exptime"] / 2 / 3600 / 24 + 0.5
|
|
29
|
+
|
|
30
|
+
return header
|
|
31
|
+
|
|
32
|
+
def get_extension(self, header, arm):
|
|
33
|
+
extension = 1
|
|
34
|
+
|
|
35
|
+
return extension
|
|
36
|
+
|
|
37
|
+
def get_wavecal_filename(self, header, arm, **kwargs):
|
|
38
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
39
|
+
# info = self.load_info()
|
|
40
|
+
cwd = os.path.dirname(__file__)
|
|
41
|
+
fname = f"metis_ifu_{arm.lower()}_2D.npz"
|
|
42
|
+
# fname = f"metis_ifu_IFU_L_2D.npz" ## f"micado_IJ_2D_det1.npz"
|
|
43
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
44
|
+
|
|
45
|
+
return fname
|