pyreduce-astro 0.7a4__cp313-cp313-win_amd64.whl → 0.7a5__cp313-cp313-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/__main__.py +6 -6
- pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
- pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
- pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
- pyreduce/combine_frames.py +25 -25
- pyreduce/instruments/aj.yaml +1 -1
- pyreduce/instruments/andes.py +17 -17
- pyreduce/instruments/andes.yaml +1 -1
- pyreduce/instruments/common.py +58 -56
- pyreduce/instruments/crires_plus.py +26 -36
- pyreduce/instruments/crires_plus.yaml +0 -2
- pyreduce/instruments/filters.py +1 -1
- pyreduce/instruments/harpn.py +12 -12
- pyreduce/instruments/harpn.yaml +2 -2
- pyreduce/instruments/harps.py +19 -12
- pyreduce/instruments/harps.yaml +2 -2
- pyreduce/instruments/instrument_info.py +14 -14
- pyreduce/instruments/jwst_miri.py +6 -4
- pyreduce/instruments/jwst_miri.yaml +2 -2
- pyreduce/instruments/jwst_niriss.py +12 -8
- pyreduce/instruments/jwst_niriss.yaml +2 -2
- pyreduce/instruments/lick_apf.py +3 -3
- pyreduce/instruments/lick_apf.yaml +2 -2
- pyreduce/instruments/mcdonald.py +5 -5
- pyreduce/instruments/mcdonald.yaml +3 -3
- pyreduce/instruments/metis_ifu.py +5 -5
- pyreduce/instruments/metis_ifu.yaml +3 -3
- pyreduce/instruments/metis_lss.py +5 -5
- pyreduce/instruments/metis_lss.yaml +3 -3
- pyreduce/instruments/micado.py +5 -5
- pyreduce/instruments/micado.yaml +3 -3
- pyreduce/instruments/models.py +6 -6
- pyreduce/instruments/neid.py +12 -12
- pyreduce/instruments/neid.yaml +2 -2
- pyreduce/instruments/nirspec.py +8 -8
- pyreduce/instruments/nirspec.yaml +1 -1
- pyreduce/instruments/nte.py +6 -6
- pyreduce/instruments/nte.yaml +1 -1
- pyreduce/instruments/uves.py +8 -8
- pyreduce/instruments/uves.yaml +2 -2
- pyreduce/instruments/xshooter.py +4 -4
- pyreduce/instruments/xshooter.yaml +3 -3
- pyreduce/pipeline.py +26 -26
- pyreduce/reduce.py +34 -34
- {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/METADATA +4 -6
- {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/RECORD +53 -65
- 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_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/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp312-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/wavecal/{crires_plus_J1228_Open_det1.npz → crires_plus_J1228_det1.npz} +0 -0
- /pyreduce/wavecal/{crires_plus_J1228_Open_det2.npz → crires_plus_J1228_det2.npz} +0 -0
- /pyreduce/wavecal/{crires_plus_J1228_Open_det3.npz → crires_plus_J1228_det3.npz} +0 -0
- {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/WHEEL +0 -0
- {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/entry_points.txt +0 -0
- {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/licenses/LICENSE +0 -0
pyreduce/__main__.py
CHANGED
|
@@ -40,7 +40,7 @@ def cli():
|
|
|
40
40
|
@click.argument("instrument")
|
|
41
41
|
@click.argument("target")
|
|
42
42
|
@click.option("--night", "-n", default=None, help="Observation night (YYYY-MM-DD)")
|
|
43
|
-
@click.option("--
|
|
43
|
+
@click.option("--channel", "-c", default=None, help="Instrument channel")
|
|
44
44
|
@click.option(
|
|
45
45
|
"--steps",
|
|
46
46
|
"-s",
|
|
@@ -71,7 +71,7 @@ def run(
|
|
|
71
71
|
instrument,
|
|
72
72
|
target,
|
|
73
73
|
night,
|
|
74
|
-
|
|
74
|
+
channel,
|
|
75
75
|
steps,
|
|
76
76
|
base_dir,
|
|
77
77
|
input_dir,
|
|
@@ -106,7 +106,7 @@ def run(
|
|
|
106
106
|
instrument=instrument,
|
|
107
107
|
target=target,
|
|
108
108
|
night=night,
|
|
109
|
-
|
|
109
|
+
channels=channel,
|
|
110
110
|
steps=steps,
|
|
111
111
|
base_dir=base_dir or "",
|
|
112
112
|
input_dir=input_dir,
|
|
@@ -281,12 +281,12 @@ def make_step_command(step_name):
|
|
|
281
281
|
@click.argument("instrument")
|
|
282
282
|
@click.argument("target")
|
|
283
283
|
@click.option("--night", "-n", default=None, help="Observation night")
|
|
284
|
-
@click.option("--
|
|
284
|
+
@click.option("--channel", "-c", default=None, help="Instrument channel")
|
|
285
285
|
@click.option("--base-dir", "-b", default=None, help="Base directory")
|
|
286
286
|
@click.option("--input-dir", "-i", default="raw", help="Input directory")
|
|
287
287
|
@click.option("--output-dir", "-o", default="reduced", help="Output directory")
|
|
288
288
|
@click.option("--plot", "-p", default=0, help="Plot level")
|
|
289
|
-
def cmd(instrument, target, night,
|
|
289
|
+
def cmd(instrument, target, night, channel, base_dir, input_dir, output_dir, plot):
|
|
290
290
|
from .configuration import get_configuration_for_instrument
|
|
291
291
|
from .reduce import main as reduce_main
|
|
292
292
|
|
|
@@ -295,7 +295,7 @@ def make_step_command(step_name):
|
|
|
295
295
|
instrument=instrument,
|
|
296
296
|
target=target,
|
|
297
297
|
night=night,
|
|
298
|
-
|
|
298
|
+
channels=channel,
|
|
299
299
|
steps=(step_name,),
|
|
300
300
|
base_dir=base_dir or "",
|
|
301
301
|
input_dir=input_dir,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
pyreduce/combine_frames.py
CHANGED
|
@@ -146,7 +146,7 @@ def fix_bad_pixels(probability, buffer, readnoise, gain, threshold):
|
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
def combine_frames_simple(
|
|
149
|
-
files, instrument,
|
|
149
|
+
files, instrument, channel, extension=None, dtype=np.float32, **kwargs
|
|
150
150
|
):
|
|
151
151
|
"""
|
|
152
152
|
Simple addition of similar images.
|
|
@@ -157,8 +157,8 @@ def combine_frames_simple(
|
|
|
157
157
|
list of fits files to combine
|
|
158
158
|
instrument : str
|
|
159
159
|
instrument id for modinfo
|
|
160
|
-
|
|
161
|
-
instrument
|
|
160
|
+
channel : str
|
|
161
|
+
instrument channel
|
|
162
162
|
extension : int, optional
|
|
163
163
|
fits extension to load (default: 1)
|
|
164
164
|
dtype : np.dtype, optional
|
|
@@ -175,13 +175,13 @@ def combine_frames_simple(
|
|
|
175
175
|
|
|
176
176
|
# Load the first file to get the shape and header
|
|
177
177
|
result, head = instrument.load_fits(
|
|
178
|
-
files[0],
|
|
178
|
+
files[0], channel, dtype=dtype, extension=extension, **kwargs
|
|
179
179
|
)
|
|
180
180
|
|
|
181
181
|
# Sum the remaining files
|
|
182
182
|
for fname in files[1:]:
|
|
183
183
|
data, _ = instrument.load_fits(
|
|
184
|
-
fname,
|
|
184
|
+
fname, channel, dtype=dtype, extension=extension, **kwargs
|
|
185
185
|
)
|
|
186
186
|
result += data
|
|
187
187
|
|
|
@@ -208,7 +208,7 @@ def combine_frames_simple(
|
|
|
208
208
|
def combine_frames(
|
|
209
209
|
files,
|
|
210
210
|
instrument,
|
|
211
|
-
|
|
211
|
+
channel,
|
|
212
212
|
extension=None,
|
|
213
213
|
threshold=3.5,
|
|
214
214
|
window=50,
|
|
@@ -276,8 +276,8 @@ def combine_frames(
|
|
|
276
276
|
list of fits files to combine
|
|
277
277
|
instrument : str
|
|
278
278
|
instrument id for arminfo
|
|
279
|
-
|
|
280
|
-
instrument
|
|
279
|
+
channel : str
|
|
280
|
+
instrument channel
|
|
281
281
|
extension : int, optional
|
|
282
282
|
fits extension to load (default: 1)
|
|
283
283
|
threshold : float, optional
|
|
@@ -315,7 +315,7 @@ def combine_frames(
|
|
|
315
315
|
raise ValueError("No files given for combine frames")
|
|
316
316
|
elif len(files) == 1:
|
|
317
317
|
result, head = instrument.load_fits(
|
|
318
|
-
files[0],
|
|
318
|
+
files[0], channel, dtype=dtype, extension=extension, **kwargs
|
|
319
319
|
)
|
|
320
320
|
readnoise = np.atleast_1d(head.get("e_readn", 0))
|
|
321
321
|
total_exposure_time = head.get("exptime", 0)
|
|
@@ -325,12 +325,12 @@ def combine_frames(
|
|
|
325
325
|
# Two images
|
|
326
326
|
elif len(files) == 2:
|
|
327
327
|
bias1, head1 = instrument.load_fits(
|
|
328
|
-
files[0],
|
|
328
|
+
files[0], channel, dtype=dtype, extension=extension, **kwargs
|
|
329
329
|
)
|
|
330
330
|
exp1 = head1.get("exptime", 0)
|
|
331
331
|
|
|
332
332
|
bias2, head2 = instrument.load_fits(
|
|
333
|
-
files[1],
|
|
333
|
+
files[1], channel, dtype=dtype, extension=extension, **kwargs
|
|
334
334
|
)
|
|
335
335
|
exp2 = head2.get("exptime", 0)
|
|
336
336
|
readnoise = head2.get("e_readn", 0)
|
|
@@ -349,7 +349,7 @@ def combine_frames(
|
|
|
349
349
|
|
|
350
350
|
heads = [
|
|
351
351
|
instrument.load_fits(
|
|
352
|
-
f,
|
|
352
|
+
f, channel, header_only=True, dtype=dtype, extension=extension, **kwargs
|
|
353
353
|
)
|
|
354
354
|
for f in files
|
|
355
355
|
]
|
|
@@ -398,7 +398,7 @@ def combine_frames(
|
|
|
398
398
|
# Load all image hdus, but leave the data on the disk, using memmap
|
|
399
399
|
# Need to scale data later
|
|
400
400
|
if extension is None:
|
|
401
|
-
extension = [instrument.get_extension(h,
|
|
401
|
+
extension = [instrument.get_extension(h, channel) for h in heads]
|
|
402
402
|
else:
|
|
403
403
|
extension = [extension] * len(heads)
|
|
404
404
|
|
|
@@ -524,7 +524,7 @@ def combine_frames(
|
|
|
524
524
|
def combine_calibrate(
|
|
525
525
|
files,
|
|
526
526
|
instrument,
|
|
527
|
-
|
|
527
|
+
channel,
|
|
528
528
|
mask=None,
|
|
529
529
|
bias=None,
|
|
530
530
|
bhead=None,
|
|
@@ -545,8 +545,8 @@ def combine_calibrate(
|
|
|
545
545
|
list of file names to load
|
|
546
546
|
instrument : Instrument
|
|
547
547
|
PyReduce instrument object with load_fits method
|
|
548
|
-
|
|
549
|
-
descriptor of the instrument
|
|
548
|
+
channel : str
|
|
549
|
+
descriptor of the instrument channel
|
|
550
550
|
mask : array
|
|
551
551
|
2D Bad Pixel Mask to apply to the master image
|
|
552
552
|
bias : tuple(bias, bhead), optional
|
|
@@ -575,7 +575,7 @@ def combine_calibrate(
|
|
|
575
575
|
Unrecognised bias_scaling option
|
|
576
576
|
"""
|
|
577
577
|
# Combine the images and try to remove bad pixels
|
|
578
|
-
orig, thead = combine_frames(files, instrument,
|
|
578
|
+
orig, thead = combine_frames(files, instrument, channel, mask=mask, **kwargs)
|
|
579
579
|
|
|
580
580
|
# Subtract bias
|
|
581
581
|
if bias is not None and bias_scaling is not None and bias_scaling != "none":
|
|
@@ -636,7 +636,7 @@ def combine_calibrate(
|
|
|
636
636
|
|
|
637
637
|
|
|
638
638
|
def combine_polynomial(
|
|
639
|
-
files, instrument,
|
|
639
|
+
files, instrument, channel, mask, degree=1, plot=False, plot_title=None
|
|
640
640
|
):
|
|
641
641
|
"""
|
|
642
642
|
Combine the input files by fitting a polynomial of the pixel value versus
|
|
@@ -648,8 +648,8 @@ def combine_polynomial(
|
|
|
648
648
|
list of file names
|
|
649
649
|
instrument : Instrument
|
|
650
650
|
PyReduce instrument object with load_fits method
|
|
651
|
-
|
|
652
|
-
|
|
651
|
+
channel : str
|
|
652
|
+
channel identifier for this instrument
|
|
653
653
|
mask : array
|
|
654
654
|
bad pixel mask to apply to the coefficients
|
|
655
655
|
degree : int, optional
|
|
@@ -666,7 +666,7 @@ def combine_polynomial(
|
|
|
666
666
|
bhead : Header
|
|
667
667
|
combined FITS header of the coefficients
|
|
668
668
|
"""
|
|
669
|
-
hdus = [instrument.load_fits(f,
|
|
669
|
+
hdus = [instrument.load_fits(f, channel) for f in tqdm(files)]
|
|
670
670
|
data = np.array([h[0] for h in hdus])
|
|
671
671
|
exptimes = np.array([h[1]["EXPTIME"] for h in hdus])
|
|
672
672
|
# Numpy polyfit can fit all polynomials at the same time
|
|
@@ -708,7 +708,7 @@ def combine_polynomial(
|
|
|
708
708
|
def combine_bias(
|
|
709
709
|
files,
|
|
710
710
|
instrument,
|
|
711
|
-
|
|
711
|
+
channel,
|
|
712
712
|
extension=None,
|
|
713
713
|
plot=False,
|
|
714
714
|
plot_title=None,
|
|
@@ -724,7 +724,7 @@ def combine_bias(
|
|
|
724
724
|
files : list(str)
|
|
725
725
|
bias files to combine
|
|
726
726
|
instrument : str
|
|
727
|
-
instrument
|
|
727
|
+
instrument channel for arminfo
|
|
728
728
|
extension : {int, str}, optional
|
|
729
729
|
fits extension to use (default: 1)
|
|
730
730
|
xr : 2-tuple(int), optional
|
|
@@ -756,10 +756,10 @@ def combine_bias(
|
|
|
756
756
|
n2 = len(list2)
|
|
757
757
|
|
|
758
758
|
# Separately images in two groups.
|
|
759
|
-
bias1, head1 = combine_frames(list1, instrument,
|
|
759
|
+
bias1, head1 = combine_frames(list1, instrument, channel, extension, **kwargs)
|
|
760
760
|
bias1 /= n1
|
|
761
761
|
|
|
762
|
-
bias2, head = combine_frames(list2, instrument,
|
|
762
|
+
bias2, head = combine_frames(list2, instrument, channel, extension, **kwargs)
|
|
763
763
|
bias2 /= n2
|
|
764
764
|
|
|
765
765
|
# Make sure we know the gain.
|
pyreduce/instruments/aj.yaml
CHANGED
pyreduce/instruments/andes.py
CHANGED
|
@@ -25,30 +25,30 @@ class ANDES(Instrument):
|
|
|
25
25
|
self.filters["decker"] = Filter(self.info["id_decker"])
|
|
26
26
|
self.shared += ["band", "decker"]
|
|
27
27
|
|
|
28
|
-
def add_header_info(self, header,
|
|
28
|
+
def add_header_info(self, header, channel, **kwargs):
|
|
29
29
|
"""read data from header and add it as REDUCE keyword back to the header"""
|
|
30
30
|
# "Normal" stuff is handled by the general version, specific changes to values happen here
|
|
31
31
|
# alternatively you can implement all of it here, whatever works
|
|
32
|
-
band, decker, detector = self.
|
|
32
|
+
band, decker, detector = self.parse_channel(channel)
|
|
33
33
|
header = super().add_header_info(header, band)
|
|
34
34
|
self.load_info()
|
|
35
35
|
|
|
36
36
|
return header
|
|
37
37
|
|
|
38
|
-
def
|
|
38
|
+
def get_supported_channels(self):
|
|
39
39
|
settings = self.info["settings"]
|
|
40
40
|
deckers = self.info["deckers"]
|
|
41
41
|
detectors = self.info["chips"]
|
|
42
|
-
|
|
42
|
+
channels = [
|
|
43
43
|
"_".join([s, d, c]) for s, d, c in product(settings, deckers, detectors)
|
|
44
44
|
]
|
|
45
|
-
return
|
|
45
|
+
return channels
|
|
46
46
|
|
|
47
|
-
def
|
|
47
|
+
def parse_channel(self, channel):
|
|
48
48
|
pattern = r"([A-Z]+)(_(Open|pos1|pos2))?_det(\d)"
|
|
49
|
-
match = re.match(pattern,
|
|
49
|
+
match = re.match(pattern, channel, flags=re.IGNORECASE)
|
|
50
50
|
if not match:
|
|
51
|
-
raise ValueError(f"Invalid
|
|
51
|
+
raise ValueError(f"Invalid channel format: {channel}")
|
|
52
52
|
band = match.group(1).upper()
|
|
53
53
|
if match.group(3) is not None:
|
|
54
54
|
decker = match.group(3).lower().capitalize()
|
|
@@ -57,9 +57,9 @@ class ANDES(Instrument):
|
|
|
57
57
|
detector = match.group(4)
|
|
58
58
|
return band, decker, detector
|
|
59
59
|
|
|
60
|
-
def get_expected_values(self, target, night,
|
|
60
|
+
def get_expected_values(self, target, night, channel):
|
|
61
61
|
expectations = super().get_expected_values(target, night)
|
|
62
|
-
band, decker, detector = self.
|
|
62
|
+
band, decker, detector = self.parse_channel(channel)
|
|
63
63
|
|
|
64
64
|
for key in expectations.keys():
|
|
65
65
|
if key == "bias":
|
|
@@ -69,28 +69,28 @@ class ANDES(Instrument):
|
|
|
69
69
|
|
|
70
70
|
return expectations
|
|
71
71
|
|
|
72
|
-
def get_extension(self, header,
|
|
73
|
-
band, decker, detector = self.
|
|
72
|
+
def get_extension(self, header, channel):
|
|
73
|
+
band, decker, detector = self.parse_channel(channel)
|
|
74
74
|
extension = int(detector)
|
|
75
75
|
return extension
|
|
76
76
|
|
|
77
|
-
def get_wavecal_filename(self, header,
|
|
77
|
+
def get_wavecal_filename(self, header, channel, **kwargs):
|
|
78
78
|
"""Get the filename of the wavelength calibration config file"""
|
|
79
79
|
cwd = os.path.dirname(__file__)
|
|
80
|
-
fname = f"{self.name}_{
|
|
80
|
+
fname = f"{self.name}_{channel}.npz"
|
|
81
81
|
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
82
82
|
return fname
|
|
83
83
|
|
|
84
|
-
def get_mask_filename(self,
|
|
84
|
+
def get_mask_filename(self, channel, **kwargs):
|
|
85
85
|
i = self.name.lower()
|
|
86
|
-
band, decker, detector = self.
|
|
86
|
+
band, decker, detector = self.parse_channel(channel)
|
|
87
87
|
|
|
88
88
|
fname = f"mask_{i}_det{detector}.fits.gz"
|
|
89
89
|
cwd = os.path.dirname(__file__)
|
|
90
90
|
fname = os.path.join(cwd, "..", "masks", fname)
|
|
91
91
|
return fname
|
|
92
92
|
|
|
93
|
-
def get_wavelength_range(self, header,
|
|
93
|
+
def get_wavelength_range(self, header, channel, **kwargs):
|
|
94
94
|
wmin = [header["ESO INS WLEN MIN%i" % i] for i in range(1, 11)]
|
|
95
95
|
wmax = [header["ESO INS WLEN MAX%i" % i] for i in range(1, 11)]
|
|
96
96
|
|
pyreduce/instruments/andes.yaml
CHANGED
pyreduce/instruments/common.py
CHANGED
|
@@ -18,7 +18,7 @@ from dateutil import parser
|
|
|
18
18
|
from tqdm import tqdm
|
|
19
19
|
|
|
20
20
|
from ..clipnflip import clipnflip
|
|
21
|
-
from .filters import
|
|
21
|
+
from .filters import ChannelFilter, Filter, InstrumentFilter, NightFilter, ObjectFilter
|
|
22
22
|
from .models import InstrumentConfig
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
@@ -58,18 +58,18 @@ def observation_date_to_night(observation_date):
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class getter:
|
|
61
|
-
"""Get data from a header/dict, based on the given
|
|
61
|
+
"""Get data from a header/dict, based on the given channel, and applies replacements"""
|
|
62
62
|
|
|
63
|
-
def __init__(self, header, info,
|
|
63
|
+
def __init__(self, header, info, channel):
|
|
64
64
|
self.header = header
|
|
65
65
|
self.info = info.copy()
|
|
66
66
|
try:
|
|
67
|
-
self.index = find_first_index(info["
|
|
67
|
+
self.index = find_first_index(info["channels"], channel.upper())
|
|
68
68
|
except KeyError:
|
|
69
|
-
logger.warning("No instrument
|
|
69
|
+
logger.warning("No instrument channels found in instrument info")
|
|
70
70
|
self.index = 0
|
|
71
71
|
|
|
72
|
-
# Pick values for the given
|
|
72
|
+
# Pick values for the given channel
|
|
73
73
|
for k, v in self.info.items():
|
|
74
74
|
if isinstance(v, list):
|
|
75
75
|
self.info[k] = v[self.index]
|
|
@@ -133,10 +133,10 @@ class Instrument:
|
|
|
133
133
|
self.science = "science"
|
|
134
134
|
self.shared = ["instrument", "night"]
|
|
135
135
|
|
|
136
|
-
# Add
|
|
137
|
-
if self.config.
|
|
138
|
-
self.filters["
|
|
139
|
-
self.shared.append("
|
|
136
|
+
# Add channel filter if kw_channel is defined (for instruments with separate files per channel)
|
|
137
|
+
if self.config.kw_channel is not None:
|
|
138
|
+
self.filters["channel"] = ChannelFilter(self.config.kw_channel)
|
|
139
|
+
self.shared.append("channel")
|
|
140
140
|
self.find_closest = [
|
|
141
141
|
"bias",
|
|
142
142
|
"flat",
|
|
@@ -151,9 +151,9 @@ class Instrument:
|
|
|
151
151
|
return self.name
|
|
152
152
|
|
|
153
153
|
@property
|
|
154
|
-
def
|
|
155
|
-
"""Available instrument
|
|
156
|
-
return self.config.
|
|
154
|
+
def channels(self) -> list[str] | None:
|
|
155
|
+
"""Available instrument channels."""
|
|
156
|
+
return self.config.channels
|
|
157
157
|
|
|
158
158
|
@property
|
|
159
159
|
def extension(self) -> int | str | list:
|
|
@@ -170,17 +170,17 @@ class Instrument:
|
|
|
170
170
|
"""Instrument identifier for header matching."""
|
|
171
171
|
return self.config.id_instrument
|
|
172
172
|
|
|
173
|
-
def get(self, key, header,
|
|
174
|
-
get = getter(header, self.info,
|
|
173
|
+
def get(self, key, header, channel, alt=None):
|
|
174
|
+
get = getter(header, self.info, channel)
|
|
175
175
|
return get(key, alt=alt)
|
|
176
176
|
|
|
177
|
-
def get_extension(self, header,
|
|
178
|
-
|
|
177
|
+
def get_extension(self, header, channel):
|
|
178
|
+
channel = channel.upper()
|
|
179
179
|
ext = self.extension # Use property
|
|
180
180
|
|
|
181
181
|
if isinstance(ext, list):
|
|
182
|
-
|
|
183
|
-
ext = ext[
|
|
182
|
+
ichannel = find_first_index(self.channels, channel)
|
|
183
|
+
ext = ext[ichannel]
|
|
184
184
|
|
|
185
185
|
return ext
|
|
186
186
|
|
|
@@ -197,8 +197,8 @@ class Instrument:
|
|
|
197
197
|
dictionary of REDUCE names for properties to Header keywords/static values
|
|
198
198
|
"""
|
|
199
199
|
# Tips & Tricks:
|
|
200
|
-
# if several
|
|
201
|
-
# if a value changes depending on the
|
|
200
|
+
# if several channels are supported, use a list for channels
|
|
201
|
+
# if a value changes depending on the channel, use a list with the same order as "channels"
|
|
202
202
|
# you can also use values from this dictionary as placeholders using {name}, just like str.format
|
|
203
203
|
|
|
204
204
|
this = os.path.dirname(__file__)
|
|
@@ -225,13 +225,13 @@ class Instrument:
|
|
|
225
225
|
return config, info
|
|
226
226
|
|
|
227
227
|
def load_fits(
|
|
228
|
-
self, fname,
|
|
228
|
+
self, fname, channel, extension=None, mask=None, header_only=False, dtype=None
|
|
229
229
|
):
|
|
230
230
|
"""
|
|
231
231
|
load fits file, REDUCE style
|
|
232
232
|
|
|
233
233
|
primary and extension header are combined
|
|
234
|
-
|
|
234
|
+
channel-specific info is applied to header
|
|
235
235
|
data is clipnflipped
|
|
236
236
|
mask is applied
|
|
237
237
|
|
|
@@ -241,8 +241,8 @@ class Instrument:
|
|
|
241
241
|
filename
|
|
242
242
|
instrument : str
|
|
243
243
|
name of the instrument
|
|
244
|
-
|
|
245
|
-
instrument
|
|
244
|
+
channel : str
|
|
245
|
+
instrument channel
|
|
246
246
|
extension : int
|
|
247
247
|
data extension of the FITS file to load
|
|
248
248
|
mask : array, optional
|
|
@@ -257,22 +257,22 @@ class Instrument:
|
|
|
257
257
|
data : masked_array
|
|
258
258
|
FITS data, clipped and flipped, and with mask
|
|
259
259
|
header : fits.header
|
|
260
|
-
FITS header (Primary and Extension +
|
|
260
|
+
FITS header (Primary and Extension + channel info)
|
|
261
261
|
|
|
262
262
|
ONLY the header is returned if header_only is True
|
|
263
263
|
"""
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
channel = channel.upper()
|
|
266
266
|
|
|
267
267
|
hdu = fits.open(fname)
|
|
268
268
|
h_prime = hdu[0].header
|
|
269
269
|
if extension is None:
|
|
270
|
-
extension = self.get_extension(h_prime,
|
|
270
|
+
extension = self.get_extension(h_prime, channel)
|
|
271
271
|
|
|
272
272
|
header = hdu[extension].header
|
|
273
273
|
if extension != 0:
|
|
274
274
|
header.extend(h_prime, strip=False)
|
|
275
|
-
header = self.add_header_info(header,
|
|
275
|
+
header = self.add_header_info(header, channel)
|
|
276
276
|
header["e_input"] = (os.path.basename(fname), "Original input filename")
|
|
277
277
|
|
|
278
278
|
if header_only:
|
|
@@ -289,15 +289,15 @@ class Instrument:
|
|
|
289
289
|
hdu.close()
|
|
290
290
|
return data, header
|
|
291
291
|
|
|
292
|
-
def add_header_info(self, header,
|
|
292
|
+
def add_header_info(self, header, channel, **kwargs):
|
|
293
293
|
"""read data from header and add it as REDUCE keyword back to the header
|
|
294
294
|
|
|
295
295
|
Parameters
|
|
296
296
|
----------
|
|
297
297
|
header : fits.header, dict
|
|
298
298
|
header to read/write info from/to
|
|
299
|
-
|
|
300
|
-
instrument
|
|
299
|
+
channel : str
|
|
300
|
+
instrument channel
|
|
301
301
|
|
|
302
302
|
Returns
|
|
303
303
|
-------
|
|
@@ -306,7 +306,7 @@ class Instrument:
|
|
|
306
306
|
"""
|
|
307
307
|
|
|
308
308
|
info = self.info
|
|
309
|
-
get = getter(header, info,
|
|
309
|
+
get = getter(header, info, channel)
|
|
310
310
|
|
|
311
311
|
# Use HIERARCH prefix only for FITS Header objects to avoid warnings
|
|
312
312
|
# For dict objects, HIERARCH is not needed and would break key access
|
|
@@ -386,7 +386,7 @@ class Instrument:
|
|
|
386
386
|
files = np.array(files)
|
|
387
387
|
return files
|
|
388
388
|
|
|
389
|
-
def get_expected_values(self, target, night,
|
|
389
|
+
def get_expected_values(self, target, night, channel=None, **kwargs):
|
|
390
390
|
expectations = {
|
|
391
391
|
"bias": {
|
|
392
392
|
"instrument": self.config.id_instrument,
|
|
@@ -431,13 +431,15 @@ class Instrument:
|
|
|
431
431
|
},
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
# Add
|
|
435
|
-
if
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
434
|
+
# Add channel filter if this instrument has separate files per channel
|
|
435
|
+
if channel is not None and self.config.kw_channel is not None:
|
|
436
|
+
id_channel = self.config.id_channel
|
|
437
|
+
channels = self.config.channels
|
|
438
|
+
channel_id = (
|
|
439
|
+
id_channel[channels.index(channel)] if channel in channels else channel
|
|
440
|
+
)
|
|
439
441
|
for key in expectations:
|
|
440
|
-
expectations[key]["
|
|
442
|
+
expectations[key]["channel"] = channel_id
|
|
441
443
|
|
|
442
444
|
return expectations
|
|
443
445
|
|
|
@@ -618,8 +620,8 @@ class Instrument:
|
|
|
618
620
|
name of the target as in the fits headers
|
|
619
621
|
night : str
|
|
620
622
|
observation night, possibly with wildcards
|
|
621
|
-
|
|
622
|
-
instrument
|
|
623
|
+
channel : str
|
|
624
|
+
instrument channel
|
|
623
625
|
Returns
|
|
624
626
|
-------
|
|
625
627
|
files_per_night : list[dict{str:dict{str:list[str]}}]
|
|
@@ -638,15 +640,15 @@ class Instrument:
|
|
|
638
640
|
)
|
|
639
641
|
return files
|
|
640
642
|
|
|
641
|
-
def get_wavecal_filename(self, header,
|
|
643
|
+
def get_wavecal_filename(self, header, channel, **kwargs):
|
|
642
644
|
"""Get the filename of the pre-existing wavelength solution for the current setting
|
|
643
645
|
|
|
644
646
|
Parameters
|
|
645
647
|
----------
|
|
646
648
|
header : fits.header, dict
|
|
647
649
|
header of the wavelength calibration file
|
|
648
|
-
|
|
649
|
-
instrument
|
|
650
|
+
channel : str
|
|
651
|
+
instrument channel
|
|
650
652
|
|
|
651
653
|
Returns
|
|
652
654
|
-------
|
|
@@ -658,23 +660,23 @@ class Instrument:
|
|
|
658
660
|
instrument = "wavecal"
|
|
659
661
|
|
|
660
662
|
cwd = os.path.dirname(__file__)
|
|
661
|
-
fname = f"{instrument.lower()}_{
|
|
663
|
+
fname = f"{instrument.lower()}_{channel}_{specifier}.npz"
|
|
662
664
|
fname = os.path.join(cwd, "..", "wavecal", fname)
|
|
663
665
|
return fname
|
|
664
666
|
|
|
665
|
-
def
|
|
666
|
-
return self.
|
|
667
|
+
def get_supported_channels(self):
|
|
668
|
+
return self.channels
|
|
667
669
|
|
|
668
|
-
def get_mask_filename(self,
|
|
670
|
+
def get_mask_filename(self, channel, **kwargs):
|
|
669
671
|
i = self.name.lower()
|
|
670
|
-
|
|
671
|
-
fname = f"mask_{i}_{
|
|
672
|
+
c = channel.lower()
|
|
673
|
+
fname = f"mask_{i}_{c}.fits.gz"
|
|
672
674
|
cwd = os.path.dirname(__file__)
|
|
673
675
|
fname = os.path.join(cwd, "..", "masks", fname)
|
|
674
676
|
return fname
|
|
675
677
|
|
|
676
|
-
def get_wavelength_range(self, header,
|
|
677
|
-
return self.get("wavelength_range", header,
|
|
678
|
+
def get_wavelength_range(self, header, channel, **kwargs):
|
|
679
|
+
return self.get("wavelength_range", header, channel)
|
|
678
680
|
|
|
679
681
|
|
|
680
682
|
class COMMON(Instrument):
|
|
@@ -699,13 +701,13 @@ def create_custom_instrument(
|
|
|
699
701
|
except:
|
|
700
702
|
return None, info
|
|
701
703
|
|
|
702
|
-
def get_extension(self, header,
|
|
704
|
+
def get_extension(self, header, channel):
|
|
703
705
|
return extension
|
|
704
706
|
|
|
705
|
-
def get_mask_filename(self,
|
|
707
|
+
def get_mask_filename(self, channel, **kwargs):
|
|
706
708
|
return mask_file
|
|
707
709
|
|
|
708
|
-
def get_wavecal_filename(self, header,
|
|
710
|
+
def get_wavecal_filename(self, header, channel, **kwargs):
|
|
709
711
|
return wavecal_file
|
|
710
712
|
|
|
711
713
|
return CUSTOM()
|