- Background scatter is now measured on the frame it corrects. The
scatterstep fits its model on the flat lamp frames (every instrument'sid_scatterpoints at a flat), andScienceExtractionsubtracted those coefficients from science frames unchanged. Since a master flat is a sum of lamp exposures, the model is in the flux units of that stack and generally has no relation to a single science exposure: measured on XSHOOTER nir (10x400 s flats vs a 5 s science frame) the subtraction was 67x the signal at the trace centres, driving 99.97% of in-order pixels negative; on HARPS red (5x40 s vs 600 s) it under-subtracted at 0.14x.NormalizeFlatFieldwas unaffected, as it corrects the same flat the model was measured on. Consumers now re-estimate the background on the calibrated image they are about to extract, via the newScatterModel.refit, so the model matches that frame's own inter-order level by construction (1.000 on the XSHOOTER frame above, against 67x before). No exposure-time bookkeeping is involved and no settings changed - HARPSPOL: the
scatterstep is no longer declared, having been measured to remove most of the stellar flux at everyextraction_heighttried (#38, #39) - Scatter
extraction_heightwas narrower than the order footprint on two instruments, leaving order wings in the background fit and biasing it high: HARPS shipped 20 px against 77 px order spacing (now0.9of the spacing, model/truth 6.1x -> 1.3x) and LICK_APF 18 px with degree 4 (now0.6andscatter_degree: 2, 0.48x -> 1.2x, the degree drop because 22 px order spacing leaves too few inter-order pixels for a quartic). UVES moved 0.9 -> 1.0 (1.23x -> 1.04x). Measured against the median of pixels further than 0.4x the order spacing from any trace. Seescatter.md
- The
scatterstep returns aScatterModel(coefficients plus the fit parameters) rather than a bare coefficient array;.scatter.npzon disk is unchanged and older files still load. A bare array passed to a consumer is still honoured, with a warning that its flux scale is unknown and it cannot be re-estimated
- HARPS
wavecal_{blue,red}_pol_2D.npzand thepolarimetrybranch ofHARPS.get_wavecal_filename. The_polfiles held every order twice (90 and 52 orders against 45 and 26 real ones), interleaved, withcs_linesotherwise identical to the non-pol files andobase/bad_ordernot adjusted — an artifact of the era before fiber groups, when the two beams were extracted as 2N separate orders. Duplicated orders are incompatible with the 2D solution HARPS configures, which fits wavelength = P(x, order): refitting the shipped lines at degree [3, 6] gives a residual RMS of 0.0005 A from the non-pol lists against 8.4 A (blue) and 14.8 A (red) from the_polones, roughly half an order spacing, or ~600 km/s. Settinginstrument.polarimetry: trueselected them silently. HARPSPOL was already using the non-pol lists, which is correct: both beams see the same spectral orders, and its per-group trace count matches their order count
No code changes from 0.9b3; stable release of the 0.9 series.
- Two pure-Python extraction backends implementing the same algorithm as
clib/slitdec.c, for pipelines that cannot build a C extension:numba_slitdec(optionalnumbaextra, 1.5-1.8x the C) andnumpy_slitdec(numpy/scipy only, no extra to install, 1.8-2.1x). Both agree with the C to ~1e-14 relative with identical masks and iteration counts, checked bytest/test_{numba,numpy}_slitdec.py - CLI
--extractiononreduce runand the per-step commands, equivalent to settingPYREDUCE_EXTRACTION - CI job running the unit suite with the compiled extension removed and
PYREDUCE_EXTRACTION=numpy, so the pure-Python path stays working
- Breaking: extraction backend selection moved from
PYREDUCE_USE_CHARSLITtoPYREDUCE_EXTRACTION, one ofc(default),charslit,numba,numpy. The old flag is no longer read; an unknown name raises instead of falling back.PYREDUCE_USE_DELTASis unchanged - The compiled extension is imported only when the
cbackend is selected, soimport pyreduce.extractworks without it; the resolved backend is logged once at INFO - Extraction is 1.1-1.4x faster (most on tall slits): the sP band width is now measured from the zeta key ranges rather than sized from
delta_x, shrinkingbandsolfrom a 185-wide elimination to a 5-wide one on a 176-row swath, and the uncertainty pass reads the zeta tensor sequentially. Spectra, slit functions, models and masks are bit-identical; uncertainties move by <2e-15 relative from the changed summation order
- Parallel science extraction via new
n_jobsscience setting (joblib semantics, -1 = all cores) - CLI:
--skip-existingflag,list-channelscommand; bad channel/instrument names fail fast listing the valid options - Fiber selection in the Pipeline API:
use_fibers(),extract(use=),from_instrument(use=), mirroring CLI--use - Wavecal quality metrics: per-group log summary and
.wavecal_quality.jsonexport (RMS/median residuals in m/s, line counts, AIC) - Trace:
max_errorsetting rejects fused double-order clusters (port of IDL REDUCEMAX_ERR) - Provenance:
PR_version/PR_githashstamped into all FITS products;DATEande_contrecorded in final headers - Atlas: full KPNO ThAr line list; UNe NIR line list
- MOSAIC: per-bundle wavelength range guess for single-order multi-bundle wavecal initialization
- Example
examples/andes_yjh_fp.py: extract and measure FP lines in an IFU frame
reduce.pysplit intopyreduce/steps/package;reduce.pykeepsmain()and re-exports step classes.mark_ordersalias renamed todetect_traces; unusedFitsIOStepremoved- MOSAIC channels renamed to the ESO INS MODE scheme (NIR:
J_LR/H_LR/H_HR; VIS: arm+resolution x detector quadrant) - wavecal_init: FFT cross-correlation against the synthesized atlas for the global offset; new
wave_deltasearch-radius parameter - Config errors fail early:
id_*regex validation, per-channel list length checks, missing centers files raise instead of falling back silently
- Corrupted
thar.fitsatlas replaced with the clean KPNO spectrum; corruptune.fitsdropped - Shared bad pixel mask was attached by reference and mutated in-place by
load_fits/combine_frames group_fibers: per-order range clamp no longer truncates later orders- Traces file write errors in wavecal/freq_comb/curvature now propagate instead of being swallowed
- Wavelength solutions are written to the correct traces when wavecal results are group-keyed (MOSAIC)
- Legacy linelist
.npzfiles migrated to the current dtype on load
- HERMES instrument (Mercator telescope)
- MOSAIC LR-J / LR-H NIR channels and starsky example
- ANDES YJH sampling/resolution example (
examples/andes_yjh_sampl.py) - Explicit
fibers.numberingconfig field (bottom_up/top_down) for fiber numbering direction - Fallback to
flatfiles fortrace/curvature/scatterwhen files are set manually
- Extraction:
slitdec(copied from charslit) is now the sole CFFI backend, ~10x faster - Wavecal: disentangle bundle index from spectral order
m - ANDES YJH: env-var output suffix,
trace_range, and IFU science flow - Trace:
center_weightmerge for missing-fiber bundles
get_supported_channelsfor single-channel instruments- Bundle naming in
group_fiberswhen one bundle perm
- Curvature step: fall back to per-trace height when
extraction_heightisNone(matchesextract.pybehavior)
- Lower minimum Python version to 3.12
- CI test matrix now covers Python 3.12, 3.13, and 3.14
- CRIRES+ L-band example script
- Settings fallback chain for composite channel names
hdf2tracetool: write HDF wavelength polynomials into trace FITS
- Fix
create_custom_instrumentcrash (was returning None config) - Fix charslit backend selection to check env var at call time
- Bundle centers fallback, curvature and norm_flat fixes for MOSAIC VIS
- Rewrite custom instrument example as student-friendly walkthrough
create_custom_instrumentnow accepts**overridesfor inline property setting
No code changes from 0.8a5; docs update and stable release.
- ANDES_UBV instrument, IZ channel for ANDES_RIZ, per-channel settings files
- Preserve individual fiber traces alongside merged group traces
- Tests for
get_y_scalecovering standard, edge, and invariant cases
- Center extraction window on trace peak (+0.5/+1 shift for odd/even height)
- Fix
get_y_scalesilently mutating caller's ycen array - Fix slit function plot alignment in ProgressPlot
- Fix curvature save: match traces by (m, group) instead of index
- Recompute heights after merging even/odd fibers (use nearest neighbor)
- Fix test warnings: tempfile leak, divide-by-zero, misplaced instrument test
- Guard against empty order_centers YAML files
- HARPSPOL instrument with dual-beam (beam splitter) support
- Otsu+DP beam-pair detection for multi-fiber trace pairing
--useCLI flag to select fiber groups (e.g.,--use upper,lower)- Wavelength propagation from trace into extracted Spectrum
- Output files go into per-night subdirectory (
reduced/{night}/) - Group filenames use
{prefix}_{group}.{step}.{ext}convention - Traces without order_centers get sequential m values (was None)
- Fix
create_image_from_linesrow count (use ntrace, not order range) - Fix 2D wavecal polynomial eval: use trace index, not physical order m
- Fix multi-channel filename overwrite (include channel in science/rectify)
- Fix linelist order drift across re-runs (normalize to 0-based on save)
- Fix output_dir CLI default (None, so config's
reduced/{night}is used)
- Replace MCMC-based
wavecal_initwith iterative peak matching algorithm - Remove
emceeandcornerdependencies
- Fix continuum trace count mismatch when norm_flat drops edge traces
- Fix extraction_height precedence: settings over trace.height
- Fix LFC plot crash on negative orders and Finalize trace selection
- Fix 2D wavecal poly eval, continuum trace selection, and numeric guards
- Fix wavecal/freq_comb trace data flow and continuum trace mismatch
Tracedataclass (trace_model.py) for unified trace data modelSpectrum/Spectraclasses (spectra.py) replacing legacy Echelle formatTrace.wlen(x)method to evaluate wavelength polynomial- Per-group wavelength calibration support
- LFC wavecal support for ANDES_RIZ
- docs/output_formats.md documenting new file formats
- All pipeline steps use
list[Trace]interface instead of array-based extract()takeslist[Trace], returnslist[Spectrum]- Renamed
nord/iordtontrace/idxfor clarity - Renamed
Trace.fibertoTrace.group, addedfiber_idxfor per-fiber wavecal - File format: traces.npz -> traces.fits (unified FITS format)
- Wavelength calibration stored in traces.fits instead of separate wavecal.npz
- Spectra use NaN masking instead of COLUMNS+MASK redundancy
curvature_model.py(curvature now stored in Trace)echelle.pyfunctionality (deprecated, use spectra.py)- Dead code from util.py
- Charslit extraction backend with degree 1-5 curvature support
- ANDES_RIZ instrument configuration and settings
trace_byconfig for separate tracing of illumination groups- HDF-to-wavecal tool and reference files for ANDES_RIZ
- ANDES spectra plotting tool
- Curvature: use literal pixel extraction_height, not fractional
- Simplify extraction_height to single full-height value
- Move fiber selection default from hardcoded to config
- Update METIS_IFU for dynamic wavelength-based channels
- Use git URL for charslit optional dependency
- PEP 723 inline metadata from examples (caused confusion with
uv run)
- Fix curve_height to use new full-height format in plot_comparison
- Fix extraction_height to give exactly N rows
- Fix CRIRES_PLUS docstring (was incorrectly HARPS)
First stable release of the 0.7 series!
- Rename
OrderTracingclass toTrace, save file from.orders.npzto.traces.npz - Store per-trace extraction heights in traces.npz
- Compute extraction heights for fiber bundles automatically
- CLI: consistent
-t/--targetoption across all commands - Allow explicit paths in settings
__inherits__directive
- Limit trace overlay to column_range in science step plot
- Add WhatsNew.md summarizing 0.7 series improvements
- Update docs for traces.npz rename and mask convention
- Per-channel settings support via
settings_{channel}.jsonfiles - Fiber grouping configuration for multi-fiber instruments (bundle_centers, fiber_groups)
preset_slitfuncparameter for single-pass extraction using pre-computed slit functionnoise_relativeparameter for trace threshold scalingextraction_rejectthreshold parameter for outlier rejection- ANDES_YJH instrument with multi-channel support
- MOSAIC VIS quadrant support (VIS1-VIS4 channels) and NIR settings
- HARPN fiber B wavecal file
- Extraction residual panel in ProgressPlot
tools/plot_swath_debug.pyfor analyzing extraction debug data
- Mask convention: Switch to numpy convention (True/1=bad) on Python side; C code uses inverted (1=good)
- Extraction convergence now based on spectrum change with global RMS for rejection
- Improved outlier rejection using 6*sigma instead of MAD in 2D extraction
- Norm_flat step saves slit function with metadata for reuse
- Refactor Pipeline trace API: add
trace_raw()andorganize()methods - Remove
extraction_cutoffparameter (dead code)
smooth_spectrumnormalization (port from cr2res)- Masked array handling in swath debug plotting
- Minimum 4 iterations enforced in curved extraction
- MOSAIC instrument support with fiber group detection and curvature step
- Extraction animation controls: pause/step buttons and speed control (
PYREDUCE_PLOT_ANIMATION_SPEED) --plot-dirand--plot-showCLI options for flexible plot output
- Rename
orderstotracesin rectify and slit_curve modules for consistency - Downgrade extraction max-iterations message from ERROR to WARNING
- Only warn about missing files for steps that are actually requested
- Handle channel mismatch gracefully in CLI
- Curvature plotting index error when peaks need int casting
- Reorganize instrument files: All instrument-related files now in per-instrument directories
instruments/{name}.py→instruments/{NAME}/__init__.pyinstruments/{name}.yaml→instruments/{NAME}/config.yamlsettings/settings_{NAME}.json→instruments/{NAME}/settings.jsonwavecal/{name}_*.npz→instruments/{NAME}/wavecal_*.npzmasks/mask_{name}_*.fits.gz→instruments/{NAME}/mask_*.fits.gz
- Base settings and schema moved to
instruments/defaults/ - Wavelength atlas files moved to
instruments/defaults/atlas/
- Orphan mask files for undefined instruments (elodie, sarg, hds, etc.)
- Curvature algorithm rewrite: Replace 2D model fitting with row-tracking method for better robustness
- Rename
make_shear.pytoslit_curve.py - Rename curvature coefficients:
tilt/shear→p1/p2throughout codebase - Rename
arc_extractiontosimple_extraction - Split curvature
extraction_heightinto separateextraction_heightandcurve_heightparameters - Save file renamed from
.shear.npzto.curve.npz
discover_channels()for automatic channel detection from data files- CLI:
--targetis now optional; loops over all targets if not specified - CLI: Uses
$REDUCE_DATAfor base_dir, reads default input_dir from config - Comprehensive CLI test coverage
- Curvature step index error when traces are removed during processing
- Validate base_dir and input_dir exist with clear error messages
- File sorting to correctly loop over all nights when not specified
- CLI dynamic dependency loading for step commands
--settingsoption for CLI to override reduction parameters from JSON file
- Rename
extraction_widthtoextraction_heightin settings (clarifies coordinate system) - Rename
orderstotracesin extract.py internal API
--fileoption for CLI step commands to bypass file discovery- NEID instrument with multi-amplifier support
- Rename
ordersstep totracethroughout codebase (CLI, API, configs)
- test_normflat to use column_range returned by extract
- Rename
armtochannelthroughout codebase (API, CLI, configs) - Remove decker from CRIRES+ channel format (
J1228_det1instead ofJ1228_Open_det1) - Example scripts now use
$REDUCE_DATAenv var instead of hardcoded paths
reduce examples --runflag to download and execute examples directly- PEP 723 inline metadata in examples for
uv runcompatibility
reduce examplescommand to list/download examples from GitHub matching installed version
- CLI startup 12x faster via lazy imports (1.2s -> 0.1s)
- Manual API calls documentation (manual_calls.md)
- ReadTheDocs build failing due to missing myst_parser
- Trace module reference in documentation
- Docs Makefile now uses uv
- pyproject.toml cleanup
- New Pipeline API with
Pipeline.from_instrument()for simplified usage - Click-based CLI replacing argparse (
uv run reduce run UVES HD132205) - Pydantic models for instrument configuration validation
- YAML instrument configs replacing JSON
- IPython startup script for interactive development
plot_diroption to save plots as PNG files- Fiber bundle tracing support for multi-fiber instruments
filter_xandfilter_typeoptions for order tracing
- Rename
modetochannelterminology throughout - Output extension changed from
.echto.fits - Documentation converted from RST to Markdown
- Trace detection parameters renamed (
opower->degree,filter_size->filter_y)
- Plotting issues with non-finite values
- Use
interpolate_replace_nansfor masked pixels
- JSON schema validation test for instrument configurations
- GitHub workflow creates GitHub Release on tag push
- Fix test_wavecal to match WavelengthCalibration.execute() signature
- Fix spec fixture to match ScienceExtraction.run() signature
- Fix instrument schema: replace invalid 'value' keyword with standard JSON Schema 'type'
- Include *.pyd files for Windows builds
- Remove redundant build test from workflow
- workflow builds multi-arch wheels
- minor fixes to make build pass
- Test organization with pytest markers (unit, instrument, slow, downloads)
- ANDES instrument configuration and settings
- CI now runs fast tests (~7s) on every push to master
- Test suite optimized with slow marker for tests >3s
- ANDES instrument configuration errors (missing decker fields, regex pattern)
- NIRSPEC FITS header errors with illegal keywords containing dots
- Pre-commit hooks now documented and working correctly
- lazy imports
- cosmetic fixes like version string
- Modern build system using uv package manager
- Minimum Python version now 3.11, default 3.13
- Migrated build system from setuptools to Hatchling (PEP 517)
- Replaced black, isort, flake8, and pyupgrade with Ruff
- Improved code formatting across entire codebase using Ruff
- Modernized GitHub Actions workflow (uv-based, Python 3.11-3.13 matrix)
- Updated documentation to reflect modern installation with uv
- Consolidated legacy build files (setup.py, requirements.txt) into pyproject.toml
- Removed automatic PyPI publishing from GitHub Actions (now manual)
- GitHub Actions tests now manual-only on master branch
- Modernized ReadTheDocs build configuration
- Close FITS file handles after reading to prevent resource leaks (#28)
- Store wavelength calibration in double precision instead of single (#30)
- Add fallback value for E_ORIENT header keyword (#31)
- Return final wavelength linelist with flags from calibration (#33)
- Fix MaskedArray filling in normflat routine
- Fix undefined TypeFilter import in NEID instrument
- Fix Sphinx documentation build warnings
- Fix pre-commit hook configuration issues
- Updated README badges for GitHub Actions and Python versions
- Updated installation instructions to use uv instead of pip
- Improved documentation build process
- CLAUDE.md / AGENTS.md for AI-assisted development guidance