Differentiable SED forward model with modular physics and clean API.
The forward model maps physical parameters (stellar mass, SFH, metallicity,
dust, AGN, etc.) to observables: photometry, spectrum, and derived SED
quantities. Internally, it decomposes the SED pipeline into independent
physics modules (stellar populations, star formation history, dust,
nebular, AGN, IGM) that are composed into prediction kernels at
initialization time, enabling fast inference and flexibility in model
configuration.
The SFH is computed via a registry-driven composed function that handles
additive smooth models, burst mixture, and correlated-field (GP) modulation
in a single call. Three prediction modes (compositional, hybrid, exact) trade
accuracy for speed, with automatic fallback.
Parameters:
spec (Parameters) – Parameter specification from tengri.Parameters. Defines
free/fixed parameters and their priors.
ssp_data (SSPData) – Pre-loaded SSP templates (from load_ssp_data()). Contains
absolute SSP grid in log10(Z) absolute, age array, and
optional mass-remaining tables for stellar mass surviving
constraints.
precompute (bool, optional) – Whether to precompute SSP photometry and spectroscopy grids at
initialization. Default True activates the Zacharegkas+2025
fast-photometry path and enabling caching of spectroscopy grids.
Set False to defer computation (useful for batch operations).
Dtype for forward model computation. Default "float64" preserves
full precision. "float32" halves memory and gives ~1.5× speedup
with <0.1% accuracy loss for photometry.
Affects both fused (photometry + precomputation) and exact paths:
Fused path: captured arrays (SSP grid, dust weights, effective
wavelengths) cast to forward_dtype at kernel build; outputs
always cast back to float64 for cosmological distance scaling.
Exact path (spectroscopy, non-precomputed AGN): three largest intermediates
— metallicity-interpolated SSP (n_age,n_wave), dust attenuation
(n_age,n_wave), dust age weights (n_age,) — computed in
forward_dtype, halving the 4.5 MB memory traffic that dominates
exact-path dust cost.
Cosmological distances always use float64 (float32 overflows at z > 0.01).
Control which approximations the fused kernel uses. Default True enables
all approximations (fastest). False disables all (forces exact path
everywhere). A dict enables selective control:
"dust_attenuation": use dust at filter effective wavelengths (True, default)
"dust_emission": use MBB at filter effective wavelengths (True, default)
"igm": use IGM at filter effective wavelengths (True, default)
dust_emission: negligible for optical (MBB peak >50 μm)
igm: exact for fixed z (precomputed once)
csp_integration (str, optional) – CSP age integration scheme. Default "trapz" (trapezoidal on
linear time). Options: "log_trapz", "log_interp" (Dopita+2005
interpolation), "dsps_native" (DSPS trapezoidal with automatic
metallicity marginalization), "dsps_met_table" (time-evolving
metallicity table). See Appendix A of the forward model paper [2]_.
Frozen model configuration (immutable after init).
Type:
ModelConfig
Notes
JIT-compatible: yes — all prediction methods (except
predict() for lazy evaluation) are fully JAX differentiable
and can be called inside jax.jit() and jax.vmap().
Gradient-safe: yes — all physical parameters are differentiable
for inference via HMC, VI, and score-based methods.
Approximation scheme: The forward model uses a three-tier kernel
hierarchy to balance speed and accuracy:
Compositional (preferred): Full-resolution JIT SED from all
components → filter integration. XLA fuses entire graph (SFH → SED → photometry).
Bit-exact and fastest.
Hybrid (fallback): Precomputed SSP×filter stellar + exact
non-stellar at full wavelength resolution.
Exact (reference): Raw pipeline, no approximations or precomputation.
Time: years (yr). User-facing API converts to Myr/Gyr.
Wavelength: Angstrom (Å).
Luminosity (SED components): erg/s/Hz (L_ν).
Luminosity (photometry): erg/s/cm²/Hz (f_ν).
Metallicity (SSP grid): log₁₀(Z) absolute. User API uses log₁₀(Z/Z☉).
AGN bolometric luminosity: log₁₀(L_bol/L☉) at API level.
IGM absorption gotcha: predict_obs_sed() applies IGM transmission
at observed-frame wavelengths (input to igm_transmission() is redshifted).
This is automatic when igm=True in spec.
Return a hashable signature identifying JIT-graph shape and structure.
Two SEDModel instances with the same compile_signature() will produce
identical XLA compilation graphs (for identical Fitter configurations),
enabling cross-galaxy engine reuse in PopulationFitter and CatalogFitter.
The signature captures every JIT-affecting field: SSP array shapes,
filter grid dimensions, dust/AGN/nebular model identities, and all
configuration flags that determine the control flow during inference.
Returns:
Hashable immutable signature. Entries are immutable types
(int, str, tuple, bool, None) or tuples thereof.
This signature is used by Fitter._get_or_build_engine to key the
module-level _SHARED_ENGINE_CACHE. Changes to SEDModel initialization
that affect JIT graph shape MUST be added to this method to avoid
silent miscompilation.
method (str) – Inference method. Default "vi" (geoVI variational inference).
Any canonical name accepted by Fitter.run() works here:
"vi", "vi_linear", "mcmc", "mcmc_raytrace",
"mcmc_nuts", "map", "laplace", "auto", etc.
data_type (str or None) – "photometry", "spectroscopy", or "joint".
When None (default), inferred from the model’s observation
or from whether photometry= / spectrum= kwargs are used.
photometry (tuple of (flux, noise), optional) – Photometric data for joint fitting. Pass alongside spectrum=.
spectrum (tuple of (flux, noise), optional) – Spectroscopic data for joint fitting. Pass alongside photometry=.
init (str or None) – Initialization strategy. "map" runs MAP optimization first, then
uses the result to warm-start the requested method. None (default)
uses the method’s own default initialization.
**kwargs – Forwarded to Fitter.run().
Returns:
Inference results. ._fitter is set so .refine() works.
After this call, self.fitter_ holds the Fitter instance.
Enables population-level constraints on shared PSD hyperparameters
(e.g., shared burst timescale across a sample). All galaxies must
use the same model configuration.
Build a SEDModel from a grouped configuration dict.
Reduces boilerplate for the common case: instead of constructing
Parameters, SSPData, Observation, and SEDModel separately,
provide a single grouped config and receive a fully configured SEDModel.
Parameters:
ssp (str or SSPData) – Path to SSP HDF5 file, or a pre-loaded SSPData instance.
sfh (str) – SFH family name, e.g. "tsnorm", "dpl", "dpl+field".
dust (str) – Dust attenuation law. "charlot_fall" (default), "calzetti", etc.
nebular (str or None) – Nebular emission backend. "baked_in", "cloudy", "cue", or None.
agn (str or None) – AGN model. None (disabled) or any AGN model name.
redshift (float or str) – Fixed redshift (float), or "free" to add a free redshift parameter.
filters (list of str, optional) – Filter names for photometry, e.g. ["sdss_u","sdss_g","sdss_r"].
wave_obs (array, optional) – Observed-frame wavelength array for spectroscopy.
priors (dict, optional) – Parameter priors. Keys may be short names ("log_peak_sfr"),
universal short names ("logzsol"), or full prefixed names.
Short names are expanded automatically.
**model_kwargs – Forwarded to SEDModel.__init__().
Returns:
Fully initialized model ready for prediction or fitting.
Shows 16th and 84th percentiles as filled region, with individual
sample curves in light color. If true_params provided, shows
truth in black with dashed line for smooth SFH (parametric part).
The same model instance, with _state.precomputed.spectroscopy
and _state.wave_obs updated immutably via
dataclasses.replace. Returned for chaining; both
model.precompute_spectroscopy(wave);model.fit(data) and
fit=model.precompute_spectroscopy(wave).fit(data) work.
Return type:
self
Notes
State internals (_precomputed, _state) are frozen
dataclasses; this method swaps them via dataclasses.replace
rather than mutating their fields. The model object itself is
the controller and is not replaced — see Phase 3 of the
refactor: cached compiled artefacts are dropped via
clear_model_cache(self) and lazily rebuilt on next use.
Caches precomputed SSP spectra at the fixed redshift,
enabling ~10-20× speedup for repeated spectrum predictions.
Requires fixed redshift (raises ValueError otherwise).
Pre-compute SSP photometry on a redshift grid for free-z fitting.
At inference time, the precomputed table is interpolated to the
current z — same speedup as fixed-z precomputation, but z is free.
Follows the DSPS precompute_ssp_obsmags_on_z_table approach.
n_z (int) – Number of grid points (default 100). More points = more accurate
interpolation. 100 gives <0.01% interpolation error for smooth
filter transmission curves.
Returns:
The same model instance, with _state.precomputed.photometry_ztable
updated immutably via dataclasses.replace. Returned for
chaining; cache cleared via clear_model_cache(self).
Return type:
self
Notes
Enables fast photometry prediction with free redshift (no fixed z).
Interpolates precomputed SSP×filter grid to current z at inference time,
achieving similar speedup as fixed-z precomputation.
Examples
>>> model.precompute_ztable(z_min=0.01,z_max=4.0,n_z=200)>>> flux=model.predict_photometry(params)# z now free
Create a lazy prediction object for all derived physical quantities.
Returns a Prediction object that computes and caches
derived quantities on first access. This is the recommended API
for interactive exploration of a single galaxy’s properties,
trading speed for convenience.
For batch computation over posterior chains or mock catalogs,
use the JIT-compatible methods predict_sfh_quantities(),
predict_sed_quantities(), or predict_line_luminosities()
with jax.vmap() instead (up to 1000× faster for large batches).
Parameters:
params (dict) – Parameter values using public parameter names.
Lazy evaluation: Quantities are computed only when accessed.
Repeated access to the same property reuses cached results.
This is transparent to the user.
NaN handling: Some quantities (e.g., stellar_mass_surviving,
l_dust_absorbed) may return NaN if required data/parameters
unavailable (e.g., no mass-remaining table, dust_model=’none’).
The Prediction object handles NaN gracefully (returns None when
data required to compute the quantity is absent).
Compute derived physical quantities as a flat dict.
Convenience wrapper around predict() that extracts the key
SFH-derived scalars into a plain dict. Use predict() for
lazy on-demand access to all quantities, or
predict_sfh_quantities() for JIT-compatible batch computation.
“stellar_mass”: total mass formed [M_sun]
“stellar_mass_surviving”: surviving mass in living stars +
remnants [M_sun] or None if mass-remaining table not loaded.
”sfr_100myr”: SFR averaged over last 100 Myr [M_sun/yr]
“sfr_10myr”: SFR averaged over last 10 Myr [M_sun/yr]
“ssfr”: specific SFR [yr^-1], uses surviving mass if
11 standard survey-diagnostic lines (lya, civ_1549, oii,
hbeta, oiii_4959/5007, nii_6548/6584, halpha, sii_6717/6731).
Returns all-NaN when the active nebular backend does not
publish a discrete line catalogue (BakedIn, shock).
where \(\text{SFR}_{10}\) is the SFR averaged over the last 10 Myr
(the ionizing-photon relevant timescale), derived from
Q_H ≈ 4.2 × 10⁵³ × SFR [photons/s] and
L_Hβ = 4.76 × 10⁻¹³ × Q_H erg/s converted to L_sun.
Parameters:
params (dict) – Model parameters (from spec.sample() or a Posterior).
Uses Case B recombination coefficients (Leitherer et al. 1999 [1]_).
If SFH computation fails (e.g., invalid params), returns safe fallback of 1 L_sun.
Calls the nebular backend to compute line luminosities,
selects target lines by wavelength matching, and converts
from luminosity (Lsun) to observed flux (erg/s/cm^2).
target_wavelengths (array, shape (n_target,), optional) – Rest-frame vacuum wavelengths (Angstrom) of lines to predict.
Each wavelength is matched to the nearest backend line.
If None, returns all lines from the nebular backend.
tolerance_aa (float or None, default 5.0) – Maximum allowed wavelength delta [Angstrom] between a requested
target and the matched catalogue line. Raises ValueError on
any miss, listing the offending targets. Pass None to disable
(recovers legacy nearest-line-no-matter-what behaviour).
Compute observed-frame SED (redshifted + IGM + DLA transmission).
Evaluates the rest-frame SED, redshifts to observed frame
(wavelength × (1+z)), and applies IGM and DLA absorption where
configured. At z=0, identical to predict_rest_sed().
Parameters:
params (dict) – Parameter values using public parameter names.
wave (array, optional) – Custom rest-frame wavelength grid [Angstrom] before redshifting.
If None, uses model default.
IGM absorption: Applies transmission via
\(T_{\mathrm{IGM}}(\lambda_{\mathrm{obs}}, z)\) when igm=True in spec.
Uses Inoue+2014 [1]_ mean IGM with optional extensions for:
CRITICAL GOTCHA: IGM transmission takes observed-frame wavelengths
as input. The redshifted wavelength in this SED is already in observed
frame, so igm_transmission(wave_obs,z) is called correctly.
DLA absorption: Applies Lyman-series damping wing when dla=True.
Parameterized by neutral column density log₁₀(N_HI) and temperature.
See dla_transmission_obs().
Physical units:
Wavelength: observed-frame Ångstrom (redshifted)
SED: erg/s/Hz (same as rest-frame), but now at redshifted
wavelengths and reduced intensity by \((1+z)\) factor from
cosmological redshift
Compute observed photometric flux densities through all filters.
Convolves the SED (redshifted and IGM-absorbed) through filter
transmission curves, returning flux densities in the AB system
at the source. Supports three prediction modes for speed/accuracy
tradeoff: compositional (exact, XLA-fused), hybrid (precomputed
stellar + exact non-stellar), and exact (full pipeline, slowest).
Parameters:
params (dict) – Parameter values using public parameter names (e.g.,
sfh_tsnorm_log_peak_sfr, met_logzsol, redshift).
See Parameters for canonical names.
"auto" — cascade through available: compositional → hybrid → exact
"compositional" — full-resolution JIT SED kernel (bit-exact,
fastest). All components evaluated at full wavelength, integrated
through filters in single XLA-fused graph. Preferred when available.
"hybrid" — precomputed SSP×filter photometry (stellar, ~0.4% error)
+ exact non-stellar (emission, AGN, dust) at full wavelength, integrated
through filters. Fallback when compositional unavailable (e.g.,
variable-redshift, evolving metallicity, tabulated SFH).
"exact" — raw forward pipeline, no kernel JIT, no precomputation.
Reference accuracy, slowest (~5–10× slower than compositional).
flux_density – Observed flux densities in erg/s/cm²/Hz (AB system, rest-frame
reference frame corrected for luminosity distance and (1+z)
redshift factor).
ValueError – If no filters configured in the model (pass filters or
observation= to constructor).
Notes
JIT-compatible: yes — compositional and hybrid modes are
JIT’d at initialization. Exact mode is not JIT’d. All modes
are safe inside jax.grad() for parameter gradients.
Approximate accuracy: Compositional and hybrid modes produce
predictions within 0.1%–0.4% of exact (see CLAUDE.md for
mode-specific tolerances). Differences driven by:
Approximations enabled via approx: see SEDModel
for individual component tolerances
Filter wavelengths: All filters loaded via load_filter_set()
or Photometry are assumed to be in observed frame (redshifted).
The model auto-redshifts rest-frame SED by \((1+z)\) before
filter integration.
Runs the SEDComponent chain on the model’s configuration,
then projects the resulting rest-frame SED through every
filter in self.observation.photometry. Returns flux
densities in the AB system at the source.
ValueError – If no photometric filters are configured on the
observation.
Notes
JIT-compatible: yes — uses jax.jit()-friendly
tengri.observation.photometry.compute_flux_density()
per filter.
Differs from the legacy predict_photometry(): this
path goes through the SEDComponent orchestrator (no fused
kernel dispatch); for inference workflows where you compile
once and run thousands of times, the warm latency is
equivalent (~2 ms). For one-shot photometry the legacy path
with its tier-1/tier-2 fast paths is still faster.
Compute rest-frame panchromatic SED luminosity spectrum.
Evaluates all stellar populations, emission (nebular, AGN), and
multi-wavelength (radio, X-ray) components in rest-frame coordinates.
Returns the total SED integrated across the age distribution set by
the SFH and stellar mass parameters.
Parameters:
params (dict) – Parameter values using public parameter names.
wave (array, optional) – Custom rest-frame wavelength grid [Angstrom]. If None,
uses the model’s default: SSP wavelength grid
(ssp_data.ssp_wave), or auto-extended grid if
radio=True or xray=True in spec.
SED: erg/s/Hz (L_ν), normalized to the total stellar mass
implied by the SFH
SED components: Total SED is the sum of:
Stellar continuum (CSP from SSP integration)
Nebular continuum (if nebular_mode ≠ ‘baked-in’)
Nebular emission lines (if neb_* params free)
AGN continuum (if agn_model set)
Dust attenuation (applied to stellar + AGN)
Dust emission (re-radiated IR, if dust_emission_model set)
Shock emission (if shock=True)
Radio/X-ray (if radio=True or xray=True)
Attenuation: Applied via two-component (birth cloud + diffuse ISM)
or single-screen dust law, parameterized by age-dependent optical depth.
See components.dust for available laws.
Compute SED-derived quantities in JIT-compatible form.
Evaluates the full forward model and computes UV slope, spectral
indices (D4000, Balmer break), bolometric/IR luminosities, dust
attenuation, and luminosity-weighted age/metallicity. Returns
a SEDQuantities NamedTuple that is fully JIT-compatible
and vmap-ready for batch inference.
Parameters:
params (dict) – Parameter values using public parameter names.
Returns:
NamedTuple with fields:
l_bol : float. Bolometric luminosity [L☉]
l_tir : float. Total infrared (8–1000 μm) luminosity [L☉]
l_dust_absorbed : float. Dust-absorbed luminosity [L☉]
(intrinsic − attenuated), or NaN if intrinsic SED unavailable.
irx : float. Infrared excess := L_TIR / L_UV(1600 Å).
Common probe of dust obscuration (Dale et al. 2001).
uv_slope_beta : float. UV slope (power-law index) in
f_λ ∝ λ^β for 1200–2600 Å.
dn4000 : float. D_n(4000) break ratio: flux average
at 3750–3950 Å / 4050–4250 Å. Indicator of stellar age.
balmer_break : float. Balmer break: flux ratio
~3700 Å / ~4000 Å. Old stellar population signature.
m_uv : float. Absolute magnitude at 1500 Å
(M_1500, standard reionization-era indicator).
fuv_flux : float. Flux at 1500 Å [erg/s/cm²]
nuv_flux : float. Flux at 2300 Å [erg/s/cm²]
fuv_flux_intrinsic : float. FUV flux, dust-free
(intrinsic SED). NaN if unavailable.
nuv_flux_intrinsic : float. NUV flux, dust-free. NaN
if unavailable.
rest_uv_color : float. Rest-frame UV color (f_1500 − f_2300).
luminosity_weighted_age_gyr : float. Luminosity-weighted
age [Gyr] (∫L_λ age dλ / ∫L_λ dλ).
luminosity_weighted_metallicity : float. Luminosity-weighted
log₁₀(Z/Z☉) or absolute log₁₀(Z).
Gradient-safe: yes — all quantities are differentiable w.r.t.
SFH, metallicity, and dust parameters.
Spectral indices: Computed directly on the rest-frame SED
(not broadband-filtered). All wavelengths defined in rest frame.
Dust-absorbed luminosity: Defined as L_dust = L_intrinsic − L_attenuated
(i.e., the energy re-radiated in the IR). Requires the forward model
to track both intrinsic and attenuated SEDs internally. Returns NaN if
dust_model="none" or intrinsic SED not available.
Compute SFH on uniform linear-time grid for visualization.
Evaluates the SFH parameterization at n_linear evenly-spaced
points in lookback time, returning both the smooth parametric
component (sfr_mean) and the full SFH including GP-field
modulation (sfr_full, if stochastic SFH enabled).
Parameters:
params (dict) – Parameter values using public parameter names.
n_linear (int, optional) – Number of output grid points, evenly spaced in lookback time.
Default 1000 (sufficient for smooth visualization).
Returns:
"t_gyr" : array, shape (n_linear,).
Lookback time [Gyr], from 0 (now) to ~13.8 (Big Bang).
"sfr_mean" : array, shape (n_linear,).
Parametric mean SFR [M☉/yr] (no GP modulation).
"sfr_full" : array, shape (n_linear,).
Full SFH including GP field [M☉/yr]. Identical to sfr_mean
if stochastic SFH not enabled.
Return type:
dict with keys
Notes
JIT-compatible: no — uses Python-side interpolation. For
JIT-compatible SFH evaluation, use predict_sfh_quantities()
to get integrated quantities (stellar mass, age, etc.).
Time grid: Output is on a uniform linear-time (lookback) grid,
not the internal log-age grid. This makes visualization cleaner
and suitable for plotting.
SFH mean vs. full: When correlated-field (stochastic) SFH is enabled,
sfr_mean shows the smooth parametric trend (e.g., exponential
decline), while sfr_full adds GP modulation for realistic burstiness.
If parametric-only SFH is used, they are identical.
Physical units: Output SFR is in M☉/yr. Lookback time is in Gyr
(cosmic time before today).
Compute SFH-derived quantities in JIT-compatible form.
Integrates the SFH to compute stellar mass, recent SFR, specific SFR,
and mass-weighted age/metallicity. Returns a SFHQuantities
NamedTuple that is fully JIT-compatible and vmap-ready for batch
inference over posterior chains or mock catalogs.
Parameters:
params (dict) – Parameter values using public parameter names.
Returns:
NamedTuple with fields:
stellar_mass : float. Total stellar mass formed [M☉]
stellar_mass_surviving : float. Mass in living stars + remnants [M☉],
or NaN if SSP mass-remaining tables not loaded.
sfr_100myr : float. SFR time-averaged over last 100 Myr [M☉/yr]
sfr_10myr : float. SFR time-averaged over last 10 Myr [M☉/yr]
ssfr : float. Specific SFR (SFR/M_surv or SFR/M_formed) [yr⁻¹]
mass_weighted_age_gyr : float. Mass-weighted age [Gyr]
mass_weighted_metallicity : float. Mass-weighted log₁₀(Z/Z☉) or
absolute log₁₀(Z) depending on metallicity mode
Gradient-safe: yes — all quantities are differentiable w.r.t.
SFH and metallicity parameters.
Surviving mass: Requires SSP grid with ssp_mass_remaining
(e.g., FSPS grids). If unavailable, returns NaN. predict()
handles NaN gracefully when the quantity is unavailable.
SFR averaging: Time-weighted mean over lookback-time window:
Routes through the orchestrator and converts the resulting
PipelineState to a legacy SFHQuantities
NamedTuple via tengri.forward.state_to_sfh_quantities().
Same return shape as the legacy method, computed via the
Phase II SEDComponent path.
Compute observed spectrum at given wavelengths with LSF convolution.
Evaluates the full SED at custom wavelengths in observed frame,
applies velocity dispersion broadening (if sigma_v in spec),
convolves with instrument line-spread function, and optionally
applies multiplicative Chebyshev calibration polynomial.
Parameters:
params (dict) – Parameter values using public parameter names.
wave_obs (array, optional) –
Observed-frame wavelength grid [Angstrom]. If None, uses:
wave_chunk_size (int, optional) – If specified, split observed-frame wavelength axis into chunks of
this size and evaluate via jax.lax.map to reduce per-chunk HLO
size for XLA compilation. Default None (no chunking, exact behavior).
For spectroscopy with R~500 at N≥64 galaxies, typical value is 32–64
to avoid XLA compilation wall-clock.
Returns:
flux – Observed spectral flux density [erg/s/cm²/Hz] in the AB system
at the specified wavelengths.
ValueError – If wave_obs is None and no precomputed wavelength grid available.
Notes
JIT-compatible: compositional and hybrid modes are JIT’d.
Exact mode is not JIT’d.
Velocity dispersion: When sigma_v is in free params,
applies line-of-sight broadening via Gaussian convolution at
FWHM = 2.355×sigma_v. Implemented as wavelength-space
Gaussian convolution (valid for linear pixels; use
apply_lsf() for
log-wavelength pixels).
Precomputed wavelength grid: For fixed-redshift models with
fixed wavelength grid, call precompute_spectroscopy(wave_obs)()
at initialization to cache spectroscopy kernels. This enables the
hybrid/compositional paths for ~10× speedup vs. exact.
Wavelength-axis chunking: Set wave_chunk_size to split the
observed-frame wavelength axis into ~N/chunk_size chunks and evaluate
independently via lax.map. Each chunk’s HLO is ~1/K of the full HLO
(K = chunk_size / min_chunk_width), reducing XLA compile-time
superlinearly. Numerical output is bitwise-identical to unchunked.
Typical runtime overhead: +5–20% per galaxy due to map overhead.
Runs the SEDComponent chain, applies the cosmological redshift +
luminosity-distance projection, interpolates onto wave_obs,
and (if configured) applies the instrument LSF + velocity-dispersion
broadening. Mirrors the contract of the legacy
predict_spectrum()’s observed-frame output but goes through
the SEDComponent chain rather than the fused kernel.
wave_obs (array_like, shape (n_pix,), optional) – Observed-frame wavelength grid [Angstrom]. If None,
falls back to the precomputed grid (self._wave_obs or
self._precomputed.spectroscopy.wave_obs_pixels).
Returns:
flux – Observed-frame spectral flux density [erg/s/cm^2/Hz].
ValueError – If no wave_obs grid is supplied or precomputed.
Notes
JIT-compatible: yes — run_components(), the rest→obs
projection in observe_spectrum_from_rest_sed(), and
apply_lsf() are all JIT-friendly. No calibration polynomial
is applied; callers that need calibration should compose it on
top via the user-likelihood Protocol path.
Forward pass via the Phase II SEDComponent orchestrator.
Builds a component chain from this model’s structural settings
(self.spec + self.ssp_data + dust / nebular / AGN / radio
/ X-ray / IGM flags) and threads params through
tengri.forward.run_components(). Returns the final
tengri.core.PipelineState, not a legacy
Prediction — callers wanting the legacy shape should
keep using predict_photometry()/predict_spectrum()
until the full integration adapter ships.
This is the public bridge between SEDModel’s
configuration surface and the orchestrator: it lets users with
an existing SEDModel go through run_components without
re-typing the chain at every call site.
Parameters:
params (Mapping) – Free parameters keyed by canonical name (sfh_*,
met_*, dust_*, agn_*, radio_*, xray_*,
igm_*, redshift).
Returns:
Threaded state after the chain runs. sed_intrinsic is
the rest-frame total SED in erg/s/Hz; sed_observed is
populated when an IGM component is present; derived
carries every cross-component publication (L_ir,
L_agn_bol, log_mstar, lnu_age, etc.).
Return type:
PipelineState
Notes
JIT-compatible: yes — run_components() and every
adapter’s apply are pure JAX.
self.spec.mean_sfh_type is a list (e.g. ["tsnorm"],
["dpl","field"]); the first entry is the mean SFH model,
and "field" anywhere in the list enables the GP-field
branch. Anything else (burst, etc.) is currently unmapped
and will raise downstream.
Return a human-readable physics tree showing the model hierarchy.
Shows the active sub-models at each physical layer (SFH, SPS, Dust,
Nebular, AGN, Observation), the free parameters at each layer, and
the recommended inference method.
Parameter specification defining all model parameters and their priors.
A Parameters object defines the complete parameter set for a SEDModel,
including both the mean SFH model(s) and all optional components (dust,
nebular emission, AGN, etc.). Parameters can be sampled (for mock data
generation) or used as priors for inference.
Parameters are specified as keyword arguments, each of which can be:
Scalar (int/float) → Fixed(value) — parameter is constant
Tuple (lo, hi) → Uniform(lo,hi) — shorthand for uniform prior
Distribution object — Uniform, Gaussian, LogUniform,
LogNormal, StudentT, or Fixed
A Parameters object also stores model configuration settings
(mean_sfh_type, dust_law, nebular mode, etc.) that control which
components are enabled. These are not fittable parameters.
Parameters:
**kwargs (keyword arguments) – Model parameters (distribution objects or shorthands) and settings
(see “Settings” section below).
ValueError – If parameter names are invalid for the selected mean_sfh_type.
ValueError – If nebular/dust/AGN settings are mutually incompatible.
Notes
Not JAX-traced: Parameters is the central user-facing object for
configuring model parameters and their priors. Parameters objects cannot be
created or modified inside a JAX gradient tape (jax.grad, jax.vmap, jax.jit).
Create all Parameters objects at the Python level before tracing. Once created,
a Parameters object is immutable — use the with_params() method to create
modified copies.
Parameter auto-detection: If mean_sfh_type is not explicit, it is
inferred from the parameter name prefixes (e.g., ‘sfh_dpl_alpha’ implies
‘dpl’ is active). The inferred type is normalized to a sorted list.
Mirror parameters: A parameter can be tied to another by passing the
target name as a string instead of a distribution. Example:
neb_logZ_gas="met_logzsol" ties gas metallicity to stellar.
Settings (model configuration, not fittable parameters)¶
The prior distribution object (Uniform, Gaussian, LogUniform, etc.)
or Fixed for non-free parameters.
Return type:
Distribution
Raises:
KeyError – If parameter name is not valid for this model configuration.
Notes
For fixed parameters, the returned Distribution has is_fixed=True.
For free parameters, the returned Distribution is one of Uniform,
Gaussian, LogUniform, LogNormal, StudentT, or other prior types.
Extract all numeric fixed parameter values as a dict.
Fixed (non-free) parameters are constants that do not vary during
inference. This method returns only the numeric ones; categorical
Fixed parameters (strings) are excluded.
Parameters:
None
Returns:
Mapping of numeric fixed parameter names to their constant values.
String-valued Fixed parameters are not included because they cannot
be represented as float.
Return a copy augmented with extra observation-level parameters.
Used by inference to inject emission-line amplitude parameters so they
flow through bounds, prior penalty loops, and summary output without
requiring special-casing in downstream code.
Parameters:
**extra_params (Distribution) – Mapping of parameter name → Distribution to add (e.g.,
eline_EW_Halpha=Uniform(0,1000)).
Returns:
New Parameters instance with extra_params included in
free_params. The original instance is not modified
(immutable pattern).
Copy mirrored parameter values from source to target.
For each mirror target→source, copies the sampled source value
to the target parameter. Used after sampling to ensure tied parameters
have identical values. Returns a new dict (immutable pattern).
Parameters:
params (dict[str, ndarray]) – Parameter name → sampled value. Must include all source parameters.
Returns:
New dict with mirrored values filled in. For each target, the
sampled value of the source parameter is assigned. Non-mirrored
parameters are unchanged.
Draw one random sample from all parameter prior distributions.
Samples all free parameters from their priors, returns fixed parameters
at their fixed values, and (if stochastic) generates the latent field
ξ ~ N(0,I). Mirrors are resolved (target ← source value).
Parameters:
key (jax.Array (PRNGKey)) – Random key for sampling.
Returns:
Parameter name → sampled value. Free parameters are sampled from
their prior distributions. Fixed parameters return their constant
value (as float or string). If stochastic, sfh_field_xi is an
array of shape (n_grid,). Dictionary is immutable-ready (no
direct mutation of values).
JIT-compatible: yes — safe to call inside jax.jit() on the
key and parameters only (not on branching logic).
Stochastic SFH: When the model includes a GP field, sfh_field_xi
is an independent N(0,1) vector of length n_grid. The SED model uses
this to generate the stochastic log-SFR perturbations.
Print a human-readable summary of the model configuration.
Displays SFH type, enabled components (nebular, dust, AGN, etc.),
dimensionality, and a table of all parameters grouped by category
(free first, then fixed). Useful for printing model status before fitting.
Use summary_str() if you need the underlying string (e.g. for
logging) — summary() itself prints and returns None,
matching the rest of the discovery API
(tengri.summary(), tengri.help(), etc.).
Parameters:
None
Returns:
Output is printed to stdout.
Return type:
None
Notes
Output includes:
- SFH type and composition
- Dimensions (n free, latent ξ, mirrored, fixed)
- Enabled optional modules (nebular, dust_emission, AGN, etc.)
- Tabular list of parameters with their distributions/values
Return a new Parameters with additional parameters merged in.
Creates an independent copy of this Parameters with extra parameters
added (usually observation-level parameters like calibration or noise).
User-defined parameters take precedence — if a name already exists in
this spec, the new value is silently skipped (user intent is preserved).
Typically used internally by SEDModel to auto-inject noise and
calibration parameters into the specification.
Parameters:
**kwargs – Parameter name → Distribution (or scalar/tuple shorthand).
Only params not already explicitly provided by the user are added.
Returns:
New instance with merged parameters. The original is not modified
(immutable pattern).
Immutability: The original Parameters object is never modified.
A new Parameters instance is created via copy.copy(), with internal
mutable structures (dicts) replaced with copies.
Parameter priority: User-provided parameters (via __init__) take
absolute precedence. Auto-merged parameters are added only if their
name is not in the user-provided set.
Lazy prediction object with on-demand computation of derived quantities.
Created via model.predict(params). Properties are computed on
first access and cached. The cache is shared across all property
groups (sfh, sed, lines, radio, xray,
ionizing), so related quantities share the expensive
intermediates.
The grouped form (pred.sfh, pred.sed, pred.lines,
pred.radio, pred.xray, pred.ionizing) exposes every
derived quantity. The top-level shortcuts cover the most-used
quantities for quick access; for less-common ones use the grouped
form. Both share the same lazy cache, so accessing a quantity by
either route triggers computation only once.
Luminosity-weighted age [Gyr]. Same as pred.sed.luminosity_weighted_age_gyr.
Both pred.sfh and pred.sed define this; the top-level shortcut
forwards to the SED version (canonical “luminosity-weighted” using the
attenuated stellar SED).
JAX-compatible array container. All fields are JAX arrays compatible with
jax.jit and jax.vmap. Returned by Prediction.lines when
accessed. All fields return NaN if no nebular model is active in the SEDModel.
Generate mock galaxy photometry with optional Gaussian noise.
Computes noiseless predicted photometry, then optionally realizes noise
at a specified signal-to-noise ratio. Useful for testing data pipelines,
validating inference, and parameter recovery studies.
Parameters:
model (object) – Any object with a predict_photometry(params) method that returns
an array of flux densities.
params (dict[str, ndarray]) – Model parameter values (typically sampled or optimized).
key (jax.Array (PRNGKey), optional) – Random key for noise realization. If None, only noiseless
photometry is returned (no flux_obs key in output).
Mock observation data with keys:
- flux_true : noiseless predicted photometry [erg/s/cm²/Hz]
- noise : noise standard deviation per band [erg/s/cm²/Hz]
- params : the input parameter values
- flux_obs : observed (noisy) photometry (only if key is not None)
Composes optional photometric, spectroscopic, and noise model
configurations. At least one of photometry or spectroscopy
must be provided.
Parameters:
photometry (Photometry or None) – Photometric filter configuration.
spectroscopy (Spectroscopy or None) – Spectroscopic instrument configuration.
noise (NoiseModel or None) – Noise model configuration (calibration floor, Student-t dof).
line_fluxes (LineFluxData or None) – Observed emission line fluxes for direct fitting.
When provided, the likelihood includes an additive chi-squared
term comparing model line luminosities against these fluxes.
Returns:
Validated observation container with at least one data modality.
A frozen, immutable dataclass that serves as a declarative container
for all observation metadata. Never enters JAX-traced code; used solely
for configuration dispatch to precomputation and inference steps.
Inspired by Synthesizer’s Instrument pattern, adapted for tengri’s
differentiable context.
This method is called by the inference engine to set up the prior
structure. Observation parameters include calibration coefficients
and noise model hyperparameters, but not SED or SFH params.
Requires photometry to be configured. Computes rest-frame wavelengths
from the observed-frame input and integrates the SED against each
filter transmission curve.
Requires spectroscopy to be configured. Applies LSF convolution
if a resolution profile is specified. Returns data ready for
likelihood evaluation against observed spectra.
Concatenate photometry and spectroscopy data in canonical order.
Validates array shapes against the observation configuration.
Canonical order: [photometry,spectroscopy].
Parameters:
phot (array or None) – Photometric data, shape (n_filters,).
spec (array or None) – Spectroscopic data, shape (n_pixels,).
Returns:
Packed data array, shape (n_data,).
Return type:
jnp.ndarray
Raises:
ValueError – If array shapes don’t match the observation configuration.
Notes
Both arrays are optional but at least one must be provided and
configured in the Observation. Useful for likelihood evaluation
and parameter inference pipelines.
A frozen dataclass that encapsulates filter metadata and transmission
curves. Provides factory methods (from_names, from_filter_set) for
convenient construction. Precomputes derived fields (filter_waves,
filter_trans, n_filters) at initialization for efficient SED projection.
Loads filter transmission curves from the SVO filter service or
local cache. Filter names are validated against the registry;
unrecognized names raise a KeyError.
resolution (float, jnp.ndarray, or None) – Spectral resolution R=lambda/delta_lambda.
Scalar for constant R, per-pixel array for wavelength-dependent,
or None to skip LSF convolution. Default: None.
sigma_lib_kms (float) – SSP library velocity dispersion [km/s] to subtract in quadrature
when applying the LSF. Default: 70.0 (MILES).
lsf_n_bins (int) – Number of bins for piecewise constant approximation of
variable-R LSF convolution. Default: 16.
calibration_order (int) – Order of multiplicative Chebyshev calibration polynomial.
0 = no calibration (default). Order N adds N free params
(cal_c1, …, cal_cN) with Gaussian(0,0.1) priors.
eline_prior_sigma (float) – Prior width on emission line amplitudes for marginalization.
Default: 100.0.
"marginalized": Analytically marginalize line amplitudes
(recommended for spectroscopic fitting).
"fitted": Line amplitudes as free MCMC parameters.
Default: "off".
eline_catalog (LineList or None) – Line catalog. None falls back to LineList.default_13() for
Use LineList.default_optical() for
FastSpecFit parity. Default: None.
eline_prior_type (str) – Prior type for line amplitudes. One of "flat" (uninformative) or
"cloudy" (CLOUDY-grid-interpolated). Default: "flat".
eline_prior_width_dex (float) – Prior scatter in dex for the "cloudy" prior. Default: 0.3.
eline_broad_fwhm_min_kms (float) – Minimum FWHM for the broad component [km/s]. Default: 500.0.
covariance (jnp.ndarray or None) – Full spectral covariance matrix, shape (n_pix,n_pix).
When provided, the likelihood uses diff@C^{-1}@diff
instead of per-pixel sum((diff/sigma)^2). The inverse is
precomputed at construction time. Default: None (diagonal noise).
Returns:
Spectroscopy instance with covariance matrix inverted and metadata set.
A frozen dataclass that encapsulates spectroscopic instrument metadata,
including wavelength grid, resolution profile, calibration strategy,
and emission-line fitting configuration. Precomputes the inverse
covariance matrix at initialization for efficient likelihood evaluation.
Used by SEDModel to configure spectral prediction and by the inference
engine to set up calibration priors.
Convenient factory for instruments with wavelength-independent
spectral resolution, such as low-resolution JWST NIRSpec PRISM
approximations or ideal spectrographs.
This configuration mirrors DESI’s optical spectroscopy capabilities,
including marginalized emission line amplitudes with CLOUDY-based
priors and atomic physics constraints. Suitable for large spectroscopic
surveys of emission-line galaxies.
Requires astropy. Generic reader for any FITS binary table
spectrum. For JWST x1d files, prefer from_jwst_x1d which
handles unit conversion and resolution auto-detection.
ext (int or str) – FITS extension containing the spectrum table. Default: 1.
resolution (float, ndarray, or None) – Spectral resolution override. If None, auto-selects based on
the FILTER or GRATING header keyword. Default: None.
**kwargs – Additional keyword arguments passed to Spectroscopy.
Returns:
(spec_config,flux_obs,flux_err) where flux values are in
erg/s/cm²/Hz and wavelengths in Angstrom.
Called by Observation.get_all_params() to register observation-level
parameters with the inference engine. Each coefficient has a weak
Gaussian prior centered at 0 in log space (unity in linear space).
Applies wavelength-dependent resolution for NIRSpec’s medium-resolution
G140M mode. Resolution is approximately constant at R~1000 across
the wavelength range.
Applies wavelength-dependent resolution appropriate for NIRSpec’s
PRISM mode. Resolution varies from R~30 in the red to R~330 in
the blue (Jakobsen et al. 2022).
Used for logging and diagnostics. Provides a compact, human-readable
representation of the instrument configuration. Intended for display to users,
not for programmatic parsing.
calibration_floor (float or Distribution) – Fractional calibration floor added in quadrature with observational
noise: sigma_eff=sqrt(sigma_obs^2+(f_cal*model)^2).
A float value becomes Fixed(value); a Distribution (e.g.
Uniform(0.01,0.15)) makes it a free parameter during inference.
Default: 0.0 (no calibration floor).
student_t_dof (float or None) – Degrees of freedom for a Student-t likelihood (heavier tails for
outlier robustness). None uses a standard Gaussian likelihood.
Default: None.
Returns:
Noise model instance with configuration validated.
Immutable container: A frozen dataclass. Fields are read-only by
convention.
—–
A frozen dataclass encapsulating noise model configuration. Replaces
the older pattern of manually creating noise_frac_cal and noise_dof
parameters. Primarily used to register observation-level hyperparameters
with the inference engine.
Called by Observation.get_all_params() to register noise model
hyperparameters with the inference engine. Parameters are only included
if they are non-trivial (calibration floor > 0 or Student-t dof is not None).
Frozen dataclass configuring the variational inference backend. Key fields:
method selects the VI algorithm ('vi' / 'vi_native'). The two
backends are NOT posterior-equivalent — 'vi_native' is ~19× faster but
PSD timescale posteriors differ from the NIFTy path; validate per-problem
before swapping.