This document summarizes the major changes for users coming back to PyReduce after the 0.7/0.8 series.
Previously, trace data was scattered across parallel arrays (traces, column_range, curvature, wave_coef) stored in separate .npz files. These could easily get out of sync.
Now all trace data lives in a single Trace dataclass:
@dataclass
class Trace:
m: int | None # spectral order number
group: str | int | None # fiber group ('A', 'B', 'cal', ...)
fiber_idx: int | None # fiber index within group
pos: np.ndarray # y(x) position polynomial
column_range: tuple[int, int]
height: float | None # extraction aperture height
slit: np.ndarray | None # curvature coefficients (filled by curvature step)
slitdelta: np.ndarray | None
wave: np.ndarray | None # wavelength polynomial (filled by wavecal step)Pipeline steps update traces in-place as they run. All trace data is saved to a single .traces.fits file. Old .npz files are still readable.
See trace_model.py for the full implementation.
The old Echelle class stored spectra as unlabeled 2D arrays — after extraction there was no way to know which row was which order or fiber.
Now each extracted spectrum is a Spectrum object with full metadata (order number, group, extraction parameters), saved as a FITS binary table with one row per trace. Invalid pixels use NaN instead of a separate mask array.
spectra = Spectra.read("file.science.fits")
for s in spectra:
print(s.m, s.group, s.spec.shape)See output_formats.md for the file format specification.
A deprecated echelle.py shim still exists for backward compatibility but will be removed in a future version.
PyReduce now handles instruments with fiber bundles (MOSAIC, ANDES, HARPSPOL):
- Fiber groups defined in instrument YAML (
fibersconfig) - Bundle detection for repeating fiber patterns (IFU, multi-fiber pseudo-slits)
- Per-step trace selection (
science: [A, B],wavecal: [cal]) - Merged traces — fibers within a group can be averaged for extraction
See fiber_bundle_tracing.md for details.
The old pyreduce.reduce.main() is replaced by a fluent Pipeline API:
from pyreduce.pipeline import Pipeline
Pipeline.from_instrument(
instrument="UVES",
target="HD132205",
steps=("bias", "flat", "trace", "science"),
).run()The old main() still works but emits a deprecation warning.
The argparse CLI is replaced with Click. Individual steps are now top-level commands:
uv run reduce run UVES -t HD132205 --steps bias,flat,trace,science
uv run reduce trace UVES -t HD132205
uv run reduce download UVESSee cli.md for the full reference.
- Instrument configs use YAML (validated by Pydantic), organized into
instruments/{NAME}/directories - Settings cascade:
defaults/settings.json<{INSTRUMENT}/settings.json<{INSTRUMENT}/settings_{channel}.json< runtime overrides modeis now calledchannel
See configuration_file.md for details.
| Old | New | Reason |
|---|---|---|
nord / iord |
ntrace / idx |
"order" conflated spectral order (m) with trace index |
orders step |
trace step |
|
extraction_width |
extraction_height |
dispersion is horizontal, extraction is vertical |
mode |
channel |
|
.ech output |
.fits output |
standard format |
| Mask 0=bad, 1=good | 1=bad, 0=good | numpy convention |
Two extraction backends are available:
- CFFI (default) — the original C slit function decomposition, supports curvature degree 1-2
- Charslit (optional) — a newer backend supporting curvature up to degree 5 and per-row slitdelta corrections. Install with
uv sync --extra charslitand enable withPYREDUCE_USE_CHARSLIT=1.
Explicit Detector and Amplifier classes for instruments with multiple readout amplifiers or detectors. Currently handled via the channels config parameter.
Declarative config for instruments with mode explosion (e.g. CRIRES+: 29 bands x 3 deckers x 3 detectors).