Instrument¶
Base Classes and Calibration Tokens¶
Abstract base class for dispersers and calibration parameter tokens.
- class unite.instrument.base.RScale(name=None, *, prior=None)[source]¶
Bases:
ParameterMultiplicative scale on the disperser resolving power R.
Nominal value is 1 (no correction). The model applies
R_eff(λ) = R_nominal(λ) * r_scale.- Parameters:
- namestr, optional
Human-readable label. When attached to a
Disperser, the site name is auto-derived as'r_scale_{disperser_name}'if not provided.- priorPrior, optional
Prior on the R scale factor. Defaults to
Fixed(1.0)(fixed at nominal).
- Parameters:
- class unite.instrument.base.FluxScale(name=None, *, prior=None)[source]¶
Bases:
ParameterMultiplicative flux normalisation between dispersers.
Nominal value is 1. The model divides observed flux by
flux_scale, allowing relative flux calibration across multiple gratings.- Parameters:
- namestr, optional
Human-readable label. When attached to a
Disperser, the site name is auto-derived as'flux_scale_{disperser_name}'if not provided.- priorPrior, optional
Prior on the flux scale factor. Defaults to
Fixed(1.0)(fixed at nominal).
- Parameters:
- class unite.instrument.base.PixOffset(name=None, *, prior=None)[source]¶
Bases:
ParameterPixel displacement of the spectrum on the detector relative to the wavelength calibration.
Nominal value is 0 (no displacement). A positive value indicates the spectrum is displaced toward longer wavelengths on the detector: the model subtracts
pix_offset * dlam_dpixfrom the calibrated pixel-edge wavelengths, shifting them blueward to compensate.For example, if a disperser consistently returns redshifts that are too high compared to a reference, the spectrum is displaced redward by some number of pixels — set
pix_offsetto that (positive) number to correct the wavelength solution.- Parameters:
- namestr, optional
Human-readable label. When attached to a
Disperser, the site name is auto-derived as'pix_offset_{disperser_name}'if not provided.- priorPrior, optional
Prior on the pixel offset. Defaults to
Fixed(0.0)(no displacement).
- Parameters:
- class unite.instrument.base.Disperser(unit, *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
ABCAbstract base class for dispersive optical elements.
A disperser maps wavelength coordinates to instrumental quantities such as resolving power and plate scale. Every concrete subclass must implement
R()anddlam_dpix(), and must carry aunitattribute that records the wavelength unit the disperser expects.Calibration is encoded by optional tokens (
RScale,FluxScale,PixOffset):r_scale— multiplicative scale on resolving power (R).flux_scale— multiplicative flux normalisation.pix_offset— pixel displacement of the spectrum on the detector (positive = redward; wavelength grid is corrected blueward).
Noneon any slot means that parameter is absent from the model entirely (equivalent to a fixed nominal value but without a token). To create a token that is fixed at its nominal value, pass e.g.r_scale=RScale()— the default prior isFixed(1.0).Sharing: two dispersers that reference the same token instance share a single parameter in the model (identity-based, like
FWHM/Redshifton lines).- Parameters:
- unitastropy.units.UnitBase
The wavelength unit that this disperser operates in (e.g.
u.Angstrom,u.nm,u.um). All wavelength values passed toR()anddlam_dpix()are assumed to be in this unit.- namestr, optional
Human-readable label (e.g.
'G235H'). Used in repr and as the defaultSpectrum.namewhen this disperser is attached.- r_scaleRScale, optional
Token for the resolving-power scale.
None→ not in model.- flux_scaleFluxScale, optional
Token for the flux normalisation.
None→ not in model.- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
None→ not in model.
- Parameters:
- abstractmethod R(wavelength)[source]¶
Return the resolving power at the given wavelengths.
- Return type:
Array|ndarray|bool|number|bool|int|float|complex- Parameters:
- wavelengthArrayLike
Wavelength values in the unit specified by
self.unit.
- Returns:
- ArrayLike
Resolving power R = λ / Δλ evaluated at each wavelength.
- Parameters:
wavelength (Array | ndarray | bool | number | bool | int | float | complex)
- abstractmethod dlam_dpix(wavelength)[source]¶
Return the linear dispersion (wavelength per pixel).
- Return type:
Array|ndarray|bool|number|bool|int|float|complex- Parameters:
- wavelengthArrayLike
Wavelength values in the unit specified by
self.unit.
- Returns:
- ArrayLike
Dispersion dλ/dpix evaluated at each wavelength.
- Parameters:
wavelength (Array | ndarray | bool | number | bool | int | float | complex)
Instrument Configuration¶
Instrument configuration: disperser models with calibration tokens.
InstrumentConfig collects one Disperser
per observing configuration. Each disperser carries optional
RScale / FluxScale /
PixOffset tokens (r_scale, flux_scale,
pix_offset) for shared calibration parameters.
Degeneracy warning¶
A multi-disperser fit is only identified if at least one disperser has
flux_scale=None (flux anchor) and at least one has pix_offset=None
(pixel-offset anchor). validate() issues UserWarning when
these conditions are not met. Validation is called automatically when this
object is passed to Configuration.
Serialization¶
to_dict() hoists all unique calibration tokens to a top-level
calib_params section keyed by token name. Disperser entries reference
tokens by name. Shared tokens round-trip correctly — the same object is
reconstructed for both entries.
Examples¶
>>> from unite.instrument import InstrumentConfig, RScale, FluxScale
>>> from unite.instrument.nirspec import G235H, G395H
>>> from unite.prior import TruncatedNormal
>>> r = RScale(prior=TruncatedNormal(1.0, 0.05, 0.8, 1.2))
>>> flux_0 = FluxScale(prior=TruncatedNormal(1.0, 0.1, 0.5, 2.0))
>>> cfg = InstrumentConfig([
... G235H(r_scale=r),
... G395H(r_scale=r, flux_scale=flux_0),
... ])
>>> cfg.names
['G235H', 'G395H']
- class unite.instrument.config.InstrumentConfig(dispersers)[source]¶
Bases:
objectConfiguration for a multi-disperser spectral dataset.
An ordered collection of
Disperserobjects, one per observing disperser. Each disperser carries optionalCalibParamtokens for calibration parameters.The configuration is data-free: it describes which dispersers are used and how they are calibrated. Actual spectral data arrays are attached later via
make_spectrum().- Parameters:
- disperserssequence of Disperser
One entry per disperser. Names (
disperser.name) must be unique.
- Raises:
- ValueError
If any disperser names are duplicated or empty.
- Parameters:
dispersers (Sequence[Disperser])
- validate()[source]¶
Check for flux and pixel-offset degeneracies.
Issues a
UserWarningfor each calibration axis (flux, dispersion) where no disperser is anchored (tokenNone).- Return type:
- to_dict()[source]¶
Serialize to a YAML-safe dictionary.
Shared
CalibParamtokens are hoisted to a top-levelcalib_paramssection. Disperser entries reference tokens by name, so sharing is preserved on round-trip.- Return type:
- Returns:
- dict
Contains
'calib_params'and'entries'keys.
- classmethod from_dict(d)[source]¶
Deserialize from a dictionary.
- classmethod from_yaml(text)[source]¶
Deserialize from a YAML string.
Generic Implementations¶
Generic disperser implementations.
These are the building blocks for custom instruments.
Import from this module directly:
from unite.instrument.generic import GenericDisperser, SimpleDisperser
The Spectrum class lives in unite.spectrum.
- class unite.instrument.generic.GenericDisperser(R_func, dlam_dpix_func, unit, *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
DisperserA disperser defined by user-supplied, JAX-jittable callables.
This is the most flexible concrete disperser: you provide arbitrary functions for R(λ) and dλ/dpix(λ) and they are forwarded directly.
- Parameters:
- R_funcCallable[[ArrayLike], ArrayLike]
A JAX-jittable function that returns the resolving power for a given array of wavelengths.
- dlam_dpix_funcCallable[[ArrayLike], ArrayLike]
A JAX-jittable function that returns the linear dispersion for a given array of wavelengths.
- unitastropy.units.UnitBase
The wavelength unit the functions expect.
- namestr, optional
Human-readable label.
- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the pixel shift.
- Parameters:
Examples
>>> import jax.numpy as jnp >>> from astropy import units as u >>> d = GenericDisperser( ... R_func=lambda w: jnp.full_like(w, 2700.0), ... dlam_dpix_func=lambda w: w / 2700.0, ... unit=u.Angstrom, ... ) >>> d.R(jnp.array([5000.0])) Array([2700.], dtype=float32)
- class unite.instrument.generic.SimpleDisperser(wavelength, *, R=None, dlam=None, dvel=None, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
DisperserA disperser defined on a pixel-sampled wavelength grid.
The wavelength array is interpreted as a sequence of pixel centers so that dλ/dpix is computed directly from the spacing of the array (via
jnp.gradient).The resolving power is derived from exactly one of three keyword arguments:
R— resolving power, scalar or array matching wavelength.dlam— spectral resolution element Δλ (same unit as wavelength).dvel— velocity fwhm in km/s. Converted via R = c / dvel.
A scalar value produces a constant R, Δλ, or Δv across the grid (and the corresponding resolving-power array is derived accordingly). An array value must have the same length as wavelength.
- Parameters:
- wavelengthu.Quantity
Pixel-center wavelengths. Must be 1-D.
- RArrayLike, optional
Resolving power (scalar or per-pixel array).
- dlamu.Quantity, optional
Spectral resolution Δλ, must be in wavelength units (scalar or per-pixel array)
- dvelu.Quantity, optional
Velocity resolution in velocity units (scalar or per-pixel array).
- namestr, optional
Human-readable label.
- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the pixel shift.
- Raises:
- ValueError
If zero or more than one of
R,dlam,dvelis provided, or if an array argument has the wrong length.
- Parameters:
Notes
When
R()ordlam_dpix()is called at wavelengths that differ from the stored grid, the values are linearly interpolated withjnp.interp.
NIRSpec Dispersers¶
JWST NIRSpec disperser implementations.
This module provides NIRSpec, a generic
Disperser for any NIRSpec grating/prism
configuration. Two resolving-power calibrations are available:
"uniform"or"uniformly-illuminated"— the official JDOX tabulated R(λ) curves shipped with the package as FITS files. This calibration is for uniformly illuminated sources."point"or"point-source"— polynomial R(λ) fits from de Graaff et al. (2025). This calibration is tailored for point-source observations.
Both calibrations share the same JDOX linear-dispersion (dλ/dpix) tables.
In addition to the generic NIRSpec class, named subclasses are
provided for each grating:
These fix the grating name while still accepting r_source and calibration
token kwargs.
- class unite.instrument.nirspec.disperser.G140H(r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
NIRSpecNIRSpec G140H disperser.
Convenience subclass of
NIRSpecwith the grating fixed to"g140h". Onlyr_sourceand calibration tokens need to be specified.- Parameters:
- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use (default
"point").- namestr, optional
Human-readable label. Defaults to
"G140H".- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- r_source
- Parameters:
Examples
>>> d = G140H() >>> d.grating 'g140h'
- class unite.instrument.nirspec.disperser.G140M(r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
NIRSpecNIRSpec G140M disperser.
Convenience subclass of
NIRSpecwith the grating fixed to"g140m". Onlyr_sourceand calibration tokens need to be specified.- Parameters:
- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use (default
"point").- namestr, optional
Human-readable label. Defaults to
"G140M".- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- r_source
- Parameters:
Examples
>>> d = G140M() >>> d.grating 'g140m'
- class unite.instrument.nirspec.disperser.G235H(r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
NIRSpecNIRSpec G235H disperser.
Convenience subclass of
NIRSpecwith the grating fixed to"g235h". Onlyr_sourceand calibration tokens need to be specified.- Parameters:
- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use (default
"point").- namestr, optional
Human-readable label. Defaults to
"G235H".- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- r_source
- Parameters:
Examples
>>> d = G235H() >>> d.grating 'g235h'
- class unite.instrument.nirspec.disperser.G235M(r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
NIRSpecNIRSpec G235M disperser.
Convenience subclass of
NIRSpecwith the grating fixed to"g235m". Onlyr_sourceand calibration tokens need to be specified.- Parameters:
- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use (default
"point").- namestr, optional
Human-readable label. Defaults to
"G235M".- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- r_source
- Parameters:
Examples
>>> d = G235M() >>> d.grating 'g235m'
- class unite.instrument.nirspec.disperser.G395H(r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
NIRSpecNIRSpec G395H disperser.
Convenience subclass of
NIRSpecwith the grating fixed to"g395h". Onlyr_sourceand calibration tokens need to be specified.- Parameters:
- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use (default
"point").- namestr, optional
Human-readable label. Defaults to
"G395H".- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- r_source
- Parameters:
Examples
>>> d = G395H() >>> d.grating 'g395h'
- class unite.instrument.nirspec.disperser.G395M(r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
NIRSpecNIRSpec G395M disperser.
Convenience subclass of
NIRSpecwith the grating fixed to"g395m". Onlyr_sourceand calibration tokens need to be specified.- Parameters:
- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use (default
"point").- namestr, optional
Human-readable label. Defaults to
"G395M".- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- r_source
- Parameters:
Examples
>>> d = G395M() >>> d.grating 'g395m'
- class unite.instrument.nirspec.disperser.PRISM(r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
NIRSpecNIRSpec PRISM disperser.
Convenience subclass of
NIRSpecwith the grating fixed to"prism". Onlyr_sourceand calibration tokens need to be specified.- Parameters:
- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use (default
"point").- namestr, optional
Human-readable label. Defaults to
"PRISM".- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- r_source
- Parameters:
Examples
>>> d = PRISM() >>> d.grating 'prism'
- class unite.instrument.nirspec.disperser.NIRSpec(grating, r_source='point', *, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
DisperserDisperser for a JWST NIRSpec grating or prism configuration.
- Parameters:
- gratingstr
NIRSpec grating name. One of
"prism","g140m","g140h","g235m","g235h","g395m","g395h"(case-insensitive).- r_source
"uniform","uniformly-illuminated","point", or"point-source", optional Which resolving-power calibration to use.
"uniform"or"uniformly-illuminated"interpolates the tabulated JDOX R(λ) curve for uniformly illuminated sources;"point"or"point-source"(default) evaluates the polynomial fit from de Graaff et al. (2025), which is tailored for point-source observations.- namestr, optional
Human-readable label. Defaults to the grating name (upper-case).
- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the detector pixel displacement.
- Parameters:
Notes
The linear dispersion dλ/dpix is always taken from the JDOX dispersion tables regardless of the
r_sourcechoice.All wavelengths are in microns (
astropy.units.um).Examples
>>> d = NIRSpec("g235h") >>> d.grating 'g235h' >>> d.r_source 'point'
>>> d_uniform = NIRSpec("prism", r_source="uniform") >>> d_uniform.r_source 'uniform'
SDSS Disperser¶
SDSS spectrograph disperser implementation.
Provides SDSSDisperser, a concrete
Disperser for SDSS optical spectra.
The resolving power R(λ) is derived from the wdisp column
(wavelength dispersion per pixel, in Angstroms) of standard SDSS
spec-*.fits files at load time via
from_sdss_fits().
Before data is loaded, the disperser can be constructed with default or placeholder R values, allowing calibration tokens to be configured and the disperser configuration to be serialized.
Examples¶
>>> from unite.instrument.sdss import SDSSDisperser
>>> d = SDSSDisperser()
>>> d.unit
Unit("Angstrom")
- class unite.instrument.sdss.disperser.SDSSDisperser(name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
DisperserDisperser for SDSS optical spectra.
The SDSS spectrographs use a log-linear wavelength grid. The resolving power and linear dispersion are set from data at spectrum load time (
from_sdss_fits()), but the disperser can also be pre-constructed with calibration tokens for serialization.- Parameters:
- wavelengthArrayLike, optional
Pixel-center wavelengths in Angstroms. When not provided,
R()anddlam_dpix()return placeholder values (constant R=2000, dlam_dpix=1.0).- wdispArrayLike, optional
Wavelength dispersion per pixel (Angstroms/pixel) from the SDSS
wdispcolumn. Same length as wavelength. The resolving power is computed asR(λ) = λ / (2.355 * wdisp)(sincewdispis sigma in wavelength units per pixel, and FWHM = 2.355 * sigma).- namestr, optional
Human-readable label. Defaults to
'SDSS'.- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the pixel shift.
- Parameters:
DESI Disperser¶
DESI spectrograph disperser implementation.
Provides DESIDisperser, a concrete
Disperser for DESI optical spectra.
The resolving power R(λ) is derived from the banded LSF resolution matrix
stored in native DESI spectra-*.fits files at load time via
from_desi_fits().
Before data is loaded, the disperser can be constructed with placeholder values, allowing calibration tokens to be configured.
Examples¶
>>> from unite.instrument.desi import DESIDisperser
>>> d = DESIDisperser('B')
>>> d.unit
Unit("Angstrom")
>>> d.arm
'B'
- class unite.instrument.desi.disperser.DESIDisperser(arm, name='', r_scale=None, flux_scale=None, pix_offset=None)[source]¶
Bases:
DisperserDisperser for a single DESI spectrograph arm.
The DESI focal-plane spectrograph splits each spectrum into three arms: B (blue, 3600-5800 Angstrom), R (red, 5760-7620 Angstrom), and Z (near-IR, 7520-9824 Angstrom). The resolving power and linear dispersion are set from data at spectrum load time (
from_desi_fits()), but the disperser can also be pre-constructed with calibration tokens for serialization.- Parameters:
- arm{‘B’, ‘R’, ‘Z’}
Spectrograph arm to model.
- namestr, optional
Human-readable label. Defaults to the lower-case arm letter.
- r_scaleRScale, optional
Token for the resolving-power scale.
- flux_scaleFluxScale, optional
Token for the flux normalisation.
- pix_offsetPixOffset, optional
Token for the pixel shift.
- Parameters: