pyreduce-astro 0.6.0b1__py3-none-any.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 (151) hide show
  1. pyreduce/__init__.py +58 -0
  2. pyreduce/__main__.py +106 -0
  3. pyreduce/clib/__init__.py +0 -0
  4. pyreduce/clib/_slitfunc_2d.cpython-313-darwin.so +0 -0
  5. pyreduce/clib/_slitfunc_bd.cpython-313-darwin.so +0 -0
  6. pyreduce/clib/build_extract.py +75 -0
  7. pyreduce/clib/slit_func_2d_xi_zeta_bd.c +1313 -0
  8. pyreduce/clib/slit_func_2d_xi_zeta_bd.h +55 -0
  9. pyreduce/clib/slit_func_bd.c +362 -0
  10. pyreduce/clib/slit_func_bd.h +17 -0
  11. pyreduce/clipnflip.py +147 -0
  12. pyreduce/combine_frames.py +855 -0
  13. pyreduce/configuration.py +186 -0
  14. pyreduce/continuum_normalization.py +329 -0
  15. pyreduce/cwrappers.py +404 -0
  16. pyreduce/datasets.py +231 -0
  17. pyreduce/echelle.py +413 -0
  18. pyreduce/estimate_background_scatter.py +129 -0
  19. pyreduce/extract.py +1359 -0
  20. pyreduce/extraction_width.py +77 -0
  21. pyreduce/instruments/__init__.py +0 -0
  22. pyreduce/instruments/andes.json +59 -0
  23. pyreduce/instruments/andes.py +100 -0
  24. pyreduce/instruments/common.json +46 -0
  25. pyreduce/instruments/common.py +675 -0
  26. pyreduce/instruments/crires_plus.json +63 -0
  27. pyreduce/instruments/crires_plus.py +103 -0
  28. pyreduce/instruments/filters.py +195 -0
  29. pyreduce/instruments/harpn.json +136 -0
  30. pyreduce/instruments/harpn.py +201 -0
  31. pyreduce/instruments/harps.json +155 -0
  32. pyreduce/instruments/harps.py +310 -0
  33. pyreduce/instruments/instrument_info.py +140 -0
  34. pyreduce/instruments/instrument_schema.json +221 -0
  35. pyreduce/instruments/jwst_miri.json +53 -0
  36. pyreduce/instruments/jwst_miri.py +29 -0
  37. pyreduce/instruments/jwst_niriss.json +52 -0
  38. pyreduce/instruments/jwst_niriss.py +98 -0
  39. pyreduce/instruments/lick_apf.json +53 -0
  40. pyreduce/instruments/lick_apf.py +35 -0
  41. pyreduce/instruments/mcdonald.json +59 -0
  42. pyreduce/instruments/mcdonald.py +123 -0
  43. pyreduce/instruments/metis_ifu.json +63 -0
  44. pyreduce/instruments/metis_ifu.py +45 -0
  45. pyreduce/instruments/metis_lss.json +65 -0
  46. pyreduce/instruments/metis_lss.py +45 -0
  47. pyreduce/instruments/micado.json +53 -0
  48. pyreduce/instruments/micado.py +45 -0
  49. pyreduce/instruments/neid.json +51 -0
  50. pyreduce/instruments/neid.py +154 -0
  51. pyreduce/instruments/nirspec.json +56 -0
  52. pyreduce/instruments/nirspec.py +215 -0
  53. pyreduce/instruments/nte.json +47 -0
  54. pyreduce/instruments/nte.py +42 -0
  55. pyreduce/instruments/uves.json +59 -0
  56. pyreduce/instruments/uves.py +46 -0
  57. pyreduce/instruments/xshooter.json +66 -0
  58. pyreduce/instruments/xshooter.py +39 -0
  59. pyreduce/make_shear.py +606 -0
  60. pyreduce/masks/mask_crires_plus_det1.fits.gz +0 -0
  61. pyreduce/masks/mask_crires_plus_det2.fits.gz +0 -0
  62. pyreduce/masks/mask_crires_plus_det3.fits.gz +0 -0
  63. pyreduce/masks/mask_ctio_chiron.fits.gz +0 -0
  64. pyreduce/masks/mask_elodie.fits.gz +0 -0
  65. pyreduce/masks/mask_feros3.fits.gz +0 -0
  66. pyreduce/masks/mask_flames_giraffe.fits.gz +0 -0
  67. pyreduce/masks/mask_harps_blue.fits.gz +0 -0
  68. pyreduce/masks/mask_harps_red.fits.gz +0 -0
  69. pyreduce/masks/mask_hds_blue.fits.gz +0 -0
  70. pyreduce/masks/mask_hds_red.fits.gz +0 -0
  71. pyreduce/masks/mask_het_hrs_2x5.fits.gz +0 -0
  72. pyreduce/masks/mask_jwst_miri_lrs_slitless.fits.gz +0 -0
  73. pyreduce/masks/mask_jwst_niriss_gr700xd.fits.gz +0 -0
  74. pyreduce/masks/mask_lick_apf_.fits.gz +0 -0
  75. pyreduce/masks/mask_mcdonald.fits.gz +0 -0
  76. pyreduce/masks/mask_nes.fits.gz +0 -0
  77. pyreduce/masks/mask_nirspec_nirspec.fits.gz +0 -0
  78. pyreduce/masks/mask_sarg.fits.gz +0 -0
  79. pyreduce/masks/mask_sarg_2x2a.fits.gz +0 -0
  80. pyreduce/masks/mask_sarg_2x2b.fits.gz +0 -0
  81. pyreduce/masks/mask_subaru_hds_red.fits.gz +0 -0
  82. pyreduce/masks/mask_uves_blue.fits.gz +0 -0
  83. pyreduce/masks/mask_uves_blue_binned_2_2.fits.gz +0 -0
  84. pyreduce/masks/mask_uves_middle.fits.gz +0 -0
  85. pyreduce/masks/mask_uves_middle_2x2_split.fits.gz +0 -0
  86. pyreduce/masks/mask_uves_middle_binned_2_2.fits.gz +0 -0
  87. pyreduce/masks/mask_uves_red.fits.gz +0 -0
  88. pyreduce/masks/mask_uves_red_2x2.fits.gz +0 -0
  89. pyreduce/masks/mask_uves_red_2x2_split.fits.gz +0 -0
  90. pyreduce/masks/mask_uves_red_binned_2_2.fits.gz +0 -0
  91. pyreduce/masks/mask_xshooter_nir.fits.gz +0 -0
  92. pyreduce/rectify.py +138 -0
  93. pyreduce/reduce.py +2205 -0
  94. pyreduce/settings/settings_CRIRES_PLUS.json +89 -0
  95. pyreduce/settings/settings_HARPN.json +73 -0
  96. pyreduce/settings/settings_HARPS.json +69 -0
  97. pyreduce/settings/settings_JWST_MIRI.json +55 -0
  98. pyreduce/settings/settings_JWST_NIRISS.json +55 -0
  99. pyreduce/settings/settings_LICK_APF.json +62 -0
  100. pyreduce/settings/settings_MCDONALD.json +58 -0
  101. pyreduce/settings/settings_METIS_IFU.json +77 -0
  102. pyreduce/settings/settings_METIS_LSS.json +77 -0
  103. pyreduce/settings/settings_MICADO.json +78 -0
  104. pyreduce/settings/settings_NEID.json +73 -0
  105. pyreduce/settings/settings_NIRSPEC.json +58 -0
  106. pyreduce/settings/settings_NTE.json +60 -0
  107. pyreduce/settings/settings_UVES.json +54 -0
  108. pyreduce/settings/settings_XSHOOTER.json +78 -0
  109. pyreduce/settings/settings_pyreduce.json +178 -0
  110. pyreduce/settings/settings_schema.json +827 -0
  111. pyreduce/tools/__init__.py +0 -0
  112. pyreduce/tools/combine.py +117 -0
  113. pyreduce/trace_orders.py +645 -0
  114. pyreduce/util.py +1288 -0
  115. pyreduce/wavecal/MICADO_HK_3arcsec_chip5.npz +0 -0
  116. pyreduce/wavecal/atlas/thar.fits +4946 -13
  117. pyreduce/wavecal/atlas/thar_list.txt +4172 -0
  118. pyreduce/wavecal/atlas/une.fits +0 -0
  119. pyreduce/wavecal/convert.py +38 -0
  120. pyreduce/wavecal/crires_plus_J1228_Open_det1.npz +0 -0
  121. pyreduce/wavecal/crires_plus_J1228_Open_det2.npz +0 -0
  122. pyreduce/wavecal/crires_plus_J1228_Open_det3.npz +0 -0
  123. pyreduce/wavecal/harpn_harpn_2D.npz +0 -0
  124. pyreduce/wavecal/harps_blue_2D.npz +0 -0
  125. pyreduce/wavecal/harps_blue_pol_2D.npz +0 -0
  126. pyreduce/wavecal/harps_red_2D.npz +0 -0
  127. pyreduce/wavecal/harps_red_pol_2D.npz +0 -0
  128. pyreduce/wavecal/mcdonald.npz +0 -0
  129. pyreduce/wavecal/metis_lss_l_2D.npz +0 -0
  130. pyreduce/wavecal/metis_lss_m_2D.npz +0 -0
  131. pyreduce/wavecal/nirspec_K2.npz +0 -0
  132. pyreduce/wavecal/uves_blue_360nm_2D.npz +0 -0
  133. pyreduce/wavecal/uves_blue_390nm_2D.npz +0 -0
  134. pyreduce/wavecal/uves_blue_437nm_2D.npz +0 -0
  135. pyreduce/wavecal/uves_middle_2x2_2D.npz +0 -0
  136. pyreduce/wavecal/uves_middle_565nm_2D.npz +0 -0
  137. pyreduce/wavecal/uves_middle_580nm_2D.npz +0 -0
  138. pyreduce/wavecal/uves_middle_600nm_2D.npz +0 -0
  139. pyreduce/wavecal/uves_middle_665nm_2D.npz +0 -0
  140. pyreduce/wavecal/uves_middle_860nm_2D.npz +0 -0
  141. pyreduce/wavecal/uves_red_580nm_2D.npz +0 -0
  142. pyreduce/wavecal/uves_red_600nm_2D.npz +0 -0
  143. pyreduce/wavecal/uves_red_665nm_2D.npz +0 -0
  144. pyreduce/wavecal/uves_red_760nm_2D.npz +0 -0
  145. pyreduce/wavecal/uves_red_860nm_2D.npz +0 -0
  146. pyreduce/wavecal/xshooter_nir.npz +0 -0
  147. pyreduce/wavelength_calibration.py +1873 -0
  148. pyreduce_astro-0.6.0b1.dist-info/METADATA +112 -0
  149. pyreduce_astro-0.6.0b1.dist-info/RECORD +151 -0
  150. pyreduce_astro-0.6.0b1.dist-info/WHEEL +4 -0
  151. pyreduce_astro-0.6.0b1.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,77 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ from .util import make_index
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def estimate_extraction_width(
11
+ img, orders, column_range, plot=False
12
+ ): # pragma: no cover
13
+ raise NotImplementedError
14
+ nrow, ncol = img.shape
15
+ nord, _ = orders.shape
16
+ extraction_width = np.zeros((nord, 2), dtype=int)
17
+
18
+ for i in range(nord):
19
+ # first guess, half way to the next order
20
+ # To order above
21
+ if i < nord - 1:
22
+ beg = max(column_range[[i, i + 1], 0])
23
+ end = min(column_range[[i, i + 1], 1])
24
+ x = np.arange(beg, end)
25
+ y = np.polyval(orders[i], x)
26
+ y_above = np.polyval(orders[i + 1], x)
27
+ width_above = int(np.mean(y_above - y) // 2)
28
+
29
+ # To order below
30
+ if i > 0:
31
+ beg = max(column_range[[i - 1, i], 0])
32
+ end = min(column_range[[i - 1, i], 1])
33
+ x = np.arange(beg, end)
34
+ y = np.polyval(orders[i], x)
35
+ y_below = np.polyval(orders[i - 1], x)
36
+ width_below = int(np.mean(y - y_below) // 2)
37
+ else:
38
+ width_below = width_above
39
+
40
+ if i == nord - 1:
41
+ width_above = width_below
42
+
43
+ beg, end = column_range[i]
44
+ x = np.arange(beg, end)
45
+ y = np.polyval(orders[i], x)
46
+
47
+ y_int = y.astype(int)
48
+ y_above = y_int + width_above
49
+ y_below = y_int - width_below
50
+
51
+ if np.ma.any(y_above >= nrow) or np.ma.any(y_below < 0):
52
+ beg, end = np.where((y_above < nrow) & (y_below >= 0))[0][[0, -1]]
53
+
54
+ index = make_index(y_int - width_below, y_int + width_above, beg, end, True)
55
+
56
+ np.ma.sum(img[index], axis=1)
57
+
58
+ # TODO fit rectangular profile
59
+
60
+ # width = int(4 * coef[2])
61
+
62
+ # plt.plot(p, slitf)
63
+ # plt.plot(p, gaussval(p, *coef))
64
+ # plt.vlines([coef[1] - width, coef[1] + width], slitf.min(), slitf.max())
65
+ # plt.show()
66
+
67
+ # width_below = min(width_below, width)
68
+ # width_above = min(width_above, width)
69
+
70
+ # index = make_index(y_int - width_below, y_int + width_above, beg, end, True)
71
+
72
+ # plt.imshow(np.log(img[index]), aspect="auto", origin="lower")
73
+ # plt.show()
74
+ width = 0.5
75
+ extraction_width[i] = [width, width]
76
+
77
+ return extraction_width
File without changes
@@ -0,0 +1,59 @@
1
+ {
2
+ "__comment__": "red and middle are in the same fits file, with different extensions, i.e. share the same mode identifier, but have different extensions",
3
+ "__instrument__": "ANDES",
4
+ "instrument": "INSTRUME",
5
+ "id_instrument": "ANDES",
6
+ "telescope": "VLT",
7
+ "date": "DATE-OBS",
8
+ "date_format": "fits",
9
+ "id_mode": "ESO INS MODE",
10
+ "id_band": "ESO INS WLEN ID",
11
+ "id_lamp": "ESO INS1 LAMP? ID",
12
+ "modes": ["SL", "IFU"],
13
+ "bands": ["UBV", "RIZ", "YJH", "K"],
14
+ "settings": ["B", "V", "R", "IZ", "Y", "J", "H", "K"],
15
+ "chips": ["det1"],
16
+ "extension": "CHIP1.INT1",
17
+ "orientation": 0,
18
+ "transpose": false,
19
+ "prescan_x": 5,
20
+ "overscan_x": 5,
21
+ "prescan_y": 5,
22
+ "overscan_y": 5,
23
+ "naxis_x": "NAXIS1",
24
+ "naxis_y": "NAXIS2",
25
+ "gain": "HIERARCH ESO DET CHIP GAIN",
26
+ "readnoise": "HIERARCH ESO DET CHIP RON",
27
+ "dark": "HIERARCH ESO DET DIT",
28
+ "sky": 0,
29
+ "exposure_time": "EXPTIME",
30
+ "image_type": "OBJECT",
31
+ "category": "HIERARCH ESO DPR CATG",
32
+ "ra": "RA",
33
+ "dec": "DEC",
34
+ "jd": "MJD-OBS",
35
+ "longitude": "HIERARCH ESO TEL GEOLON",
36
+ "latitude": "HIERARCH ESO TEL GEOLAT",
37
+ "altitude": "HIERARCH ESO TEL GEOELEV",
38
+ "target": "OBJECT",
39
+ "observation_type": "ESO DPR TYPE",
40
+
41
+ "kw_bias" : "ESO DPR TYPE",
42
+ "kw_flat" : "ESO DPR TYPE",
43
+ "kw_curvature": "ESO DPR TYPE",
44
+ "kw_scatter": "ESO DPR TYPE",
45
+ "kw_orders" : "ESO DPR TYPE",
46
+ "kw_wave": "ESO DPR TYPE",
47
+ "kw_comb": "ESO DPR TYPE",
48
+ "kw_spec": "ESO DPR TYPE",
49
+ "id_bias": "DARK",
50
+ "id_flat": "FLAT",
51
+ "id_orders": "FLAT",
52
+ "id_curvature": "WAVE,FPET",
53
+ "id_scatter": "FLAT",
54
+ "id_wave": "WAVE,UNE",
55
+ "id_comb": "WAVE,FPET",
56
+ "id_spec": "STAR,*,*",
57
+ "id_lamp_wavecal" : "UNe_HCL",
58
+ "id_lamp_etalon": "Etalon_Halogen"
59
+ }
@@ -0,0 +1,100 @@
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
+ from itertools import product
11
+
12
+ import numpy as np
13
+
14
+ from .common import Instrument
15
+ from .filters import Filter
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ANDES(Instrument):
21
+ def __init__(self):
22
+ super().__init__()
23
+ self.filters["lamp"] = Filter(self.info["id_lamp"])
24
+ self.filters["band"] = Filter(self.info["id_band"])
25
+ self.filters["decker"] = Filter(self.info["id_decker"])
26
+ self.shared += ["band", "decker"]
27
+
28
+ def add_header_info(self, header, mode, **kwargs):
29
+ """read data from header and add it as REDUCE keyword back to the header"""
30
+ # "Normal" stuff is handled by the general version, specific changes to values happen here
31
+ # alternatively you can implement all of it here, whatever works
32
+ band, decker, detector = self.parse_mode(mode)
33
+ header = super().add_header_info(header, band)
34
+ self.load_info()
35
+
36
+ return header
37
+
38
+ def get_supported_modes(self):
39
+ settings = self.info["settings"]
40
+ deckers = self.info["deckers"]
41
+ detectors = self.info["chips"]
42
+ modes = [
43
+ "_".join([s, d, c]) for s, d, c in product(settings, deckers, detectors)
44
+ ]
45
+ return modes
46
+
47
+ def parse_mode(self, mode):
48
+ pattern = r"([YJHKLM]\d{4})(_(Open|pos1|pos2))?_det(\d)"
49
+ match = re.match(pattern, mode, flags=re.IGNORECASE)
50
+ band = match.group(1).upper()
51
+ if match.group(3) is not None:
52
+ decker = match.group(3).lower().capitalize()
53
+ else:
54
+ decker = "Open"
55
+ detector = match.group(4)
56
+ return band, decker, detector
57
+
58
+ def get_expected_values(self, target, night, mode):
59
+ expectations = super().get_expected_values(target, night)
60
+ band, decker, detector = self.parse_mode(mode)
61
+
62
+ for key in expectations.keys():
63
+ if key == "bias":
64
+ continue
65
+ expectations[key]["band"] = band
66
+ expectations[key]["decker"] = decker
67
+
68
+ return expectations
69
+
70
+ def get_extension(self, header, mode):
71
+ band, decker, detector = self.parse_mode(mode)
72
+ extension = int(detector)
73
+ return extension
74
+
75
+ def get_wavecal_filename(self, header, mode, **kwargs):
76
+ """Get the filename of the wavelength calibration config file"""
77
+ cwd = os.path.dirname(__file__)
78
+ fname = f"{self.name}_{mode}.npz"
79
+ fname = os.path.join(cwd, "..", "wavecal", fname)
80
+ return fname
81
+
82
+ def get_mask_filename(self, mode, **kwargs):
83
+ i = self.name.lower()
84
+ band, decker, detector = self.parse_mode(mode)
85
+
86
+ fname = f"mask_{i}_det{detector}.fits.gz"
87
+ cwd = os.path.dirname(__file__)
88
+ fname = os.path.join(cwd, "..", "masks", fname)
89
+ return fname
90
+
91
+ def get_wavelength_range(self, header, mode, **kwargs):
92
+ wmin = [header["ESO INS WLEN MIN%i" % i] for i in range(1, 11)]
93
+ wmax = [header["ESO INS WLEN MAX%i" % i] for i in range(1, 11)]
94
+
95
+ wavelength_range = np.array([wmin, wmax]).T
96
+ # Invert the order numbering
97
+ wavelength_range = wavelength_range[::-1]
98
+ # Convert from nm to Angstrom
99
+ wavelength_range *= 10
100
+ return wavelength_range
@@ -0,0 +1,46 @@
1
+ {
2
+ "__instrument__": "COMMON",
3
+ "instrument": "INSTRUME",
4
+ "id_instrument": "COMMON",
5
+ "telescope": "TELESCOP",
6
+ "target": "OBJECT",
7
+ "date": "DATE-OBS",
8
+ "date_format": "fits",
9
+ "extension": 0,
10
+ "orientation": 0,
11
+ "transpose": false,
12
+ "prescan_x": 0,
13
+ "overscan_x": 0,
14
+ "prescan_y": 0,
15
+ "overscan_y": 0,
16
+ "naxis_x": "NAXIS1",
17
+ "naxis_y": "NAXIS2",
18
+ "gain": 1,
19
+ "readnoise": 0,
20
+ "dark": 0,
21
+ "sky": 0,
22
+ "exposure_time": "EXPTIME",
23
+ "ra": "RA",
24
+ "dec": "DEC",
25
+ "longitude": null,
26
+ "latitude": null,
27
+ "altitude": null,
28
+ "__comment__": "These define the keywords we look for, and the values they should have in the fits header, to be sorted into that category",
29
+ "kw_bias" : "",
30
+ "kw_flat" : "",
31
+ "kw_curvature": "",
32
+ "kw_scatter": "",
33
+ "kw_orders" : "",
34
+ "kw_wave": "",
35
+ "kw_comb": "",
36
+ "kw_spec": "",
37
+ "id_bias" : "BIAS",
38
+ "id_flat": "FLAT",
39
+ "id_orders": "ORDER",
40
+ "id_curvature": "CURV",
41
+ "id_scatter": "SCATTER",
42
+ "id_wave": "WAVE",
43
+ "id_comb": "COMB",
44
+ "id_spec": "SPEC",
45
+ "wavelength_range": null
46
+ }