Recipes¶
A recipe is a measurement written down once, reviewed, referenced and versioned, so that two observers with two different instruments produce numbers that can be compared. spectro-kernel calls them presets in the code; this section uses both words for the same thing.
The problem recipes solve¶
Marc observes with an Alpy 600 in Toulouse, Sophie with a LHIRES III in Lyon. Both follow the Be star γ Cas and want to submit an Hα equivalent width to BeSS. Each writes their own pipeline file: Marc normalises on 6500–6520 Å, Sophie on 6480–6500 Å because she read something else; Marc integrates over 40 Å, Sophie over 30. Two numbers labelled "EW(Hα)" land in the database, measured differently, and a researcher cannot combine them. The bricks were the same; the convention was not shared, and it was tangled with each observer's instrument settings.
Three words¶
| Word | What it is | Who writes it | Where it lives |
|---|---|---|---|
| Brick | one published algorithm (equivalent_width, normalize_to_region, ...) |
kernel contributors, with a reference | the kernel, algorithm catalogue |
| Recipe | which bricks, in which order, with which conventions; declares what depends on the instrument as variables | the community, reviewed | the kernel (a few generic ones) or a collection such as spectro-kernel-recipes |
| Profile | the values for one instrument and one observer (slit tilt, extraction width, site) | each observer or application | your disk, your application |
The variables are the contract between a recipe and a profile. A recipe never contains an instrument value; a profile never contains a scientific convention.
The same story, with recipes¶
The recipe fixes the science and asks only for what it cannot know, here the observer's site, needed to bring the wavelengths to the barycentric frame before measuring (BeSS stores spectra that way):
# be_halpha_ew.yaml (from the collection, not from Marc's disk)
name: be_halpha_ew
version: 1.0.0
requires: ">=0.7"
description: H-alpha equivalent width and V/R of a Be star, measured the same way by every observer.
metadata:
kind: campaign
status: draft
references: ["Vollmann & Eversberg 2006, AN 327, 862", "Neiner et al. 2011, AJ 142, 149"]
conventions: "continuum band 6500-6520 Å, 60 Å window around H-alpha, V/R within 15 Å"
variables:
latitude_deg: {type: float, required: true, description: "observatory latitude (deg)"}
longitude_deg: {type: float, required: true, description: "observatory longitude (deg, east)"}
steps:
- algorithm: barycentric_correction
params: {latitude_deg: "${latitude_deg}", longitude_deg: "${longitude_deg}"}
- algorithm: normalize_to_region
params: {wave_lo: 6500.0, wave_hi: 6520.0} # fixed by the recipe
- algorithm: equivalent_width
params: {line_center_angstrom: 6562.79, window_angstrom: 60.0}
- algorithm: vr_ratio
params: {line_center_aa: 6562.79, window_half_width_aa: 15.0}
Marc's profile is two lines, with nothing scientific in it:
And the run binds the two:
Sophie runs exactly the same command with lyon.yaml. Both results
carry the same band, the same window, the same error formula, and each
spectrum's history ends with a record naming the recipe, its version, the
bound variables and the kernel version:
[ok] pipeline:be_halpha_ew v1.0.0 (412.3ms)
variables={'latitude_deg': 43.6, 'longitude_deg': 1.44, 'elevation_m': 0.0} kernel_version=0.7.0
That record is what lets a researcher say "these two measurements are
comparable" and what lets you write "measured with recipe be_halpha_ew
v1.0.0" in a report. The instrument geometry of a raw-frame reduction
(slit tilt, extraction width) is handled the same way by the reduction
recipes, solar_long_slit and stellar_long_slit.
In one minute¶
spectro presets # every recipe this install can see
spectro preset show snr_check # its variables and steps
spectro pipeline snr_check --input obs.fits # run one (no variables here)
spectro preset validate my_recipe.yaml # check a recipe before sharing it
From Python:
from spectro_kernel import WorkContext
from spectro_kernel.pipeline import PipelineBuilder
pipeline = PipelineBuilder().from_preset("be_halpha_ew", {"latitude_deg": 43.6, "longitude_deg": 1.44}).build()
result = pipeline.execute(WorkContext(spectrum=spec))
print(result.history[-1]) # pipeline:be_halpha_ew v1.0.0 ...
From an MCP client, the run_preset tool takes the same variables.
Where to go next¶
- Recipe format: the file, the variable types, the substitution
rules, the
requiresrange, the metadata vocabulary. - Catalogue: every recipe this documentation build can see, with conventions, references and variables.
- Share a recipe: the three tiers, the
spectro-kernel-recipescollection, how a draft becomes a reference.