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,62 @@
|
|
|
1
|
+
# METIS IFU instrument configuration
|
|
2
|
+
|
|
3
|
+
__instrument__: METIS_IFU
|
|
4
|
+
instrument: INSTRUME
|
|
5
|
+
id_instrument: METIS_IFU
|
|
6
|
+
telescope: ELT
|
|
7
|
+
|
|
8
|
+
date: MJD-OBS
|
|
9
|
+
date_format: mjd
|
|
10
|
+
|
|
11
|
+
arms: [IFU_NOMINAL, IFU_EXTENDED]
|
|
12
|
+
kw_arm: INSTMODE
|
|
13
|
+
id_arm: [ifu_nom, ifu_ext]
|
|
14
|
+
extension: 0
|
|
15
|
+
orientation: [1, 1, 1]
|
|
16
|
+
|
|
17
|
+
prescan_x: 0
|
|
18
|
+
overscan_x: 0
|
|
19
|
+
prescan_y: 0
|
|
20
|
+
overscan_y: 0
|
|
21
|
+
naxis_x: NAXIS1
|
|
22
|
+
naxis_y: NAXIS2
|
|
23
|
+
|
|
24
|
+
gain: 1
|
|
25
|
+
readnoise: 4
|
|
26
|
+
dark: 10
|
|
27
|
+
exposure_time: "HIERARCH OBS_EXPTIME"
|
|
28
|
+
|
|
29
|
+
ra: RA
|
|
30
|
+
dec: DEC
|
|
31
|
+
jd: MJD-OBS
|
|
32
|
+
longitude: -70
|
|
33
|
+
latitude: -24
|
|
34
|
+
altitude: 3060
|
|
35
|
+
instrument_mode: "HIERARCH ESO INS MODE"
|
|
36
|
+
observation_type: "HIERARCH ESO DPR TYPE"
|
|
37
|
+
observation_category: "HIERARCH ESO DPR CATG"
|
|
38
|
+
target: ""
|
|
39
|
+
object: OBJECT
|
|
40
|
+
|
|
41
|
+
id_dark: DARK
|
|
42
|
+
id_format: "LAMP,FMTCHK"
|
|
43
|
+
id_tell: "STD,TELLURIC"
|
|
44
|
+
|
|
45
|
+
# File classification keywords and patterns
|
|
46
|
+
kw_bias: "HIERARCH ESO DPR TYPE"
|
|
47
|
+
kw_flat: "HIERARCH ESO DPR TYPE"
|
|
48
|
+
kw_curvature: "HIERARCH ESO DPR TYPE"
|
|
49
|
+
kw_scatter: "HIERARCH ESO DPR TYPE"
|
|
50
|
+
kw_orders: "HIERARCH ESO DPR TYPE"
|
|
51
|
+
kw_wave: "HIERARCH ESO DPR TYPE"
|
|
52
|
+
kw_comb: null
|
|
53
|
+
kw_spec: "HIERARCH ESO DPR TYPE"
|
|
54
|
+
|
|
55
|
+
id_bias: DARK
|
|
56
|
+
id_flat: "LAMP,FLAT"
|
|
57
|
+
id_orders: "FLAT,PINHOLE"
|
|
58
|
+
id_curvature: "SKY,WAVE"
|
|
59
|
+
id_scatter: "LAMP,FLAT"
|
|
60
|
+
id_wave: "SKY,WAVE"
|
|
61
|
+
id_comb: null
|
|
62
|
+
id_spec: OBJECT
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the METIS_LSS LSS 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_LSS(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_lss_{arm.lower()}_2D.npz"
|
|
42
|
+
# fname = f"metis_lss_LSS_L_2D.npz" ## f"micado_IJ_2D_det1.npz"
|
|
43
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
44
|
+
|
|
45
|
+
return fname
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# METIS LSS instrument configuration
|
|
2
|
+
|
|
3
|
+
__instrument__: METIS_LSS
|
|
4
|
+
instrument: INSTRUME
|
|
5
|
+
id_instrument: METIS_LSS
|
|
6
|
+
telescope: ELT
|
|
7
|
+
|
|
8
|
+
date: MJD-OBS
|
|
9
|
+
date_format: mjd
|
|
10
|
+
|
|
11
|
+
arms: [LSS_L, LSS_M, LSS_N]
|
|
12
|
+
kw_arm: INSTMODE
|
|
13
|
+
id_arm: [lss_l, lss_m, lss_n]
|
|
14
|
+
extension: 0
|
|
15
|
+
orientation: [1, 1, 1]
|
|
16
|
+
|
|
17
|
+
prescan_x: 0
|
|
18
|
+
overscan_x: 0
|
|
19
|
+
prescan_y: 0
|
|
20
|
+
overscan_y: 0
|
|
21
|
+
naxis_x: NAXIS1
|
|
22
|
+
naxis_y: NAXIS2
|
|
23
|
+
|
|
24
|
+
gain: 1
|
|
25
|
+
readnoise: 4
|
|
26
|
+
dark: 10
|
|
27
|
+
exposure_time: "HIERARCH OBS_EXPTIME"
|
|
28
|
+
|
|
29
|
+
ra: RA
|
|
30
|
+
dec: DEC
|
|
31
|
+
jd: MJD-OBS
|
|
32
|
+
longitude: -70
|
|
33
|
+
latitude: -24
|
|
34
|
+
altitude: 3060
|
|
35
|
+
instrument_mode: "HIERARCH ESO INS MODE"
|
|
36
|
+
observation_type: "HIERARCH ESO DPR TYPE"
|
|
37
|
+
observation_category: "HIERARCH ESO DPR CATG"
|
|
38
|
+
target: ""
|
|
39
|
+
object: OBJECT
|
|
40
|
+
|
|
41
|
+
id_dark: DARK
|
|
42
|
+
id_format: "LAMP,FMTCHK"
|
|
43
|
+
id_tell: "STD,TELLURIC"
|
|
44
|
+
|
|
45
|
+
# File classification keywords and patterns
|
|
46
|
+
kw_bias: "HIERARCH ESO DPR TYPE"
|
|
47
|
+
kw_flat: "HIERARCH ESO DPR TYPE"
|
|
48
|
+
kw_curvature: "HIERARCH ESO DPR TYPE"
|
|
49
|
+
kw_scatter: "HIERARCH ESO DPR TYPE"
|
|
50
|
+
kw_orders: "HIERARCH ESO DPR TYPE"
|
|
51
|
+
kw_wave: "HIERARCH ESO DPR TYPE"
|
|
52
|
+
kw_comb: null
|
|
53
|
+
kw_spec: "HIERARCH ESO DPR TYPE"
|
|
54
|
+
|
|
55
|
+
id_bias: DARK
|
|
56
|
+
id_flat: "LAMP,FLAT"
|
|
57
|
+
id_orders: "FLAT,PINHOLE"
|
|
58
|
+
id_curvature: "SKY,WAVE"
|
|
59
|
+
id_scatter: "LAMP,FLAT"
|
|
60
|
+
id_wave: "SKY,WAVE"
|
|
61
|
+
id_comb: null
|
|
62
|
+
id_spec: OBJECT
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles instrument specific info for the MICADO 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 MICADO(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 = 5
|
|
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"xshooter_{arm.lower()}.npz"
|
|
42
|
+
fname = "MICADO_HK_3arcsec_chip5.npz" ## f"micado_IJ_2D_det1.npz"
|
|
43
|
+
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
44
|
+
|
|
45
|
+
return fname
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# MICADO instrument configuration
|
|
2
|
+
|
|
3
|
+
__instrument__: MICADO
|
|
4
|
+
instrument: INSTRUME
|
|
5
|
+
id_instrument: MICADO
|
|
6
|
+
telescope: ELT
|
|
7
|
+
|
|
8
|
+
date: MJD-OBS
|
|
9
|
+
date_format: mjd
|
|
10
|
+
|
|
11
|
+
arms: [NIR]
|
|
12
|
+
kw_arm: "ESO DET CHIP*NAME"
|
|
13
|
+
id_arm: [Hawaii4RG]
|
|
14
|
+
extension: 0
|
|
15
|
+
orientation: [1]
|
|
16
|
+
|
|
17
|
+
prescan_x: 0
|
|
18
|
+
overscan_x: 0
|
|
19
|
+
prescan_y: 0
|
|
20
|
+
overscan_y: 0
|
|
21
|
+
naxis_x: NAXIS1
|
|
22
|
+
naxis_y: NAXIS2
|
|
23
|
+
|
|
24
|
+
gain: 1
|
|
25
|
+
readnoise: 4
|
|
26
|
+
dark: 10
|
|
27
|
+
exposure_time: "HIERARCH OBS_EXPTIME"
|
|
28
|
+
|
|
29
|
+
ra: RA
|
|
30
|
+
dec: DEC
|
|
31
|
+
jd: MJD-OBS
|
|
32
|
+
longitude: -70
|
|
33
|
+
latitude: -24
|
|
34
|
+
altitude: 3060
|
|
35
|
+
instrument_mode: "HIERARCH ESO SEQ ARM"
|
|
36
|
+
observation_type: "HIERARCH ESO DPR TYPE"
|
|
37
|
+
observation_category: "HIERARCH ESO DPR CATG"
|
|
38
|
+
target: ""
|
|
39
|
+
object: OBJECT
|
|
40
|
+
|
|
41
|
+
id_dark: DARK
|
|
42
|
+
id_format: WAVE_PINH
|
|
43
|
+
id_tell: "STD,TELLURIC"
|
|
44
|
+
|
|
45
|
+
# File classification keywords and patterns
|
|
46
|
+
kw_bias: "HIERARCH ESO DPR TYPE"
|
|
47
|
+
kw_flat: "HIERARCH ESO DPR TYPE"
|
|
48
|
+
kw_curvature: "HIERARCH ESO DPR TYPE"
|
|
49
|
+
kw_scatter: "HIERARCH ESO DPR TYPE"
|
|
50
|
+
kw_orders: "HIERARCH ESO DPR TYPE"
|
|
51
|
+
kw_wave: "HIERARCH ESO DPR TYPE"
|
|
52
|
+
kw_comb: null
|
|
53
|
+
kw_spec: "HIERARCH ESO DPR TYPE"
|
|
54
|
+
|
|
55
|
+
id_bias: DARK
|
|
56
|
+
id_flat: SFLATSLIT
|
|
57
|
+
id_orders: SFLAT_PINH
|
|
58
|
+
id_curvature: WAVE
|
|
59
|
+
id_scatter: "LAMP,FLAT"
|
|
60
|
+
id_wave: WAVE
|
|
61
|
+
id_comb: null
|
|
62
|
+
id_spec: OBJECT
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""Pydantic models for instrument configuration validation.
|
|
2
|
+
|
|
3
|
+
These models provide type-safe validation for instrument configuration files.
|
|
4
|
+
Currently validates the flat YAML structure; will evolve toward the nested
|
|
5
|
+
structure described in REDESIGN.md.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, field_validator
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HeaderRef(BaseModel):
|
|
16
|
+
"""Reference to a FITS header keyword."""
|
|
17
|
+
|
|
18
|
+
key: str
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(extra="forbid")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Type for values that can be either a literal or a header reference
|
|
24
|
+
HeaderOrValue = float | int | str | HeaderRef | None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FileClassification(BaseModel):
|
|
28
|
+
"""Keywords and patterns for file type classification."""
|
|
29
|
+
|
|
30
|
+
kw_bias: str | None = None
|
|
31
|
+
kw_flat: str | None = None
|
|
32
|
+
kw_curvature: str | None = None
|
|
33
|
+
kw_scatter: str | None = None
|
|
34
|
+
kw_orders: str | None = None
|
|
35
|
+
kw_wave: str | None = None
|
|
36
|
+
kw_comb: str | None = None
|
|
37
|
+
kw_spec: str | None = None
|
|
38
|
+
|
|
39
|
+
id_bias: str | None = None
|
|
40
|
+
id_flat: str | None = None
|
|
41
|
+
id_curvature: str | None = None
|
|
42
|
+
id_scatter: str | None = None
|
|
43
|
+
id_orders: str | None = None
|
|
44
|
+
id_wave: str | None = None
|
|
45
|
+
id_comb: str | None = None
|
|
46
|
+
id_spec: str | None = None
|
|
47
|
+
|
|
48
|
+
model_config = ConfigDict(extra="allow")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class InstrumentConfig(BaseModel):
|
|
52
|
+
"""Configuration for an astronomical instrument.
|
|
53
|
+
|
|
54
|
+
This model validates the flat YAML structure used by instrument configs.
|
|
55
|
+
It allows extra fields for instrument-specific parameters.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
# Required identification
|
|
59
|
+
__instrument__: str | None = None # Internal name (uses alias due to dunder)
|
|
60
|
+
instrument: str # Header keyword for instrument name
|
|
61
|
+
id_instrument: str # Value/pattern to match
|
|
62
|
+
|
|
63
|
+
# Telescope
|
|
64
|
+
telescope: str | None = None
|
|
65
|
+
|
|
66
|
+
# Date handling
|
|
67
|
+
date: str = "DATE-OBS"
|
|
68
|
+
date_format: str = "fits"
|
|
69
|
+
|
|
70
|
+
# Arm system (detectors/channels)
|
|
71
|
+
arms: list[str] | None = None
|
|
72
|
+
arms_id: list[str] | None = None
|
|
73
|
+
kw_arm: str | None = None
|
|
74
|
+
id_arm: list[str] | None = None
|
|
75
|
+
extension: int | str | list[int | str] = 0
|
|
76
|
+
orientation: int | list[int] = 0
|
|
77
|
+
transpose: bool = False
|
|
78
|
+
|
|
79
|
+
# Detector dimensions
|
|
80
|
+
naxis_x: str | int = "NAXIS1"
|
|
81
|
+
naxis_y: str | int = "NAXIS2"
|
|
82
|
+
|
|
83
|
+
# Overscan/prescan regions
|
|
84
|
+
prescan_x: int | str = 0
|
|
85
|
+
overscan_x: int | str = 0
|
|
86
|
+
prescan_y: int | str = 0
|
|
87
|
+
overscan_y: int | str = 0
|
|
88
|
+
|
|
89
|
+
# Calibration values (can be literals or header keywords)
|
|
90
|
+
gain: float | int | str = 1
|
|
91
|
+
readnoise: float | int | str = 0
|
|
92
|
+
dark: float | int | str = 0
|
|
93
|
+
sky: float | int | str = 0
|
|
94
|
+
exposure_time: str = "EXPTIME"
|
|
95
|
+
|
|
96
|
+
# Location (for barycentric correction)
|
|
97
|
+
ra: str | None = "RA"
|
|
98
|
+
dec: str | None = "DEC"
|
|
99
|
+
longitude: float | str | None = None
|
|
100
|
+
latitude: float | str | None = None
|
|
101
|
+
altitude: float | str | None = None
|
|
102
|
+
|
|
103
|
+
# Target identification
|
|
104
|
+
target: str = "OBJECT"
|
|
105
|
+
observation_type: str | None = None
|
|
106
|
+
category: str | None = None
|
|
107
|
+
image_type: str | None = None
|
|
108
|
+
instrument_mode: str | None = None
|
|
109
|
+
|
|
110
|
+
# File classification - header keywords
|
|
111
|
+
kw_bias: str | None = None
|
|
112
|
+
kw_flat: str | None = None
|
|
113
|
+
kw_curvature: str | None = None
|
|
114
|
+
kw_scatter: str | None = None
|
|
115
|
+
kw_orders: str | None = None
|
|
116
|
+
kw_wave: str | None = None
|
|
117
|
+
kw_comb: str | None = None
|
|
118
|
+
kw_spec: str | None = None
|
|
119
|
+
|
|
120
|
+
# File classification - identifier patterns
|
|
121
|
+
id_bias: str | None = None
|
|
122
|
+
id_flat: str | None = None
|
|
123
|
+
id_curvature: str | None = None
|
|
124
|
+
id_scatter: str | None = None
|
|
125
|
+
id_orders: str | None = None
|
|
126
|
+
id_wave: str | None = None
|
|
127
|
+
id_comb: str | None = None
|
|
128
|
+
id_spec: str | None = None
|
|
129
|
+
|
|
130
|
+
# Wavelength information
|
|
131
|
+
wavelength_range: list | None = None
|
|
132
|
+
wavecal_specifier: str | None = None
|
|
133
|
+
|
|
134
|
+
# Allow additional fields for instrument-specific parameters
|
|
135
|
+
model_config = ConfigDict(
|
|
136
|
+
extra="allow",
|
|
137
|
+
populate_by_name=True,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
@field_validator("arms", "arms_id", "id_arm", mode="before")
|
|
141
|
+
@classmethod
|
|
142
|
+
def ensure_list(cls, v):
|
|
143
|
+
"""Convert single values to lists."""
|
|
144
|
+
if v is None:
|
|
145
|
+
return None
|
|
146
|
+
if isinstance(v, str):
|
|
147
|
+
return [v]
|
|
148
|
+
return v
|
|
149
|
+
|
|
150
|
+
@field_validator("extension", "orientation", mode="before")
|
|
151
|
+
@classmethod
|
|
152
|
+
def normalize_list_or_scalar(cls, v):
|
|
153
|
+
"""Keep as-is, validation handles both forms."""
|
|
154
|
+
return v
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# Future models for the nested structure (REDESIGN.md)
|
|
158
|
+
# These will be used when migrating to the new architecture
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class AmplifierConfig(BaseModel):
|
|
162
|
+
"""Configuration for a detector readout amplifier."""
|
|
163
|
+
|
|
164
|
+
id: str
|
|
165
|
+
gain: float | dict[str, str] # literal or {key: "HEADER_KEY"}
|
|
166
|
+
readnoise: float | dict[str, str]
|
|
167
|
+
region: dict[str, list[int]] | None = None
|
|
168
|
+
linearity: dict | None = None
|
|
169
|
+
bad_pixel_mask: str | None = None
|
|
170
|
+
|
|
171
|
+
model_config = ConfigDict(extra="forbid")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class DetectorConfig(BaseModel):
|
|
175
|
+
"""Configuration for a physical detector."""
|
|
176
|
+
|
|
177
|
+
name: str
|
|
178
|
+
naxis: tuple[int, int] | list[int]
|
|
179
|
+
orientation: int = 0
|
|
180
|
+
prescan: dict[str, list[int] | None] | None = None
|
|
181
|
+
overscan: dict[str, list[int] | None] | None = None
|
|
182
|
+
amplifiers: list[AmplifierConfig] = []
|
|
183
|
+
bad_pixel_mask: str | None = None
|
|
184
|
+
|
|
185
|
+
model_config = ConfigDict(extra="forbid")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class BeamArmConfig(BaseModel):
|
|
189
|
+
"""Configuration for a beam-splitter arm."""
|
|
190
|
+
|
|
191
|
+
name: str
|
|
192
|
+
polarization: str | None = None
|
|
193
|
+
wavelength_shift: float = 0.0
|
|
194
|
+
trace_offset: float = 0.0
|
|
195
|
+
|
|
196
|
+
model_config = ConfigDict(extra="forbid")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class OpticalPathConfig(BaseModel):
|
|
200
|
+
"""Configuration for an optical path (fiber, slit position)."""
|
|
201
|
+
|
|
202
|
+
name: str
|
|
203
|
+
beam_arms: list[BeamArmConfig] | None = None
|
|
204
|
+
|
|
205
|
+
model_config = ConfigDict(extra="forbid")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class DimensionConfig(BaseModel):
|
|
209
|
+
"""Configuration for a varying dimension (mode, fiber, etc.)."""
|
|
210
|
+
|
|
211
|
+
values: list[str]
|
|
212
|
+
header_key: str | None = None
|
|
213
|
+
optional: bool = False
|
|
214
|
+
|
|
215
|
+
model_config = ConfigDict(extra="forbid")
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class InstrumentConfigV2(BaseModel):
|
|
219
|
+
"""Future nested instrument configuration structure.
|
|
220
|
+
|
|
221
|
+
This model represents the target architecture from REDESIGN.md.
|
|
222
|
+
Not yet used - will be activated when migrating instruments.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
instrument: str
|
|
226
|
+
telescope: str | None = None
|
|
227
|
+
id_instrument: str
|
|
228
|
+
|
|
229
|
+
detectors: list[DetectorConfig] = []
|
|
230
|
+
optical_paths: list[OpticalPathConfig] = []
|
|
231
|
+
dimensions: dict[str, DimensionConfig] = {}
|
|
232
|
+
|
|
233
|
+
headers: dict[str, str] = {}
|
|
234
|
+
file_types: dict[str, dict[str, str]] = {}
|
|
235
|
+
|
|
236
|
+
model_config = ConfigDict(extra="allow")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def validate_instrument_config(data: dict[str, Any]) -> InstrumentConfig:
|
|
240
|
+
"""Validate instrument configuration data.
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
data : dict
|
|
245
|
+
Raw configuration data (from YAML or JSON)
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
InstrumentConfig
|
|
250
|
+
Validated configuration
|
|
251
|
+
|
|
252
|
+
Raises
|
|
253
|
+
------
|
|
254
|
+
pydantic.ValidationError
|
|
255
|
+
If validation fails
|
|
256
|
+
"""
|
|
257
|
+
return InstrumentConfig(**data)
|
|
@@ -0,0 +1,156 @@
|
|
|
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.config.instrument),
|
|
20
|
+
"night": NightFilter(self.config.date),
|
|
21
|
+
# "branch": Filter(, regex=True),
|
|
22
|
+
"mode": Filter(
|
|
23
|
+
self.config.instrument_mode, regex=True, flags=re.IGNORECASE
|
|
24
|
+
),
|
|
25
|
+
"type": Filter(self.config.observation_type),
|
|
26
|
+
"target": ObjectFilter(self.config.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(
|
|
45
|
+
self, target, night, arm=None, mode=None, fiber=None, **kwargs
|
|
46
|
+
):
|
|
47
|
+
"""Determine the default expected values in the headers for a given observation configuration
|
|
48
|
+
|
|
49
|
+
Any parameter may be None, to indicate that all values are allowed
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
target : str
|
|
54
|
+
Name of the star / observation target
|
|
55
|
+
night : str
|
|
56
|
+
Observation night/nights
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
expectations: dict
|
|
60
|
+
Dictionary of expected header values, with one entry per step.
|
|
61
|
+
The entries for each step refer to the filters defined in self.filters
|
|
62
|
+
|
|
63
|
+
Raises
|
|
64
|
+
------
|
|
65
|
+
ValueError
|
|
66
|
+
Invalid combination of parameters
|
|
67
|
+
"""
|
|
68
|
+
if target is not None:
|
|
69
|
+
target = target.replace(" ", r"(?:\s*|-)")
|
|
70
|
+
else:
|
|
71
|
+
target = ".*"
|
|
72
|
+
|
|
73
|
+
id_orddef = "LAMP,DARK,TUN"
|
|
74
|
+
id_spec = "STAR,WAVE"
|
|
75
|
+
|
|
76
|
+
expectations = {
|
|
77
|
+
"flat": {"instrument": "NEID", "night": night, "type": r"LAMP,LAMP,TUN"},
|
|
78
|
+
"orders": {
|
|
79
|
+
"instrument": "NEID",
|
|
80
|
+
"night": night,
|
|
81
|
+
"type": id_orddef,
|
|
82
|
+
},
|
|
83
|
+
"scatter": {
|
|
84
|
+
"instrument": "NEID",
|
|
85
|
+
"night": night,
|
|
86
|
+
"type": id_orddef, # Same as orders or same as flat?
|
|
87
|
+
},
|
|
88
|
+
"wavecal_master": {
|
|
89
|
+
"instrument": "NEID",
|
|
90
|
+
"night": night,
|
|
91
|
+
"type": r"WAVE,WAVE,THAR2",
|
|
92
|
+
},
|
|
93
|
+
"freq_comb_master": {
|
|
94
|
+
"instrument": "NEID",
|
|
95
|
+
"night": night,
|
|
96
|
+
"type": r"WAVE,WAVE,COMB",
|
|
97
|
+
},
|
|
98
|
+
"science": {
|
|
99
|
+
"instrument": "NEID",
|
|
100
|
+
"night": night,
|
|
101
|
+
"mode": mode,
|
|
102
|
+
"type": id_spec,
|
|
103
|
+
"target": target,
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
return expectations
|
|
107
|
+
|
|
108
|
+
def get_extension(self, header, arm):
|
|
109
|
+
extension = super().get_extension(header, arm)
|
|
110
|
+
|
|
111
|
+
return extension
|
|
112
|
+
|
|
113
|
+
def add_header_info(self, header, arm, **kwargs):
|
|
114
|
+
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
115
|
+
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
116
|
+
# alternatively you can implement all of it here, whatever works
|
|
117
|
+
header = super().add_header_info(header, arm)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
header["e_ra"] /= 15
|
|
121
|
+
header["e_jd"] += header["e_exptim"] / (7200 * 24) + 0.5
|
|
122
|
+
|
|
123
|
+
except:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
if (
|
|
128
|
+
header["NAXIS"] == 2
|
|
129
|
+
and header["NAXIS1"] == 4296
|
|
130
|
+
and header["NAXIS2"] == 4096
|
|
131
|
+
):
|
|
132
|
+
# both arms are in the same image
|
|
133
|
+
prescan_x = 50
|
|
134
|
+
overscan_x = 50
|
|
135
|
+
naxis_x = 2148
|
|
136
|
+
if arm == "BLUE":
|
|
137
|
+
header["e_xlo"] = prescan_x
|
|
138
|
+
header["e_xhi"] = naxis_x - overscan_x
|
|
139
|
+
elif arm == "RED":
|
|
140
|
+
header["e_xlo"] = naxis_x + prescan_x
|
|
141
|
+
header["e_xhi"] = 2 * naxis_x - overscan_x
|
|
142
|
+
except KeyError:
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
return header
|
|
146
|
+
|
|
147
|
+
def get_wavecal_filename(self, header, arm, **kwargs):
|
|
148
|
+
"""Get the filename of the wavelength calibration config file"""
|
|
149
|
+
cwd = dirname(__file__)
|
|
150
|
+
fname = f"NEID_{arm.lower()}_2D.npz"
|
|
151
|
+
fname = join(cwd, "..", "wavecal", fname)
|
|
152
|
+
return fname
|
|
153
|
+
|
|
154
|
+
def get_wavelength_range(self, header, arm, **kwargs):
|
|
155
|
+
wave_range = super().get_wavelength_range(header, arm, **kwargs)
|
|
156
|
+
return wave_range
|