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.
Files changed (65) hide show
  1. pyreduce/__main__.py +6 -6
  2. pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
  3. pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
  4. pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
  5. pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
  6. pyreduce/combine_frames.py +25 -25
  7. pyreduce/instruments/aj.yaml +1 -1
  8. pyreduce/instruments/andes.py +17 -17
  9. pyreduce/instruments/andes.yaml +1 -1
  10. pyreduce/instruments/common.py +58 -56
  11. pyreduce/instruments/crires_plus.py +26 -36
  12. pyreduce/instruments/crires_plus.yaml +0 -2
  13. pyreduce/instruments/filters.py +1 -1
  14. pyreduce/instruments/harpn.py +12 -12
  15. pyreduce/instruments/harpn.yaml +2 -2
  16. pyreduce/instruments/harps.py +19 -12
  17. pyreduce/instruments/harps.yaml +2 -2
  18. pyreduce/instruments/instrument_info.py +14 -14
  19. pyreduce/instruments/jwst_miri.py +6 -4
  20. pyreduce/instruments/jwst_miri.yaml +2 -2
  21. pyreduce/instruments/jwst_niriss.py +12 -8
  22. pyreduce/instruments/jwst_niriss.yaml +2 -2
  23. pyreduce/instruments/lick_apf.py +3 -3
  24. pyreduce/instruments/lick_apf.yaml +2 -2
  25. pyreduce/instruments/mcdonald.py +5 -5
  26. pyreduce/instruments/mcdonald.yaml +3 -3
  27. pyreduce/instruments/metis_ifu.py +5 -5
  28. pyreduce/instruments/metis_ifu.yaml +3 -3
  29. pyreduce/instruments/metis_lss.py +5 -5
  30. pyreduce/instruments/metis_lss.yaml +3 -3
  31. pyreduce/instruments/micado.py +5 -5
  32. pyreduce/instruments/micado.yaml +3 -3
  33. pyreduce/instruments/models.py +6 -6
  34. pyreduce/instruments/neid.py +12 -12
  35. pyreduce/instruments/neid.yaml +2 -2
  36. pyreduce/instruments/nirspec.py +8 -8
  37. pyreduce/instruments/nirspec.yaml +1 -1
  38. pyreduce/instruments/nte.py +6 -6
  39. pyreduce/instruments/nte.yaml +1 -1
  40. pyreduce/instruments/uves.py +8 -8
  41. pyreduce/instruments/uves.yaml +2 -2
  42. pyreduce/instruments/xshooter.py +4 -4
  43. pyreduce/instruments/xshooter.yaml +3 -3
  44. pyreduce/pipeline.py +26 -26
  45. pyreduce/reduce.py +34 -34
  46. {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/METADATA +4 -6
  47. {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/RECORD +53 -65
  48. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.exp +0 -0
  49. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.lib +0 -0
  50. pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.exp +0 -0
  51. pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.lib +0 -0
  52. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.exp +0 -0
  53. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.lib +0 -0
  54. pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.exp +0 -0
  55. pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.lib +0 -0
  56. pyreduce/clib/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
  57. pyreduce/clib/_slitfunc_2d.cp312-win_amd64.pyd +0 -0
  58. pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
  59. pyreduce/clib/_slitfunc_bd.cp312-win_amd64.pyd +0 -0
  60. /pyreduce/wavecal/{crires_plus_J1228_Open_det1.npz → crires_plus_J1228_det1.npz} +0 -0
  61. /pyreduce/wavecal/{crires_plus_J1228_Open_det2.npz → crires_plus_J1228_det2.npz} +0 -0
  62. /pyreduce/wavecal/{crires_plus_J1228_Open_det3.npz → crires_plus_J1228_det3.npz} +0 -0
  63. {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/WHEEL +0 -0
  64. {pyreduce_astro-0.7a4.dist-info → pyreduce_astro-0.7a5.dist-info}/entry_points.txt +0 -0
  65. {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("--arm", "-a", default=None, help="Instrument arm/detector")
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
- arm,
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
- arms=arm,
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("--arm", "-a", default=None, help="Instrument arm")
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, arm, base_dir, input_dir, output_dir, plot):
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
- arms=arm,
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
@@ -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, arm, extension=None, dtype=np.float32, **kwargs
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
- arm : str
161
- instrument arm
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], arm, dtype=dtype, extension=extension, **kwargs
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, arm, dtype=dtype, extension=extension, **kwargs
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
- arm,
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
- arm : str
280
- instrument arm
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], arm, dtype=dtype, extension=extension, **kwargs
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], arm, dtype=dtype, extension=extension, **kwargs
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], arm, dtype=dtype, extension=extension, **kwargs
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, arm, header_only=True, dtype=dtype, extension=extension, **kwargs
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, arm) for h in heads]
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
- arm,
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
- arm : str
549
- descriptor of the instrument arm
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, arm, mask=mask, **kwargs)
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, arm, mask, degree=1, plot=False, plot_title=None
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
- arm : str
652
- arm identifier for this instrument
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, arm) for f in tqdm(files)]
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
- arm,
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 arm for arminfo
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, arm, extension, **kwargs)
759
+ bias1, head1 = combine_frames(list1, instrument, channel, extension, **kwargs)
760
760
  bias1 /= n1
761
761
 
