pyreduce-astro 0.7a5__cp314-cp314-win_amd64.whl → 0.7a7__cp314-cp314-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 (48) hide show
  1. pyreduce/__main__.py +114 -20
  2. pyreduce/cli.py +1 -1
  3. pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
  4. pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
  5. pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
  6. pyreduce/clib/_slitfunc_2d.cp314-win_amd64.pyd +0 -0
  7. pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
  8. pyreduce/clib/_slitfunc_bd.cp314-win_amd64.pyd +0 -0
  9. pyreduce/combine_frames.py +8 -0
  10. pyreduce/configuration.py +20 -0
  11. pyreduce/estimate_background_scatter.py +8 -8
  12. pyreduce/extract.py +126 -126
  13. pyreduce/{extraction_width.py → extraction_height.py} +4 -4
  14. pyreduce/instruments/common.py +201 -15
  15. pyreduce/instruments/harpn.py +2 -2
  16. pyreduce/instruments/harps.py +2 -2
  17. pyreduce/instruments/models.py +24 -0
  18. pyreduce/instruments/neid.py +49 -77
  19. pyreduce/instruments/neid.yaml +39 -40
  20. pyreduce/instruments/nirspec.py +2 -2
  21. pyreduce/make_shear.py +12 -12
  22. pyreduce/pipeline.py +6 -6
  23. pyreduce/rectify.py +10 -10
  24. pyreduce/reduce.py +14 -14
  25. pyreduce/settings/settings_AJ.json +2 -2
  26. pyreduce/settings/settings_ANDES.json +7 -7
  27. pyreduce/settings/settings_CRIRES_PLUS.json +7 -7
  28. pyreduce/settings/settings_HARPN.json +7 -7
  29. pyreduce/settings/settings_HARPS.json +7 -7
  30. pyreduce/settings/settings_JWST_MIRI.json +7 -7
  31. pyreduce/settings/settings_JWST_NIRISS.json +7 -7
  32. pyreduce/settings/settings_LICK_APF.json +7 -7
  33. pyreduce/settings/settings_MCDONALD.json +7 -7
  34. pyreduce/settings/settings_METIS_IFU.json +7 -7
  35. pyreduce/settings/settings_METIS_LSS.json +7 -7
  36. pyreduce/settings/settings_MICADO.json +7 -7
  37. pyreduce/settings/settings_NEID.json +15 -24
  38. pyreduce/settings/settings_NIRSPEC.json +7 -7
  39. pyreduce/settings/settings_NTE.json +6 -6
  40. pyreduce/settings/settings_UVES.json +4 -4
  41. pyreduce/settings/settings_XSHOOTER.json +5 -5
  42. pyreduce/settings/settings_pyreduce.json +8 -8
  43. pyreduce/settings/settings_schema.json +10 -10
  44. {pyreduce_astro-0.7a5.dist-info → pyreduce_astro-0.7a7.dist-info}/METADATA +3 -3
  45. {pyreduce_astro-0.7a5.dist-info → pyreduce_astro-0.7a7.dist-info}/RECORD +48 -48
  46. {pyreduce_astro-0.7a5.dist-info → pyreduce_astro-0.7a7.dist-info}/WHEEL +0 -0
  47. {pyreduce_astro-0.7a5.dist-info → pyreduce_astro-0.7a7.dist-info}/entry_points.txt +0 -0
  48. {pyreduce_astro-0.7a5.dist-info → pyreduce_astro-0.7a7.dist-info}/licenses/LICENSE +0 -0
pyreduce/__main__.py CHANGED
@@ -4,8 +4,8 @@ PyReduce command-line interface.
4
4
  Usage:
5
5
  uv run reduce --help
6
6
  uv run reduce run UVES HD132205 --night 2010-04-01
7
- uv run reduce run UVES HD132205 --steps bias,flat,orders
8
- uv run reduce bias UVES HD132205
7
+ uv run reduce run UVES HD132205 --steps bias,flat,trace
8
+ uv run reduce trace UVES HD132205
9
9
  uv run reduce combine --output combined.fits *.final.fits
10
10
  """
11
11
 
@@ -14,7 +14,7 @@ import click
14
14
  ALL_STEPS = (
15
15
  "bias",
16
16
  "flat",
17
- "orders",
17
+ "trace",
18
18
  "curvature",
19
19
  "scatter",
20
20
  "norm_flat",
@@ -67,6 +67,12 @@ def cli():
67
67
  default=None,
68
68
  help="Order range to process (e.g., '1,21')",
69
69
  )
70
+ @click.option(
71
+ "--settings",
72
+ default=None,
73
+ type=click.Path(exists=True),
74
+ help="JSON file with settings overrides",
75
+ )
70
76
  def run(
71
77
  instrument,
72
78
  target,
@@ -78,13 +84,14 @@ def run(
78
84
  output_dir,
79
85
  plot,
80
86
  order_range,
87
+ settings,
81
88
  ):
82
89
  """Run the reduction pipeline.
83
90
 
84
91
  INSTRUMENT: Name of the instrument (e.g., UVES, HARPS, XSHOOTER)
