Skip to content

Data types

spectro-kernel defines one canonical representation for each kind of object. Every algorithm consumes and produces these types - never ad-hoc tuples or per-project variants. That single agreement is what makes results comparable across applications.

Spectrum1D - the spectrum

The heart of the library: a 1D spectrum is flux sampled over a wavelength axis.

from spectro_kernel import Spectrum1D

spec = Spectrum1D(
    wavelength=wave,        # 1D array, conventionally Ångström
    flux=flux,              # 1D array, same length
    flux_unit="ADU",
    uncertainty=None,       # optional 1-sigma error array
    mask=None,              # optional boolean array (True = ignore)
    meta={"object": "Vega"},  # free-form metadata
    header=None,            # raw FITS header, if loaded from a file
)
Field Meaning
wavelength The wavelength axis (Å by convention).
flux Flux values, one per wavelength sample.
flux_unit Label for the flux unit.
uncertainty Optional per-sample 1-σ uncertainty.
mask Optional boolean array; True marks a sample to ignore.
meta Free-form metadata (object name, instrument, dates…).
header The original FITS header, when read from a file.

It is a small, well-behaved object:

len(spec)                       # number of samples
spec.npix                       # same thing, explicit
spec.wavelength_min, spec.wavelength_max
spec.copy()                     # a deep, independent copy
spec.select_range(6500, 6620)   # a cropped sub-spectrum
spec.content_hash()             # stable SHA-256 of the numeric content
spec.preview()                  # small JSON-safe summary (used by MCP)

Why one type matters

When read_fits, normalize_polynomial and fit_gaussian_line all speak Spectrum1D, they compose without glue code - and a spectrum measured in one application means exactly the same thing in another.

Related types live alongside it: ImageFrame (a 2D detector frame, the entry point for CCD reduction), LineFitResult, Periodogram, LightCurve, CatalogResult, SpectralLine.

WorkContext - the analysis state

A WorkContext is the mutable bag of results carried through an analysis. It generalises the idea of "the data I am working on": it has typed slots for the common objects plus an open extras dict.

flowchart TD
    subgraph WC["WorkContext"]
        S["spectrum : Spectrum1D"]
        SS["spectra : list<Spectrum1D>"]
        IMG["image / images"]
        LF["line_fits : dict"]
        PG["periodograms : dict"]
        LC["light_curves : dict"]
        CL["catalog_lookups : dict"]
        FIG["figures : dict"]
        EXP["exports : dict"]
        MET["metrics : dict<str,float>"]
        EXT["extras : dict (anything)"]
        HIS["history : list<ProcessingStep>"]
    end

An algorithm reads its inputs from the context and writes its outputs back:

from spectro_kernel import WorkContext, run_algorithm

ctx = WorkContext(spectrum=spec)
run_algorithm("snr_der", ctx)          # reads ctx.spectrum, writes ctx.metrics
run_algorithm("fit_gaussian_line", ctx, {"line_center_angstrom": 6562.8})

ctx.metrics            # {"snr_der": 142.3, ...}
ctx.line_fits          # {"6562.8": LineFitResult(...)}
ctx.history            # the full audit trail
ctx.summary()          # a compact, JSON-safe description of everything held

Because one context carries everything, an algorithm can use the output of an earlier one with no plumbing - that is exactly how pipelines work.

ProcessingStep - the audit trail

Every algorithm execution appends a ProcessingStep to ctx.history:

ProcessingStep(
    algorithm="snr_der", version="1.0.0", params={...},
    timestamp="2026-05-22T14:23:15+00:00",
    input_hash="sha256:…", output_hash="sha256:…",
    duration_ms=1.8, success=True, message="DER_SNR = 142.3",
)

Together with per-algorithm versions, this is what makes a spectro-kernel result traceable and replayable - see Pipelines → Reproducibility.

Full field-by-field API

See the API reference for the complete, generated documentation of every type.