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.
Files changed (182) hide show
  1. pyreduce/__init__.py +67 -0
  2. pyreduce/__main__.py +322 -0
  3. pyreduce/cli.py +342 -0
  4. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.exp +0 -0
  5. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.lib +0 -0
  6. pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.exp +0 -0
  7. pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.lib +0 -0
  8. pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.exp +0 -0
  9. pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.lib +0 -0
  10. pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.exp +0 -0
  11. pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.lib +0 -0
  12. pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
  13. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.exp +0 -0
  14. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.lib +0 -0
  15. pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.exp +0 -0
  16. pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.lib +0 -0
  17. pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.exp +0 -0
  18. pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.lib +0 -0
  19. pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.exp +0 -0
  20. pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.lib +0 -0
  21. pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
  22. pyreduce/clib/__init__.py +0 -0
  23. pyreduce/clib/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
  24. pyreduce/clib/_slitfunc_2d.cp312-win_amd64.pyd +0 -0
  25. pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
  26. pyreduce/clib/_slitfunc_2d.cp314-win_amd64.pyd +0 -0
  27. pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
  28. pyreduce/clib/_slitfunc_bd.cp312-win_amd64.pyd +0 -0
  29. pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
  30. pyreduce/clib/_slitfunc_bd.cp314-win_amd64.pyd +0 -0
  31. pyreduce/clib/build_extract.py +75 -0
  32. pyreduce/clib/slit_func_2d_xi_zeta_bd.c +1313 -0
  33. pyreduce/clib/slit_func_2d_xi_zeta_bd.h +55 -0
  34. pyreduce/clib/slit_func_bd.c +362 -0
  35. pyreduce/clib/slit_func_bd.h +17 -0
  36. pyreduce/clipnflip.py +147 -0
  37. pyreduce/combine_frames.py +861 -0
  38. pyreduce/configuration.py +191 -0
  39. pyreduce/continuum_normalization.py +329 -0
  40. pyreduce/cwrappers.py +404 -0
  41. pyreduce/datasets.py +238 -0
  42. pyreduce/echelle.py +413 -0
  43. pyreduce/estimate_background_scatter.py +130 -0
  44. pyreduce/extract.py +1362 -0
  45. pyreduce/extraction_width.py +77 -0
  46. pyreduce/instruments/__init__.py +0 -0
  47. pyreduce/instruments/aj.py +9 -0
  48. pyreduce/instruments/aj.yaml +51 -0
  49. pyreduce/instruments/andes.py +102 -0
  50. pyreduce/instruments/andes.yaml +72 -0
  51. pyreduce/instruments/common.py +711 -0
  52. pyreduce/instruments/common.yaml +57 -0
  53. pyreduce/instruments/crires_plus.py +103 -0
  54. pyreduce/instruments/crires_plus.yaml +101 -0
  55. pyreduce/instruments/filters.py +195 -0
  56. pyreduce/instruments/harpn.py +203 -0
  57. pyreduce/instruments/harpn.yaml +140 -0
  58. pyreduce/instruments/harps.py +312 -0
  59. pyreduce/instruments/harps.yaml +144 -0
  60. pyreduce/instruments/instrument_info.py +140 -0
  61. pyreduce/instruments/jwst_miri.py +29 -0
  62. pyreduce/instruments/jwst_miri.yaml +53 -0
  63. pyreduce/instruments/jwst_niriss.py +98 -0
  64. pyreduce/instruments/jwst_niriss.yaml +60 -0
  65. pyreduce/instruments/lick_apf.py +35 -0
  66. pyreduce/instruments/lick_apf.yaml +60 -0
  67. pyreduce/instruments/mcdonald.py +123 -0
  68. pyreduce/instruments/mcdonald.yaml +56 -0
  69. pyreduce/instruments/metis_ifu.py +45 -0
  70. pyreduce/instruments/metis_ifu.yaml +62 -0
  71. pyreduce/instruments/metis_lss.py +45 -0
  72. pyreduce/instruments/metis_lss.yaml +62 -0
  73. pyreduce/instruments/micado.py +45 -0
  74. pyreduce/instruments/micado.yaml +62 -0
  75. pyreduce/instruments/models.py +257 -0
  76. pyreduce/instruments/neid.py +156 -0
  77. pyreduce/instruments/neid.yaml +61 -0
  78. pyreduce/instruments/nirspec.py +215 -0
  79. pyreduce/instruments/nirspec.yaml +63 -0
  80. pyreduce/instruments/nte.py +42 -0
  81. pyreduce/instruments/nte.yaml +55 -0
  82. pyreduce/instruments/uves.py +46 -0
  83. pyreduce/instruments/uves.yaml +65 -0
  84. pyreduce/instruments/xshooter.py +39 -0
  85. pyreduce/instruments/xshooter.yaml +63 -0
  86. pyreduce/make_shear.py +607 -0
  87. pyreduce/masks/mask_crires_plus_det1.fits.gz +0 -0
  88. pyreduce/masks/mask_crires_plus_det2.fits.gz +0 -0
  89. pyreduce/masks/mask_crires_plus_det3.fits.gz +0 -0
  90. pyreduce/masks/mask_ctio_chiron.fits.gz +0 -0
  91. pyreduce/masks/mask_elodie.fits.gz +0 -0
  92. pyreduce/masks/mask_feros3.fits.gz +0 -0
  93. pyreduce/masks/mask_flames_giraffe.fits.gz +0 -0
  94. pyreduce/masks/mask_harps_blue.fits.gz +0 -0
  95. pyreduce/masks/mask_harps_red.fits.gz +0 -0
  96. pyreduce/masks/mask_hds_blue.fits.gz +0 -0
  97. pyreduce/masks/mask_hds_red.fits.gz +0 -0
  98. pyreduce/masks/mask_het_hrs_2x5.fits.gz +0 -0
  99. pyreduce/masks/mask_jwst_miri_lrs_slitless.fits.gz +0 -0
  100. pyreduce/masks/mask_jwst_niriss_gr700xd.fits.gz +0 -0
  101. pyreduce/masks/mask_lick_apf_.fits.gz +0 -0
  102. pyreduce/masks/mask_mcdonald.fits.gz +0 -0
  103. pyreduce/masks/mask_nes.fits.gz +0 -0
  104. pyreduce/masks/mask_nirspec_nirspec.fits.gz +0 -0
  105. pyreduce/masks/mask_sarg.fits.gz +0 -0
  106. pyreduce/masks/mask_sarg_2x2a.fits.gz +0 -0
  107. pyreduce/masks/mask_sarg_2x2b.fits.gz +0 -0
  108. pyreduce/masks/mask_subaru_hds_red.fits.gz +0 -0
  109. pyreduce/masks/mask_uves_blue.fits.gz +0 -0
  110. pyreduce/masks/mask_uves_blue_binned_2_2.fits.gz +0 -0
  111. pyreduce/masks/mask_uves_middle.fits.gz +0 -0
  112. pyreduce/masks/mask_uves_middle_2x2_split.fits.gz +0 -0
  113. pyreduce/masks/mask_uves_middle_binned_2_2.fits.gz +0 -0
  114. pyreduce/masks/mask_uves_red.fits.gz +0 -0
  115. pyreduce/masks/mask_uves_red_2x2.fits.gz +0 -0
  116. pyreduce/masks/mask_uves_red_2x2_split.fits.gz +0 -0
  117. pyreduce/masks/mask_uves_red_binned_2_2.fits.gz +0 -0
  118. pyreduce/masks/mask_xshooter_nir.fits.gz +0 -0
  119. pyreduce/pipeline.py +619 -0
  120. pyreduce/rectify.py +138 -0
  121. pyreduce/reduce.py +2065 -0
  122. pyreduce/settings/settings_AJ.json +19 -0
  123. pyreduce/settings/settings_ANDES.json +89 -0
  124. pyreduce/settings/settings_CRIRES_PLUS.json +89 -0
  125. pyreduce/settings/settings_HARPN.json +73 -0
  126. pyreduce/settings/settings_HARPS.json +69 -0
  127. pyreduce/settings/settings_JWST_MIRI.json +55 -0
  128. pyreduce/settings/settings_JWST_NIRISS.json +55 -0
  129. pyreduce/settings/settings_LICK_APF.json +62 -0
  130. pyreduce/settings/settings_MCDONALD.json +58 -0
  131. pyreduce/settings/settings_METIS_IFU.json +77 -0
  132. pyreduce/settings/settings_METIS_LSS.json +77 -0
  133. pyreduce/settings/settings_MICADO.json +78 -0
  134. pyreduce/settings/settings_NEID.json +73 -0
  135. pyreduce/settings/settings_NIRSPEC.json +58 -0
  136. pyreduce/settings/settings_NTE.json +60 -0
  137. pyreduce/settings/settings_UVES.json +54 -0
  138. pyreduce/settings/settings_XSHOOTER.json +78 -0
  139. pyreduce/settings/settings_pyreduce.json +184 -0
  140. pyreduce/settings/settings_schema.json +850 -0
  141. pyreduce/tools/__init__.py +0 -0
  142. pyreduce/tools/combine.py +117 -0
  143. pyreduce/trace.py +979 -0
  144. pyreduce/util.py +1366 -0
  145. pyreduce/wavecal/MICADO_HK_3arcsec_chip5.npz +0 -0
  146. pyreduce/wavecal/atlas/thar.fits +4946 -13
  147. pyreduce/wavecal/atlas/thar_list.txt +4172 -0
  148. pyreduce/wavecal/atlas/une.fits +0 -0
  149. pyreduce/wavecal/convert.py +38 -0
  150. pyreduce/wavecal/crires_plus_J1228_Open_det1.npz +0 -0
  151. pyreduce/wavecal/crires_plus_J1228_Open_det2.npz +0 -0
  152. pyreduce/wavecal/crires_plus_J1228_Open_det3.npz +0 -0
  153. pyreduce/wavecal/harpn_harpn_2D.npz +0 -0
  154. pyreduce/wavecal/harps_blue_2D.npz +0 -0
  155. pyreduce/wavecal/harps_blue_pol_2D.npz +0 -0
  156. pyreduce/wavecal/harps_red_2D.npz +0 -0
  157. pyreduce/wavecal/harps_red_pol_2D.npz +0 -0
  158. pyreduce/wavecal/mcdonald.npz +0 -0
  159. pyreduce/wavecal/metis_lss_l_2D.npz +0 -0
  160. pyreduce/wavecal/metis_lss_m_2D.npz +0 -0
  161. pyreduce/wavecal/nirspec_K2.npz +0 -0
  162. pyreduce/wavecal/uves_blue_360nm_2D.npz +0 -0
  163. pyreduce/wavecal/uves_blue_390nm_2D.npz +0 -0
  164. pyreduce/wavecal/uves_blue_437nm_2D.npz +0 -0
  165. pyreduce/wavecal/uves_middle_2x2_2D.npz +0 -0
  166. pyreduce/wavecal/uves_middle_565nm_2D.npz +0 -0
  167. pyreduce/wavecal/uves_middle_580nm_2D.npz +0 -0
  168. pyreduce/wavecal/uves_middle_600nm_2D.npz +0 -0
  169. pyreduce/wavecal/uves_middle_665nm_2D.npz +0 -0
  170. pyreduce/wavecal/uves_middle_860nm_2D.npz +0 -0
  171. pyreduce/wavecal/uves_red_580nm_2D.npz +0 -0
  172. pyreduce/wavecal/uves_red_600nm_2D.npz +0 -0
  173. pyreduce/wavecal/uves_red_665nm_2D.npz +0 -0
  174. pyreduce/wavecal/uves_red_760nm_2D.npz +0 -0
  175. pyreduce/wavecal/uves_red_860nm_2D.npz +0 -0
  176. pyreduce/wavecal/xshooter_nir.npz +0 -0
  177. pyreduce/wavelength_calibration.py +1871 -0
  178. pyreduce_astro-0.7a4.dist-info/METADATA +106 -0
  179. pyreduce_astro-0.7a4.dist-info/RECORD +182 -0
  180. pyreduce_astro-0.7a4.dist-info/WHEEL +4 -0
  181. pyreduce_astro-0.7a4.dist-info/entry_points.txt +2 -0
  182. 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