85
92
  TARGET: Target star name or regex pattern
86
93
  """
87
- from .configuration import get_configuration_for_instrument
94
+ from .configuration import get_configuration_for_instrument, load_settings_override
88
95
  from .reduce import main as reduce_main
89
96
 
90
97
  # Parse steps
@@ -100,6 +107,8 @@ def run(
100
107
 
101
108
  # Load configuration
102
109
  config = get_configuration_for_instrument(instrument)
110
+ if settings:
111
+ config = load_settings_override(config, settings)
103
112
 
104
113
  # Run reduction
105
114
  reduce_main(
@@ -279,30 +288,115 @@ def make_step_command(step_name):
279
288
 
280
289
  @click.command(name=step_name)
281
290
  @click.argument("instrument")
282
- @click.argument("target")
291
+ @click.argument("target", required=False, default="")
283
292
  @click.option("--night", "-n", default=None, help="Observation night")
284
293
  @click.option("--channel", "-c", default=None, help="Instrument channel")
285
294
  @click.option("--base-dir", "-b", default=None, help="Base directory")
286
295
  @click.option("--input-dir", "-i", default="raw", help="Input directory")
287
296
  @click.option("--output-dir", "-o", default="reduced", help="Output directory")
288
297
  @click.option("--plot", "-p", default=0, help="Plot level")
289
- def cmd(instrument, target, night, channel, base_dir, input_dir, output_dir, plot):
290
- from .configuration import get_configuration_for_instrument
298
+ @click.option(
299
+ "--file",
300
+ "-f",
301
+ default=None,
302
+ help="Specific input file (bypasses file discovery)",
303
+ )
304
+ @click.option(
305
+ "--settings",
306
+ default=None,
307
+ type=click.Path(exists=True),
308
+ help="JSON file with settings overrides",
309
+ )
310
+ def cmd(
311
+ instrument,
312
+ target,
313
+ night,
314
+ channel,
315
+ base_dir,
316
+ input_dir,
317
+ output_dir,
318
+ plot,
319
+ file,
320
+ settings,
321
+ ):
322
+ from .configuration import (
323
+ get_configuration_for_instrument,
324
+ load_settings_override,
325
+ )
291
326
  from .reduce import main as reduce_main
292
327
 
293
- config = get_configuration_for_instrument(instrument)
294
- reduce_main(
295
- instrument=instrument,
296
- target=target,
297
- night=night,
298
- channels=channel,
299
- steps=(step_name,),
300
- base_dir=base_dir or "",
301
- input_dir=input_dir,
302
- output_dir=output_dir,
303
- configuration=config,
304
- plot=plot,
305
- )
328
+ if file:
329
+ # Direct file mode: run step on specific file
330
+ import os
331
+
332
+ import numpy as np
333
+
334
+ from . import reduce as reduce_module
335
+ from .instruments.instrument_info import load_instrument
336
+
337
+ inst = load_instrument(instrument)
338
+ channel = channel or (inst.channels[0] if inst.channels else "")
339
+ output_dir_full = output_dir
340
+ if base_dir:
341
+ output_dir_full = os.path.join(base_dir, output_dir)
342
+ os.makedirs(output_dir_full, exist_ok=True)
343
+
344
+ # Load configuration for this step
345
+ config = get_configuration_for_instrument(instrument)
346
+ if settings:
347
+ config = load_settings_override(config, settings)
348
+ step_config = config.get(step_name, {})
349
+ step_config["plot"] = plot
350
+
351
+ # Get the step class
352
+ step_classes = {
353
+ "bias": reduce_module.Bias,
354
+ "flat": reduce_module.Flat,
355
+ "trace": reduce_module.OrderTracing,
356
+ "curvature": reduce_module.SlitCurvatureDetermination,
357
+ "scatter": reduce_module.BackgroundScatter,
358
+ "norm_flat": reduce_module.NormalizeFlatField,
359
+ "wavecal_master": reduce_module.WavelengthCalibrationMaster,
360
+ "wavecal_init": reduce_module.WavelengthCalibrationInitialize,
361
+ "wavecal": reduce_module.WavelengthCalibrationFinalize,
362
+ "freq_comb_master": reduce_module.LaserFrequencyCombMaster,
363
+ "freq_comb": reduce_module.LaserFrequencyCombFinalize,
364
+ "science": reduce_module.ScienceExtraction,
365
+ "continuum": reduce_module.ContinuumNormalization,
366
+ }
367
+
368
+ if step_name not in step_classes:
369
+ raise click.ClickException(
370
+ f"Step '{step_name}' does not support --file option"
371
+ )
372
+
373
+ step_class = step_classes[step_name]
374
+ step = step_class(
375
+ inst,
376
+ channel,
377
+ target=target or "",
378
+ night=night,
379
+ output_dir=output_dir_full,
380
+ order_range=None,
381
+ **step_config,
382
+ )
383
+ step.run(files=np.array([file]), mask=None, bias=None)
384
+ else:
385
+ config = get_configuration_for_instrument(instrument)
386
+ if settings:
387
+ config = load_settings_override(config, settings)
388
+ reduce_main(
389
+ instrument=instrument,
390
+ target=target,
391
+ night=night,
392
+ channels=channel,
393
+ steps=(step_name,),
394
+ base_dir=base_dir or "",
395
+ input_dir=input_dir,
396
+ output_dir=output_dir,
397
+ configuration=config,
398
+ plot=plot,
399
+ )
306
400
 
307
401
  cmd.__doc__ = f"Run the '{step_name}' step."
308
402
  return cmd
pyreduce/cli.py CHANGED
@@ -287,7 +287,7 @@ def run(config_file: str, steps: str, skip_existing: bool, plot: int):
287
287
  pipe = pipe.flat(_expand_globs(files["flat"]))
288
288
 
289
289
  if "trace" in config_steps:
290
- trace_files = files.get("orders") or files.get("flat")
290
+ trace_files = files.get("trace") or files.get("flat")
291
291
  pipe = pipe.trace_orders(_expand_globs(trace_files) if trace_files else None)
292
292
 
293
293
  if "scatter" in config_steps:
Binary file
Binary file
@@ -305,6 +305,14 @@ def combine_frames(
305
305
  if instrument is None or isinstance(instrument, str):
306
306
  instrument = load_instrument(instrument)
307
307
 
308
+ # For multi-amplifier instruments, use simple combination since the
309
+ # row-by-row approach doesn't work with multi-extension assembly
310
+ if instrument.config.amplifiers is not None:
311
+ logger.debug("Multi-amplifier instrument detected, using simple combination")
312
+ return combine_frames_simple(
313
+ files, instrument, channel, extension=extension, dtype=dtype, **kwargs
314
+ )
315
+
308
316
  # summarize file info
309
317
  logger.debug("Files:")
310
318
  for i, fname in zip(range(len(files)), files, strict=False):
pyreduce/configuration.py CHANGED
@@ -43,6 +43,26 @@ def get_configuration_for_instrument(instrument, **kwargs):
43
43
  return config
44
44
 
45
45
 
46
+ def load_settings_override(config, settings_file):
47
+ """Apply settings overrides from a JSON file.
48
+
49
+ Parameters
50
+ ----------
51
+ config : dict
52
+ Base configuration to override
53
+ settings_file : str
54
+ Path to JSON file with override settings
55
+
56
+ Returns
57
+ -------
58
+ config : dict
59
+ Updated configuration
60
+ """
61
+ with open(settings_file) as f:
62
+ overrides = json.load(f)
63
+ return update(config, overrides, check=False)
64
+
65
+
46
66
  def load_config(configuration, instrument, j=0):
47
67
  if configuration is None:
48
68
  logger.info(
@@ -18,7 +18,7 @@ def estimate_background_scatter(
18
18
  img,
19
19
  orders,
20
20
  column_range=None,
21
- extraction_width=0.1,
21
+ extraction_height=0.1,
22
22
  scatter_degree=4,
23
23
  sigma_cutoff=2,
24
24
  border_width=10,
@@ -38,7 +38,7 @@ def estimate_background_scatter(
38
38
  order polynomial coefficients
39
39
  column_range : array[nord, 2], optional
40
40
  range of columns to use in each order (default: None == all columns)
41
- extraction_width : float, array[nord, 2], optional
41
+ extraction_height : float, array[nord, 2], optional
42
42
  extraction width for each order, values below 1.5 are considered fractional, others as number of pixels (default: 0.1)
43
43
  scatter_degree : int, optional
44
44
  polynomial degree of the 2d fit for the background scatter (default: 4)
@@ -56,8 +56,8 @@ def estimate_background_scatter(
56
56
  nrow, ncol = img.shape
57
57
  nord, _ = orders.shape
58
58
 
59
- extraction_width, column_range, orders = fix_parameters(
60
- extraction_width,
59
+ extraction_height, column_range, orders = fix_parameters(
60
+ extraction_height,
61
61
  column_range,
62
62
  orders,
63
63
  nrow,
@@ -73,16 +73,16 @@ def estimate_background_scatter(
73
73
  mask[:bw] = mask[-bw:] = mask[:, :bw] = mask[:, -bw:] = False
74
74
  for i in range(nord):
75
75
  left, right = column_range[i]
76
- left -= extraction_width[i, 1] * 2
77
- right += extraction_width[i, 0] * 2
76
+ left -= extraction_height[i, 1] * 2
77
+ right += extraction_height[i, 0] * 2
78
78
  left = max(0, left)
79
79
  right = min(ncol, right)
80
80
 
81
81
  x_order = np.arange(left, right)
82
82
  y_order = np.polyval(orders[i], x_order)
83
83
 
84
- y_above = y_order + extraction_width[i, 1]
85
- y_below = y_order - extraction_width[i, 0]
84
+ y_above = y_order + extraction_height[i, 1]
85
+ y_below = y_order - extraction_height[i, 0]
86
86
 
87
87
  y_above = np.floor(y_above)
88
88
  y_below = np.ceil(y_below)