Configuration¶
PyReduce uses two types of configuration files:
Instrument configs (YAML) - Define the instrument hardware and header mappings
Reduction settings (JSON) - Define algorithm parameters for each step
Reduction Settings¶
Location: pyreduce/instruments/{INSTRUMENT}/settings.json
These control HOW the reduction is performed - polynomial degrees, thresholds, extraction parameters, etc.
{
"bias": {
"degree": 0
},
"trace": {
"degree": 4,
"noise": 100,
"min_cluster": 500,
"filter_y": 120
},
"science": {
"extraction_method": "optimal",
"extraction_height": null,
"oversampling": 10
}
}
Settings Cascade¶
Settings are resolved by merging files in order, with later levels overriding earlier ones:
instruments/defaults/settings.json— base defaultsinstruments/{INSTRUMENT}/settings.json— instrument-specific overridesinstruments/{INSTRUMENT}/settings_{channel}.json— per-channel overrides (if channel is specified and the file exists)Runtime overrides via
configurationparameter or keyword arguments
Each file only needs to specify the values it wants to change. Missing keys are inherited from the parent file.
Inheritance (__inherits__)¶
Every settings file (except defaults/settings.json) declares its parent via __inherits__:
{
"__inherits__": "ANDES_RIZ/settings.json",
"curvature": {
"curve_height": 171,
"extraction_height": 20
}
}
The path is relative to pyreduce/instruments/. Inheritance is resolved recursively — a per-channel file inherits from the instrument file, which inherits from defaults.
You can also inherit from another channel’s settings to avoid duplication. For example, MOSAIC VIS2-VIS4 inherit from VIS1:
{
"__inherits__": "MOSAIC/settings_VIS1.json"
}
Per-Channel Settings¶
Instruments with multiple channels often need different parameters (extraction heights, curvature settings, etc.). Create settings_{channel}.json files alongside settings.json:
pyreduce/instruments/ANDES_RIZ/
settings.json # Shared settings for all ANDES_RIZ channels
settings_r.json # Overrides for R channel
settings_iz.json # Overrides for IZ channel
When you pass channel= to the pipeline, the per-channel file is loaded automatically:
Pipeline.from_instrument("ANDES_RIZ", channel="r", ...) # loads settings_r.json
Pipeline.from_instrument("ANDES_RIZ", ...) # loads settings.json
If settings_{channel}.json doesn’t exist, it falls back to settings.json.
To override settings at runtime:
from pyreduce.configuration import get_configuration_for_instrument
config = get_configuration_for_instrument("UVES")
config["trace"]["degree"] = 5
config["science"]["oversampling"] = 8
Pipeline.from_instrument(
instrument="UVES",
...,
configuration=config,
).run()
Instrument Configs¶
Location: pyreduce/instruments/{INSTRUMENT}/config.yaml
These define WHAT the instrument is - detector properties, header keyword mappings, file classification patterns.
# Identity
instrument: HARPS
telescope: ESO-3.6m
channels: [red, blue]
# Detector
naxis: [4096, 4096]
orientation: 4
extension: 0
gain: ESO DET OUT1 CONAD
readnoise: ESO DET OUT1 RON
# Header mappings
date: DATE-OBS
target: ESO OBS TARG NAME
exposure_time: EXPTIME
# File classification
kw_bias: ESO DPR TYPE
id_bias: BIAS
kw_flat: ESO DPR TYPE
id_flat: FLAT.*
Instrument configs are validated by Pydantic models at load time.
See pyreduce/instruments/models.py for the full schema.
Common Settings¶
Trace (Order Tracing)¶
Parameter |
Description |
Default |
|---|---|---|
|
Polynomial degree for trace fitting |
4 |
|
Absolute noise threshold for detection |
0 |
|
Relative noise threshold (fraction of image max) |
0 |
|
Minimum pixels for valid order |
500 |
|
Median filter size in y |
null |
|
Median filter size in x |
0 |
|
Filter type (“boxcar” or “median”) |
“boxcar” |
|
Pixels to ignore at edges. Int or |
null |
Use either noise (absolute threshold) or noise_relative (e.g., 0.01 for 1% of image maximum) for trace detection.
Science (Extraction)¶
Parameter |
Description |
Default |
|---|---|---|
|
“optimal” or “simple” |
“optimal” |
|
Extraction aperture (see below) |
null |
|
Slit function oversampling |
10 |
|
Smoothing factor |
0.1 |
|
Spectrum smoothing factor |
1e-7 |
|
Width of extraction swaths |
300 |
|
Sigma threshold for outlier rejection |
6 |
|
Maximum extraction iterations |
30 |
extraction_height¶
The extraction aperture can be specified as:
null(default) - Use per-trace heights computed during tracing, stored intraces.fits. This provides optimal apertures based on actual trace spacing.Pixels (≥2) - Explicit pixel height, e.g.,
20for 20 pixels total (10 above, 10 below trace)Fraction (<2) - Fraction of order separation, e.g.,
0.5for half the distance to neighbors
The automatic heights (null) are recommended for most cases. They adapt to varying trace spacing across the detector and between orders.
Using a Pre-computed Slit Function¶
For faster extraction, the slit function computed during norm_flat can be reused in subsequent steps. The normalized flat step saves the slit function to .flat_norm.npz with metadata (extraction_height, osample). To use it:
import numpy as np
from pyreduce.extract import extract
# Load slit function from norm_flat output
norm_data = np.load("output/uves.flat_norm.npz", allow_pickle=True)
slitfunc_list = list(norm_data["slitfunc"])
slitfunc_meta = norm_data["slitfunc_meta"].item()
# Extract with preset slit function
spectra = extract(
image,
traces,
extraction_height=slitfunc_meta["extraction_height"],
osample=slitfunc_meta["osample"],
preset_slitfunc=slitfunc_list,
)
This performs single-pass extraction without iterating to find the slit function shape, which is useful for instruments with stable slit profiles.
Wavelength Calibration¶
Parameter |
Description |
Default |
|---|---|---|
|
Polynomial degree [x, order] |
[6, 6] |
|
Line detection threshold |
100 |
|
Refinement iterations |
3 |
|
Refractive medium (“air” or “vacuum”) |
“air” |
|
“1D” (per-trace) or “2D” (shared polynomial) |
“2D” |
|
Line atlas name (“thar”, “une”, “lfc”, etc.) |
“thar” |
Per-Group Wavelength Calibration¶
For multi-fiber instruments, wavelength calibration can process fiber groups
separately. This is configured via fibers.use.wavecal in config.yaml:
fibers:
groups:
A: {range: [1, 36], merge: average}
cal: {range: [37, 40], merge: average}
B: {range: [40, 76], merge: average}
use:
wavecal: [A, B] # Separate calibration for each group
# OR
wavecal: [cal] # Use only calibration fiber
# OR
wavecal: per_fiber # Separate calibration per fiber_idx
# OR
wavecal: all # All traces together (default)
[A, B]- Calibrate each named group separately. Useful when groups have different optical paths (e.g., science fibers A and B).per_fiber- Calibrate each fiber index separately. Each uniquefiber_idxvalue gets its own wavelength polynomial. Use this when individual fibers within a group have slightly different wavelength solutions and you want maximum precision.all- Combine all traces into a single calibration (default for single-fiber instruments).
The wavelength polynomial is stored in each Trace.wave attribute and saved to
traces.fits. Subsequent steps (science extraction, continuum normalization)
read wavelengths directly from the traces.
See Fiber Bundle Configuration for full details on multi-fiber setup.
Continuum Normalization¶
Parameter |
Description |
Default |
|---|---|---|
|
Polynomial degree for fit |
5 |
|
Sigma clipping threshold |
3 |
|
Fit iterations |
5 |