pyreduce-astro 0.6.0b5__cp311-cp311-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. pyreduce/__init__.py +67 -0
  2. pyreduce/__main__.py +106 -0
  3. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.exp +0 -0
  4. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.lib +0 -0
  5. pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
  6. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.exp +0 -0
  7. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.lib +0 -0
  8. pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
  9. pyreduce/clib/__init__.py +0 -0
  10. pyreduce/clib/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
  11. pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
  12. pyreduce/clib/build_extract.py +75 -0
  13. pyreduce/clib/slit_func_2d_xi_zeta_bd.c +1313 -0
  14. pyreduce/clib/slit_func_2d_xi_zeta_bd.h +55 -0
  15. pyreduce/clib/slit_func_bd.c +362 -0
  16. pyreduce/clib/slit_func_bd.h +17 -0
  17. pyreduce/clipnflip.py +147 -0
  18. pyreduce/combine_frames.py +855 -0
  19. pyreduce/configuration.py +186 -0
  20. pyreduce/continuum_normalization.py +329 -0
  21. pyreduce/cwrappers.py +404 -0
  22. pyreduce/datasets.py +231 -0
  23. pyreduce/echelle.py +413 -0
  24. pyreduce/estimate_background_scatter.py +129 -0
  25. pyreduce/extract.py +1361 -0
  26. pyreduce/extraction_width.py +77 -0
  27. pyreduce/instruments/__init__.py +0 -0
  28. pyreduce/instruments/andes.json +61 -0
  29. pyreduce/instruments/andes.py +102 -0
  30. pyreduce/instruments/common.json +46 -0
  31. pyreduce/instruments/common.py +675 -0
  32. pyreduce/instruments/crires_plus.json +63 -0
  33. pyreduce/instruments/crires_plus.py +103 -0
  34. pyreduce/instruments/filters.py +195 -0
  35. pyreduce/instruments/harpn.json +136 -0
  36. pyreduce/instruments/harpn.py +201 -0
  37. pyreduce/instruments/harps.json +155 -0
  38. pyreduce/instruments/harps.py +310 -0
  39. pyreduce/instruments/instrument_info.py +140 -0
  40. pyreduce/instruments/instrument_schema.json +221 -0
  41. pyreduce/instruments/jwst_miri.json +53 -0
  42. pyreduce/instruments/jwst_miri.py +29 -0
  43. pyreduce/instruments/jwst_niriss.json +52 -0
  44. pyreduce/instruments/jwst_niriss.py +98 -0
  45. pyreduce/instruments/lick_apf.json +53 -0
  46. pyreduce/instruments/lick_apf.py +35 -0
  47. pyreduce/instruments/mcdonald.json +59 -0
  48. pyreduce/instruments/mcdonald.py +123 -0
  49. pyreduce/instruments/metis_ifu.json +63 -0
  50. pyreduce/instruments/metis_ifu.py +45 -0
  51. pyreduce/instruments/metis_lss.json +65 -0
  52. pyreduce/instruments/metis_lss.py +45 -0
  53. pyreduce/instruments/micado.json +53 -0
  54. pyreduce/instruments/micado.py +45 -0
  55. pyreduce/instruments/neid.json +51 -0
  56. pyreduce/instruments/neid.py +154 -0
  57. pyreduce/instruments/nirspec.json +56 -0
  58. pyreduce/instruments/nirspec.py +215 -0
  59. pyreduce/instruments/nte.json +47 -0
  60. pyreduce/instruments/nte.py +42 -0
  61. pyreduce/instruments/uves.json +59 -0
  62. pyreduce/instruments/uves.py +46 -0
  63. pyreduce/instruments/xshooter.json +66 -0
  64. pyreduce/instruments/xshooter.py +39 -0
  65. pyreduce/make_shear.py +606 -0
  66. pyreduce/masks/mask_crires_plus_det1.fits.gz +0 -0
  67. pyreduce/masks/mask_crires_plus_det2.fits.gz +0 -0
  68. pyreduce/masks/mask_crires_plus_det3.fits.gz +0 -0
  69. pyreduce/masks/mask_ctio_chiron.fits.gz +0 -0
  70. pyreduce/masks/mask_elodie.fits.gz +0 -0
  71. pyreduce/masks/mask_feros3.fits.gz +0 -0
  72. pyreduce/masks/mask_flames_giraffe.fits.gz +0 -0
  73. pyreduce/masks/mask_harps_blue.fits.gz +0 -0
  74. pyreduce/masks/mask_harps_red.fits.gz +0 -0
  75. pyreduce/masks/mask_hds_blue.fits.gz +0 -0
  76. pyreduce/masks/mask_hds_red.fits.gz +0 -0
  77. pyreduce/masks/mask_het_hrs_2x5.fits.gz +0 -0
  78. pyreduce/masks/mask_jwst_miri_lrs_slitless.fits.gz +0 -0
  79. pyreduce/masks/mask_jwst_niriss_gr700xd.fits.gz +0 -0
  80. pyreduce/masks/mask_lick_apf_.fits.gz +0 -0
  81. pyreduce/masks/mask_mcdonald.fits.gz +0 -0
  82. pyreduce/masks/mask_nes.fits.gz +0 -0
  83. pyreduce/masks/mask_nirspec_nirspec.fits.gz +0 -0
  84. pyreduce/masks/mask_sarg.fits.gz +0 -0
  85. pyreduce/masks/mask_sarg_2x2a.fits.gz +0 -0
  86. pyreduce/masks/mask_sarg_2x2b.fits.gz +0 -0
  87. pyreduce/masks/mask_subaru_hds_red.fits.gz +0 -0
  88. pyreduce/masks/mask_uves_blue.fits.gz +0 -0
  89. pyreduce/masks/mask_uves_blue_binned_2_2.fits.gz +0 -0
  90. pyreduce/masks/mask_uves_middle.fits.gz +0 -0
  91. pyreduce/masks/mask_uves_middle_2x2_split.fits.gz +0 -0
  92. pyreduce/masks/mask_uves_middle_binned_2_2.fits.gz +0 -0
  93. pyreduce/masks/mask_uves_red.fits.gz +0 -0
  94. pyreduce/masks/mask_uves_red_2x2.fits.gz +0 -0
  95. pyreduce/masks/mask_uves_red_2x2_split.fits.gz +0 -0
  96. pyreduce/masks/mask_uves_red_binned_2_2.fits.gz +0 -0
  97. pyreduce/masks/mask_xshooter_nir.fits.gz +0 -0
  98. pyreduce/rectify.py +138 -0
  99. pyreduce/reduce.py +2205 -0
  100. pyreduce/settings/settings_ANDES.json +89 -0
  101. pyreduce/settings/settings_CRIRES_PLUS.json +89 -0
  102. pyreduce/settings/settings_HARPN.json +73 -0
  103. pyreduce/settings/settings_HARPS.json +69 -0
  104. pyreduce/settings/settings_JWST_MIRI.json +55 -0
  105. pyreduce/settings/settings_JWST_NIRISS.json +55 -0
  106. pyreduce/settings/settings_LICK_APF.json +62 -0
  107. pyreduce/settings/settings_MCDONALD.json +58 -0
  108. pyreduce/settings/settings_METIS_IFU.json +77 -0
  109. pyreduce/settings/settings_METIS_LSS.json +77 -0
  110. pyreduce/settings/settings_MICADO.json +78 -0
  111. pyreduce/settings/settings_NEID.json +73 -0
  112. pyreduce/settings/settings_NIRSPEC.json +58 -0
  113. pyreduce/settings/settings_NTE.json +60 -0
  114. pyreduce/settings/settings_UVES.json +54 -0
  115. pyreduce/settings/settings_XSHOOTER.json +78 -0
  116. pyreduce/settings/settings_pyreduce.json +178 -0
  117. pyreduce/settings/settings_schema.json +827 -0
  118. pyreduce/tools/__init__.py +0 -0
  119. pyreduce/tools/combine.py +117 -0
  120. pyreduce/trace_orders.py +645 -0
  121. pyreduce/util.py +1288 -0
  122. pyreduce/wavecal/MICADO_HK_3arcsec_chip5.npz +0 -0
  123. pyreduce/wavecal/atlas/thar.fits +4946 -13
  124. pyreduce/wavecal/atlas/thar_list.txt +4172 -0
  125. pyreduce/wavecal/atlas/une.fits +0 -0
  126. pyreduce/wavecal/convert.py +38 -0
  127. pyreduce/wavecal/crires_plus_J1228_Open_det1.npz +0 -0
  128. pyreduce/wavecal/crires_plus_J1228_Open_det2.npz +0 -0
  129. pyreduce/wavecal/crires_plus_J1228_Open_det3.npz +0 -0
  130. pyreduce/wavecal/harpn_harpn_2D.npz +0 -0
  131. pyreduce/wavecal/harps_blue_2D.npz +0 -0
  132. pyreduce/wavecal/harps_blue_pol_2D.npz +0 -0
  133. pyreduce/wavecal/harps_red_2D.npz +0 -0
  134. pyreduce/wavecal/harps_red_pol_2D.npz +0 -0
  135. pyreduce/wavecal/mcdonald.npz +0 -0
  136. pyreduce/wavecal/metis_lss_l_2D.npz +0 -0
  137. pyreduce/wavecal/metis_lss_m_2D.npz +0 -0
  138. pyreduce/wavecal/nirspec_K2.npz +0 -0
  139. pyreduce/wavecal/uves_blue_360nm_2D.npz +0 -0
  140. pyreduce/wavecal/uves_blue_390nm_2D.npz +0 -0
  141. pyreduce/wavecal/uves_blue_437nm_2D.npz +0 -0
  142. pyreduce/wavecal/uves_middle_2x2_2D.npz +0 -0
  143. pyreduce/wavecal/uves_middle_565nm_2D.npz +0 -0
  144. pyreduce/wavecal/uves_middle_580nm_2D.npz +0 -0
  145. pyreduce/wavecal/uves_middle_600nm_2D.npz +0 -0
  146. pyreduce/wavecal/uves_middle_665nm_2D.npz +0 -0
  147. pyreduce/wavecal/uves_middle_860nm_2D.npz +0 -0
  148. pyreduce/wavecal/uves_red_580nm_2D.npz +0 -0
  149. pyreduce/wavecal/uves_red_600nm_2D.npz +0 -0
  150. pyreduce/wavecal/uves_red_665nm_2D.npz +0 -0
  151. pyreduce/wavecal/uves_red_760nm_2D.npz +0 -0
  152. pyreduce/wavecal/uves_red_860nm_2D.npz +0 -0
  153. pyreduce/wavecal/xshooter_nir.npz +0 -0
  154. pyreduce/wavelength_calibration.py +1873 -0
  155. pyreduce_astro-0.6.0b5.dist-info/METADATA +113 -0
  156. pyreduce_astro-0.6.0b5.dist-info/RECORD +158 -0
  157. pyreduce_astro-0.6.0b5.dist-info/WHEEL +4 -0
  158. pyreduce_astro-0.6.0b5.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,155 @@
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__": "HARPS",
4
+ "id_instrument": "HARPS(pol)?",
5
+ "instrument": "INSTRUME",
6
+ "telescope": "TELESCOP",
7
+ "date": "DATE-OBS",
8
+ "date_format": "fits",
9
+ "modes": [
10
+ "BLUE",
11
+ "RED"
12
+ ],
13
+ "modes_id": [
14
+ "HARPS",
15
+ "HARPS"
16
+ ],
17
+ "modes_id_polarimetry": [
18
+ "HARPSpol",
19
+ "HARPSpol"
20
+ ],
21
+ "extension": [
22
+ 1,
23
+ 2
24
+ ],
25
+ "id": [
26
+ [
27
+ 1,
28
+ 1
29
+ ],
30
+ [
31
+ 1,
32
+ 2
33
+ ]
34
+ ],
35
+ "orientation": 1,
36
+ "transpose": false,
37
+ "prescan_x": "HIERARCH ESO DET OUT{id[0]} PRSCX",
38
+ "overscan_x": "HIERARCH ESO DET OUT{id[0]} OVSCX",
39
+ "prescan_y": 0,
40
+ "overscan_y": 0,
41
+ "naxis_x": "NAXIS1",
42
+ "naxis_y": "NAXIS2",
43
+ "polarization_linear": "eso ins ret50 pos",
44
+ "polarization_circular": "eso ins ret25 pos",
45
+ "gain": "HIERARCH ESO DET OUT{id[0]} CONAD",
46
+ "readnoise": "HIERARCH ESO DET OUT{id[0]} RON",
47
+ "dark": "HIERARCH ESO INS DET{id[1]} OFFDRK",
48
+ "sky": "HIERARCH ESO INS DET{id[1]} OFFSKY",
49
+ "exposure_time": "EXPTIME",
50
+ "image_type": "OBJECT",
51
+ "category": "HIERARCH ESO DPR CATG",
52
+ "ra": "RA",
53
+ "dec": "DEC",
54
+ "longitude": "HIERARCH ESO TEL GEOLON",
55
+ "latitude": "HIERARCH ESO TEL GEOLAT",
56
+ "altitude": "HIERARCH ESO TEL GEOELEV",
57
+ "target": "OBJECT",
58
+ "instrument_mode": "ESO INS MODE",
59
+ "instrument_mode_alternative": "ESO TPL NAME",
60
+ "observation_type": "ESO DPR TYPE",
61
+ "id_fiber_a": "LAMP,DARK,TUN",
62
+ "id_fiber_b": "DARK,LAMP,TUN",
63
+ "kw_bias": "ESO DPR TYPE",
64
+ "kw_flat": "ESO DPR TYPE",
65
+ "kw_curvature": "ESO DPR TYPE",
66
+ "kw_scatter": "ESO DPR TYPE",
67
+ "kw_orders": "ESO DPR TYPE",
68
+ "kw_wave": "ESO DPR TYPE",
69
+ "kw_comb": "ESO DPR TYPE",
70
+ "kw_spec": "ESO DPR TYPE",
71
+ "id_bias": "BIAS,BIAS",
72
+ "id_flat": "LAMP,LAMP,TUN",
73
+ "id_orders": "LAMP,LAMP,TUN",
74
+ "id_curvature": "WAVE,WAVE,THAR2",
75
+ "id_scatter": "LAMP,LAMP,TUN",
76
+ "id_wave": "WAVE,WAVE,THAR2",
77
+ "id_comb": "WAVE,WAVE,COMB",
78
+ "id_spec": "STAR,*,*",
79
+ "wavelength_range": [[
80
+ [5245.4, 5304.3],
81
+ [5200.5, 5259.0],
82
+ [5156.5, 5214.4],
83
+ [5113.2, 5170.6],
84
+ [5070.6, 5127.6],
85
+ [5028.7, 5085.2],
86
+ [4987.5, 5043.5],
87
+ [4946.9, 5002.5],
88
+ [4907.0, 4962.2],
89
+ [4867.8, 4922.5],
90
+ [4829.2, 4883.4],
91
+ [4791.1, 4845.0],
92
+ [4753.7, 4807.1],
93
+ [4716.9, 4769.9],
94
+ [4680.6, 4733.2],
95
+ [4644.9, 4697.1],
96
+ [4609.7, 4661.5],
97
+ [4575.1, 4626.5],
98
+ [4540.9, 4591.9],
99
+ [4507.3, 4557.9],
100
+ [4474.2, 4524.4],
101
+ [4441.5, 4491.4],
102
+ [4409.3, 4458.9],
103
+ [4377.6, 4426.8],
104
+ [4346.4, 4395.2],
105
+ [4315.5, 4364.0],
106
+ [4285.2, 4333.3],
107
+ [4255.2, 4303.0],
108
+ [4225.7, 4273.1],
109
+ [4196.5, 4243.6],
110
+ [4167.8, 4214.6],
111
+ [4139.4, 4185.9],
112
+ [4111.5, 4157.6],
113
+ [4083.9, 4129.7],
114
+ [4056.7, 4102.2],
115
+ [4029.8, 4075.0],
116
+ [4003.3, 4048.2],
117
+ [3977.1, 4021.8],
118
+ [3951.3, 3995.7],
119
+ [3925.8, 3969.9],
120
+ [3900.7, 3944.5],
121
+ [3875.8, 3919.3],
122
+ [3851.3, 3894.5],
123
+ [3827.1, 3870.0],
124
+ [3803.2, 3845.9],
125
+ [3779.6, 3822.0]
126
+ ],[
127
+ [6835.9, 6913.0],
128
+ [6760.0, 6836.2],
129
+ [6685.7, 6761.1],
130
+ [6613.1, 6687.6],
131
+ [6542.0, 6615.7],
132
+ [6472.4, 6545.4],
133
+ [6404.3, 6476.5],
134
+ [6337.7, 6409.1],
135
+ [6272.3, 6343.0],
136
+ [6208.4, 6278.3],
137
+ [6145.7, 6214.9],
138
+ [6084.2, 6152.8],
139
+ [6024.0, 6091.9],
140
+ [5965.0, 6032.1],
141
+ [5907.1, 5973.6],
142
+ [5850.3, 5916.2],
143
+ [5794.6, 5859.8],
144
+ [5739.9, 5804.6],
145
+ [5686.3, 5750.3],
146
+ [5633.7, 5697.1],
147
+ [5582.0, 5644.8],
148
+ [5531.3, 5593.5],
149
+ [5481.5, 5543.2],
150
+ [5432.5, 5493.7],
151
+ [5384.5, 5445.1],
152
+ [5337.3, 5397.3]
153
+ ]
154
+ ]
155
+ }
@@ -0,0 +1,310 @@
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 re
9
+ from os.path import dirname, join
10
+
11
+ import numpy as np
12
+
13
+ from .common import Instrument
14
+ from .filters import Filter, InstrumentFilter, NightFilter, ObjectFilter
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class TypeFilter(Filter):
20
+ def __init__(self, keyword="ESO DPR TYPE"):
21
+ super().__init__(keyword, regex=True)
22
+
23
+ def classify(self, value):
24
+ if value is not None:
25
+ match = self.match(value)
26
+ data = np.asarray(self.data)
27
+ data = np.unique(data[match])
28
+ try:
29
+ regex = re.compile(value)
30
+ keys = [regex.match(f) for f in data]
31
+ keys = [[g for g in d.groups() if g is not None][0] for d in keys]
32
+ unique = np.unique(keys)
33
+ assign = {
34
+ u: [d for k, d in zip(keys, data, strict=False) if k == u]
35
+ for u in unique
36
+ }
37
+ data = [(u, self.match("|".join(a))) for u, a in assign.items()]
38
+ except IndexError:
39
+ data = np.asarray(self.data)
40
+ data = np.unique(data[match])
41
+ data = [(d, self.match(d)) for d in data]
42
+ else:
43
+ data = np.unique(self.data)
44
+ data = [(d, self.match(d)) for d in data]
45
+ return data
46
+
47
+
48
+ class FiberFilter(Filter):
49
+ def __init__(self, keyword="ESO DPR TYPE"):
50
+ super().__init__(keyword, regex=True)
51
+ self.lamp_values = ["LAMP", "STAR", "CIRPOL", "LINPOL"]
52
+
53
+ def collect(self, header):
54
+ value = header.get(self.keyword)
55
+ if value is None:
56
+ value = ""
57
+ else:
58
+ value = value.split(",")
59
+ if value[0] in self.lamp_values and value[1] in self.lamp_values:
60
+ value = "AB"
61
+ elif value[1] in self.lamp_values:
62
+ value = "B"
63
+ elif value[0] in self.lamp_values:
64
+ value = "A"
65
+ else:
66
+ value = ""
67
+
68
+ self.data.append(value)
69
+ return value
70
+
71
+
72
+ class PolarizationFilter(Filter):
73
+ def __init__(self, keyword="ESO INS RET?? POS"):
74
+ super().__init__(keyword, regex=True)
75
+
76
+ def collect(self, header):
77
+ dpr_type = header.get("ESO DPR TYPE", "")
78
+ match = re.match(r"^.*,(CIR|LIN)POL,.*$", dpr_type)
79
+ if match is None:
80
+ value = "none"
81
+ elif match.group(1) == "CIR":
82
+ value = "circular"
83
+ elif match.group(1) == "LIN":
84
+ value = "linear"
85
+ else:
86
+ raise ValueError("Polarization not recognised")
87
+ self.data.append(value)
88
+ return value
89
+
90
+
91
+ class HARPS(Instrument):
92
+ def __init__(self):
93
+ super().__init__()
94
+ self.filters = {
95
+ "instrument": InstrumentFilter(self.info["instrument"]),
96
+ "night": NightFilter(self.info["date"]),
97
+ # "branch": Filter(, regex=True),
98
+ "mode": Filter(
99
+ self.info["instrument_mode"], regex=True, flags=re.IGNORECASE
100
+ ),
101
+ "type": TypeFilter(self.info["observation_type"]),
102
+ "polarization": PolarizationFilter(),
103
+ "target": ObjectFilter(self.info["target"], regex=True),
104
+ "fiber": FiberFilter(),
105
+ }
106
+ self.night = "night"
107
+ self.science = "science"
108
+ self.shared = ["instrument", "night", "mode", "polarization", "fiber"]
109
+ self.find_closest = [
110
+ "bias",
111
+ "flat",
112
+ "wavecal_master",
113
+ "freq_comb_master",
114
+ "orders",
115
+ "scatter",
116
+ "curvature",
117
+ ]
118
+
119
+ def get_expected_values(self, target, night, mode, fiber, polarimetry):
120
+ """Determine the default expected values in the headers for a given observation configuration
121
+
122
+ Any parameter may be None, to indicate that all values are allowed
123
+
124
+ Parameters
125
+ ----------
126
+ target : str
127
+ Name of the star / observation target
128
+ night : str
129
+ Observation night/nights
130
+ fiber : "A", "B", "AB"
131
+ Which of the fibers should carry observation signal
132
+ polarimetry : "none", "linear", "circular", bool
133
+ Whether the instrument is used in HARPS or HARPSpol mode
134
+ and which polarization is observed. Set to true for both kinds
135
+ of polarisation.
136
+
137
+ Returns
138
+ -------
139
+ expectations: dict
140
+ Dictionary of expected header values, with one entry per step.
141
+ The entries for each step refer to the filters defined in self.filters
142
+
143
+ Raises
144
+ ------
145
+ ValueError
146
+ Invalid combination of parameters
147
+ """
148
+ if target is not None:
149
+ target = target.replace(" ", r"(?:\s*|-)")
150
+ else:
151
+ target = ".*"
152
+
153
+ if fiber == "AB":
154
+ template = r"({a},{a}),{c}"
155
+ elif fiber == "A":
156
+ template = r"({a},{b}),{c}"
157
+ elif fiber == "B":
158
+ template = r"({b},{a}),{c}"
159
+ elif fiber is None:
160
+ template = None
161
+ fiber = "(AB)|(A)|(B)"
162
+ else:
163
+ raise ValueError(
164
+ "fiber keyword not understood, possible values are 'AB', 'A', 'B'"
165
+ )
166
+
167
+ if polarimetry == "none" or not polarimetry:
168
+ mode = "HARPS"
169
+ if template is not None:
170
+ id_orddef = template.format(a="LAMP", b="DARK", c=".*?")
171
+ id_spec = template.format(a="STAR", b="(?!STAR).*?", c=".*?")
172
+ else:
173
+ id_spec = (
174
+ r"^(STAR,(?!STAR).*),.*$|^((?!STAR).*?,STAR),.*$|^(STAR,STAR),.*$"
175
+ )
176
+ id_orddef = r"^(LAMP,DARK),.*$|^(DARK,LAMP),.*$|^(LAMP,LAMP),.*$"
177
+ polarimetry = "none"
178
+ else:
179
+ mode = "HARPSpol"
180
+ id_orddef = r"(LAMP,LAMP),.*"
181
+ if polarimetry == r"linear":
182
+ id_spec = r"(STAR,LINPOL),.*"
183
+ elif polarimetry == "circular":
184
+ id_spec = r"(STAR,CIRPOL),.*"
185
+ elif polarimetry:
186
+ id_spec = r"(STAR,(?:LIN|CIR)POL),.*"
187
+ polarimetry = r"(circular|linear)"
188
+ else:
189
+ raise ValueError(
190
+ f"polarization parameter not recognized. Expected one of 'none', 'linear', 'circular', but got {polarimetry}"
191
+ )
192
+
193
+ expectations = {
194
+ "bias": {"instrument": "HARPS", "night": night, "type": r"BIAS,BIAS"},
195
+ "flat": {"instrument": "HARPS", "night": night, "type": r"(LAMP,LAMP),.*"},
196
+ "orders": {
197
+ "instrument": "HARPS",
198
+ "night": night,
199
+ "fiber": fiber,
200
+ "type": id_orddef,
201
+ },
202
+ "scatter": {
203
+ "instrument": "HARPS",
204
+ "night": night,
205
+ "type": id_orddef, # Same as orders or same as flat?
206
+ },
207
+ "curvature": {
208
+ "instrument": "HARPS",
209
+ "night": night,
210
+ "type": [r"(WAVE,WAVE,COMB)", r"(WAVE,WAVE,THAR)\d?"],
211
+ },
212
+ "wavecal_master": {
213
+ "instrument": "HARPS",
214
+ "night": night,
215
+ "type": r"(WAVE,WAVE,THAR)\d?",
216
+ },
217
+ "freq_comb_master": {
218
+ "instrument": "HARPS",
219
+ "night": night,
220
+ "type": r"(WAVE,WAVE,COMB)",
221
+ },
222
+ "science": {
223
+ "instrument": "HARPS",
224
+ "night": night,
225
+ "mode": mode,
226
+ "type": id_spec,
227
+ "fiber": fiber,
228
+ "polarization": polarimetry,
229
+ "target": target,
230
+ },
231
+ }
232
+ return expectations
233
+
234
+ def get_extension(self, header, mode):
235
+ extension = super().get_extension(header, mode)
236
+
237
+ try:
238
+ if (
239
+ header["NAXIS"] == 2
240
+ and header["NAXIS1"] == 4296
241
+ and header["NAXIS2"] == 4096
242
+ ):
243
+ extension = 0
244
+ except KeyError:
245
+ pass
246
+
247
+ return extension
248
+
249
+ def add_header_info(self, header, mode, **kwargs):
250
+ """read data from header and add it as REDUCE keyword back to the header"""
251
+ # "Normal" stuff is handled by the general version, specific changes to values happen here
252
+ # alternatively you can implement all of it here, whatever works
253
+ header = super().add_header_info(header, mode)
254
+
255
+ try:
256
+ header["e_ra"] /= 15
257
+ header["e_jd"] += header["e_exptim"] / (7200 * 24) + 0.5
258
+
259
+ pol_angle = header.get("eso ins ret25 pos")
260
+ if pol_angle is None:
261
+ pol_angle = header.get("eso ins ret50 pos")
262
+ if pol_angle is None:
263
+ pol_angle = "no polarimeter"
264
+ else:
265
+ pol_angle = "lin %i" % pol_angle
266
+ else:
267
+ pol_angle = "cir %i" % pol_angle
268
+
269
+ header["e_pol"] = (pol_angle, "polarization angle")
270
+ except:
271
+ pass
272
+
273
+ try:
274
+ if (
275
+ header["NAXIS"] == 2
276
+ and header["NAXIS1"] == 4296
277
+ and header["NAXIS2"] == 4096
278
+ ):
279
+ # both modes are in the same image
280
+ prescan_x = 50
281
+ overscan_x = 50
282
+ naxis_x = 2148
283
+ if mode == "BLUE":
284
+ header["e_xlo"] = prescan_x
285
+ header["e_xhi"] = naxis_x - overscan_x
286
+ elif mode == "RED":
287
+ header["e_xlo"] = naxis_x + prescan_x
288
+ header["e_xhi"] = 2 * naxis_x - overscan_x
289
+ except KeyError:
290
+ pass
291
+
292
+ return header
293
+
294
+ def get_wavecal_filename(self, header, mode, polarimetry, **kwargs):
295
+ """Get the filename of the wavelength calibration config file"""
296
+ cwd = dirname(__file__)
297
+ if polarimetry:
298
+ pol = "_pol"
299
+ else:
300
+ pol = ""
301
+ fname = f"harps_{mode.lower()}{pol}_2D.npz"
302
+ fname = join(cwd, "..", "wavecal", fname)
303
+ return fname
304
+
305
+ def get_wavelength_range(self, header, mode, **kwargs):
306
+ wave_range = super().get_wavelength_range(header, mode, **kwargs)
307
+ # The wavelength orders are in inverse order in the .json file
308
+ # because I was to lazy to invert them in the file
309
+ wave_range = wave_range[::-1]
310
+ return wave_range
@@ -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, mode, **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
+ mode : str
73
+ instrument mode, if applicable (e.g. red/blue 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, mode, **kwargs)
91
+
92
+
93
+ def get_supported_modes(instrument):
94
+ instrument = load_instrument(instrument)
95
+ return instrument.get_supported_modes()
96
+
97
+
98
+ def modeinfo(header, instrument, mode, **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
+ mode : str
108
+ instrument mode (e.g. red/blue 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, mode, **kwargs)
118
+ return header
119
+
120
+
121
+ def get_wavecal_filename(header, instrument, mode, **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
+ mode : str
131
+ instrument mode (e.g. red/blue 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, mode, **kwargs)