pyreduce-astro 0.7a3__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 +42 -11
  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.7a3.dist-info → pyreduce_astro-0.7a5.dist-info}/METADATA +4 -6
  47. {pyreduce_astro-0.7a3.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.7a3.dist-info → pyreduce_astro-0.7a5.dist-info}/WHEEL +0 -0
  64. {pyreduce_astro-0.7a3.dist-info → pyreduce_astro-0.7a5.dist-info}/entry_points.txt +0 -0
  65. {pyreduce_astro-0.7a3.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,
@@ -161,20 +161,25 @@ def download(instrument):
161
161
  "--list", "-l", "list_examples", is_flag=True, help="List available examples"
162
162
  )
163
163
  @click.option("--all", "-a", "download_all", is_flag=True, help="Download all examples")
164
+ @click.option("--run", "-r", is_flag=True, help="Run the example after downloading")
164
165
  @click.option("--output", "-o", default=".", help="Output directory")
165
- def examples(filename, list_examples, download_all, output):
166
- """List or download example scripts from GitHub.
166
+ def examples(filename, list_examples, download_all, run, output):
167
+ """List, download, or run example scripts from GitHub.
167
168
 
168
169
  Downloads examples matching your installed PyReduce version.
169
170
 
170
171
  \b
171
172
  Examples:
172
- reduce examples # List available examples
173
- reduce examples uves_example.py # Download to current dir
174
- reduce examples --all -o ~/scripts # Download all to ~/scripts
173
+ reduce examples # List available examples
174
+ reduce examples uves_example.py # Download to current dir
175
+ reduce examples -r uves_example.py # Download and run
176
+ reduce examples --all -o ~/scripts # Download all to ~/scripts
175
177
  """
176
178
  import json
177
179
  import os
180
+ import subprocess
181
+ import sys
182
+ import tempfile
178
183
  import urllib.request
179
184
  from urllib.error import HTTPError
180
185
 
@@ -212,6 +217,32 @@ def examples(filename, list_examples, download_all, output):
212
217
  click.echo(f" {name}")
213
218
  return
214
219
 
220
+ if run and download_all:
221
+ raise click.ClickException("Cannot use --run with --all")
222
+
223
+ # Run mode: download to temp and execute
224
+ if run:
225
+ if filename not in example_files:
226
+ raise click.ClickException(
227
+ f"Unknown example '{filename}'. Use 'reduce examples --list' to see available."
228
+ )
229
+ url = f"{github_raw}/{filename}"
230
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
231
+ try:
232
+ with urllib.request.urlopen(url) as resp:
233
+ f.write(resp.read().decode())
234
+ temp_path = f.name
235
+ except HTTPError as e:
236
+ raise click.ClickException(
237
+ f"Failed to download {filename}: {e}"
238
+ ) from None
239
+ try:
240
+ click.echo(f"Running {filename}...")
241
+ result = subprocess.run([sys.executable, temp_path], check=False)
242
+ sys.exit(result.returncode)
243
+ finally:
244
+ os.unlink(temp_path)
245
+
215
246
  # Ensure output directory exists
216
247
  os.makedirs(output, exist_ok=True)
217
248
 
@@ -250,12 +281,12 @@ def make_step_command(step_name):
250
281
  @click.argument("instrument")
251
282
  @click.argument("target")
252
283
  @click.option("--night", "-n", default=None, help="Observation night")
253
- @click.option("--arm", "-a", default=None, help="Instrument arm")
284
+ @click.option("--channel", "-c", default=None, help="Instrument channel")
254
285
  @click.option("--base-dir", "-b", default=None, help="Base directory")
255
286
  @click.option("--input-dir", "-i", default="raw", help="Input directory")
256
287
  @click.option("--output-dir", "-o", default="reduced", help="Output directory")
257
288
  @click.option("--plot", "-p", default=0, help="Plot level")
258
- 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):
259
290
  from .configuration import get_configuration_for_instrument
260
291
  from .reduce import main as reduce_main
261
292
 
@@ -264,7 +295,7 @@ def make_step_command(step_name):
264
295
  instrument=instrument,
265
296
  target=target,
266
297
  night=night,
267
- arms=arm,
298
+ channels=channel,
268
299
  steps=(step_name,),
269
300
  base_dir=base_dir or "",
270
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]