762
- bias2, head = combine_frames(list2, instrument, arm, extension, **kwargs)
762
+ bias2, head = combine_frames(list2, instrument, channel, extension, **kwargs)
763
763
  bias2 /= n2
764
764
 
765
765
  # Make sure we know the gain.
@@ -9,7 +9,7 @@ telescope: Simulated
9
9
  date: DATE-OBS
10
10
  date_format: fits
11
11
 
12
- arms: [ALL]
12
+ channels: [ALL]
13
13
  extension: 0
14
14
  orientation: 0
15
15
  transpose: false
@@ -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, arm, **kwargs):
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.parse_arm(arm)
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 get_supported_arms(self):
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
- arms = [
42
+ channels = [
43
43
  "_".join([s, d, c]) for s, d, c in product(settings, deckers, detectors)
44
44
  ]
45
- return arms
45
+ return channels
46
46
 
47
- def parse_arm(self, arm):
47
+ def parse_channel(self, channel):
48
48
  pattern = r"([A-Z]+)(_(Open|pos1|pos2))?_det(\d)"
49
- match = re.match(pattern, arm, flags=re.IGNORECASE)
49
+ match = re.match(pattern, channel, flags=re.IGNORECASE)
50
50
  if not match:
51
- raise ValueError(f"Invalid arm format: {arm}")
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, arm):
60
+ def get_expected_values(self, target, night, channel):
61
61
  expectations = super().get_expected_values(target, night)
62
- band, decker, detector = self.parse_arm(arm)
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, arm):
73
- band, decker, detector = self.parse_arm(arm)
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, arm, **kwargs):
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}_{arm}.npz"
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, arm, **kwargs):
84
+ def get_mask_filename(self, channel, **kwargs):
85
85
  i = self.name.lower()
86
- band, decker, detector = self.parse_arm(arm)
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, arm, **kwargs):
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
 
@@ -15,7 +15,7 @@ id_band: "ESO INS WLEN ID"
15
15
  id_decker: "ESO INS OPTI8 ID"
16
16
  id_lamp: "ESO INS1 LAMP? ID"
17
17
 
18
- arms: [SL, IFU]
18
+ channels: [SL, IFU]
19
19
  deckers: [Open, pos1, pos2]
20
20
  bands: [UBV, RIZ, YJH, K]
21
21
  settings: [B, V, R, IZ, Y, J, H, K]
@@ -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 ArmFilter, Filter, InstrumentFilter, NightFilter, ObjectFilter
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 arm, and applies replacements"""
61
+ """Get data from a header/dict, based on the given channel, and applies replacements"""
62
62
 
63
- def __init__(self, header, info, arm):
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["arms"], arm.upper())
67
+ self.index = find_first_index(info["channels"], channel.upper())
68
68
  except KeyError:
69
- logger.warning("No instrument arms found in instrument info")
69
+ logger.warning("No instrument channels found in instrument info")
70
70
  self.index = 0
71
71
 
72
- # Pick values for the given arm
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 arm filter if kw_arm is defined (for instruments with separate files per arm)
137
- if self.config.kw_arm is not None:
138
- self.filters["arm"] = ArmFilter(self.config.kw_arm)
139
- self.shared.append("arm")
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 arms(self) -> list[str] | None:
155
- """Available instrument arms (detectors/channels)."""
156
- return self.config.arms
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, arm, alt=None):
174
- get = getter(header, self.info, arm)
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, arm):
178
- arm = arm.upper()
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
- iarm = find_first_index(self.arms, arm)
183
- ext = ext[iarm]
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 arms are supported, use a list for arms
201
- # if a value changes depending on the arm, use a list with the same order as "arms"
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, arm, extension=None, mask=None, header_only=False, dtype=None
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
- arm-specific info is applied to header
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
- arm : str
245
- instrument arm (detector/channel)
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 + arm info)
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
- arm = arm.upper()
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, arm)
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, arm)
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, arm, **kwargs):
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
- arm : str
300
- instrument arm (detector/channel)
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, arm)
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, arm=None, **kwargs):
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 arm filter if this instrument has separate files per arm
435
- if arm is not None and self.config.kw_arm is not None:
436
- id_arm = self.config.id_arm
437
- arms = self.config.arms
438
- arm_id = id_arm[arms.index(arm)] if arm in arms else arm
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]["arm"] = arm_id
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
- arm : str
622
- instrument arm
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, arm, **kwargs):
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
- arm : str
649
- instrument arm
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()}_{arm}_{specifier}.npz"
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 get_supported_arms(self):
666
- return self.arms
667
+ def get_supported_channels(self):
668
+ return self.channels
667
669
 
668
- def get_mask_filename(self, arm, **kwargs):
670
+ def get_mask_filename(self, channel, **kwargs):
669
671
  i = self.name.lower()
670
- a = arm.lower()
671
- fname = f"mask_{i}_{a}.fits.gz"
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, arm, **kwargs):
677
- return self.get("wavelength_range", header, arm)
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, arm):
704
+ def get_extension(self, header, channel):
703
705
  return extension
704
706
 
705
- def get_mask_filename(self, arm, **kwargs):
707
+ def get_mask_filename(self, channel, **kwargs):
706
708
  return mask_file
707
709
 
708
- def get_wavecal_filename(self, header, arm, **kwargs):
710
+ def get_wavecal_filename(self, header, channel, **kwargs):
709
711
  return wavecal_file
710
712
 
711
713
  return CUSTOM()