kinextract
A non-parametric LOSVD (line-of-sight velocity distribution) extractor for galaxy spectroscopy.
kinextract recovers the LOSVD directly from an absorption-line
galaxy spectrum as a smoothly-positive, low-dimensional log-density
spline -- without assuming a Gauss-Hermite (or any other fixed)
functional form for the underlying kinematics. Positivity is structural
(built into the representation via an exp link), not an afterthought
enforced after the fact, and the number of free parameters is chosen to
match how much velocity information a spectrum actually contains, not
fixed at an arbitrarily fine grid.
Example notebooks
examples/notebooks/ walks from simple synthetic data to real data, in
order:
-
01_basic_mock_fit.ipynb-- foundations: the spectral model equation, the LOSVD's B-spline/exp-link parameterization, and the core LOSVD + template-mixture recovery machinery on a synthetic mock built from real E-MILES templates you choose (editTEMPLATE_RECIPE: any number of templates, any ages/metallicities/weights -- no separate single-template-vs-mixture code path), no external files needed. Stops at the point estimate. -
02_realistic_mock_fit.ipynb-- picks up where 01 left off: a genuinely degenerate 4-template mixture, what the regularization costs and a Cramer-Rao-bound noise-floor analysis forh4, thelamrule for broad LOSVDs, Laplace uncertainty, and the co-fitted continuum -- including why a recovered continuum's shape is trustworthy but its absolute level is not identifiable (a real, root-caused property of the model, not a bug). -
03_template_mismatch.ipynb-- fits the same mock spectrum with a template library that actually contains the true stellar population, and with one that systematically doesn't, and compares the recovered LOSVD directly -- motivates04_template_calibration.ipynb. -
04_template_calibration.ipynb-- run this first for any real MUSE target. Gets a MUSE datacube (a real ESO archive download -- here the dust-free half of NGC 4751's NFM cube -- a synthetic Sersic-profile mock with known ground truth, or shipped pre-computed products), bins it in radial annuli, fits every annulus against the full 636-template E-MILES grid, and iteratively reduces to a representative template subset (converges when the union of surviving templates across bins stabilizes) -- saved for reuse in07_full_galaxy_analysis.ipynb. -
05_noise_correlation.ipynb-- measures real MUSE pixel-noise correlation (AR(1) lag-1 correlation length) on a real spectrum, connects it to the Bacon et al. (2017) MUSE variance-underestimate factor, shows correlated-noise whitening (FitConfig.rho,measure_and_whiten) applied to a real fit and its uncertainty band, and confirms the whole mechanism on a synthetic mock with known, injected correlated noise. Section 8 adds the systematic residuals that dominate at high S/N (sky lines, template mismatch): thenoise_model_errorsvariance model andlosvd_error_bands. -
06_other_instruments.ipynb-- adapting this package to a different instrument (HST/STIS): follows the same 01 -> 02 -> real-fit progression, adapted to STIS -- a basic mock and a continuum-distorted mock built entirely from real STIS-observed stellar templates (no LSF-matching step needed -- templates and data share the same instrument), then a real HST/STIS NGC 5102 spectrum fit end-to-end -- demonstrates this package's workflow is not MUSE-specific and shows how to adapt it to your own instrument. -
07_full_galaxy_analysis.ipynb-- the capstone: a complete extraction of a real cube (NGC 4751, MUSE NFM), from masks to maps. It runs:- masks -- the dusty half of the galaxy and defective spaxels -- and a legacy polar dartboard;
- fits with the package defaults;
losvd_error_bands_parallel;- flags and hand tuning (
LAM_OVERRIDE,REVIEW) and example fits; - V_sys, moment maps and radial profiles;
- a mirror-symmetry check of the errors (bin against its mirror bin).
It runs from shipped results by default, or from the cube with
FORCE_RECOMPUTE = True.
Features
The defaults are the recipe validated on NGC 4751 MUSE data (480 matched
mocks and the real bins; dev_notes/regularization_v2/FINDINGS.md): a
bare FitConfig() and fit_losvd_robust reproduce that extraction
exactly, and losvd_error_bands its errors.
- LOSVD recovery (
kinextract.losvd,kinextract.config,kinextract.multi_template): one fitting engine (fit_losvd) recovers the LOSVD -- and, for more than one template, the mixture weights jointly via alternating minimization (exact NNLS for the weights, a multi-start exact-Hessian Newton MAP fit for the spline coefficients) -- for anyn_templates >= 1. Seelosvd.py's andmulti_template.py's module docstrings for the method, andexamples/notebooks/01_basic_mock_fit.ipynbfor a worked example with a configurable template recipe. - Regularization (
FitConfig.lam,FitConfig.lam_rule,sigma_scaled_lam,with_lam_rule): a third-difference roughness penalty on the log-LOSVD spline (its null space is any Gaussian, so heavy smoothing shrinks toward the best-fitting Gaussian, never toward fat tails) atlam = 4096. LOSVDs broader than 350 km/s are refitted at4096 (sigma/350)^6(capped at 131072), and fits still at the edge of the velocity grid (edge_amplitude_frac) at 2x, then a further 4x, theirlam. On the mocks nolamselector beat this rule by more than a few per cent; adaptive selection failed on the real spectra. - Continuum co-fitting (
kinextract.continuum, on by default with 4 knots): a shared, structurally-positive multiplicative continuum (C(lambda) = exp(spline(lambda))), jointly recovered with the LOSVD and template weights via 3-block alternating minimization. Validated on synthetic data, including the adversarial "does the continuum contaminate the weight recovery" check -- seecontinuum.py's own docstring and02_realistic_mock_fit.ipynb/06_other_instruments.ipynb. The recovered continuum's shape is reliable; its absolute mean is not identifiable (C's mean and the template weights' overall scale are the same degree of freedom --dev_notes/continuum/FINDINGS.md, 2026-08-01). - Robust fitting (
kinextract.robust_fit):fit_losvd_robustinflates the per-pixel errors wherever a preliminary fit's residuals call for it (sky-subtraction residuals, bad pixels; smoothed over 3 A, capped at 100x), with a loose cleanup clip for single-pixel outliers and the CaII triplet cores protected from clipping. Fixed bad-pixel ranges can be masked beforehand (mask_wavelength_windows), and emission lines detected (detect_emission_lines). - Error bands (
kinextract.laplace_uncertainty,kinextract.noise_model): the Laplace (curvature) approximation to the posterior at the fit'slam, jointly over the LOSVD and continuum coefficients (laplace_losvd_uncertainty). For real IFU data,losvd_error_bandsfirst fits a per-pixel varianceerr^2 + (kappa*s)^2 + (f*model)^2--sthe LSF-blended sky-line spectrum (sky_line_profile,load_muse_sky_lines),fa template-mismatch term -- to a preliminary fit's residuals, runs Laplace with it at the fit'slamandrhowithout its clipped pixels, and moves the draws onto the fit (recenter_samples). On the mocks this kept the shapes unchanged and raised the coverage of the model-binned LOSVD under sky contamination from 0.48 to 0.78. An optional NUTS (NumPyro) reference mode (kinextract.nuts_uncertainty, thenutsextra) samples the exact posterior, for spot-checks. - LOSVD moment estimation (
kinextract.gh_moments): summarizes a recovered LOSVD as (V, sigma, h3, h4) via a smooth-taper direct moment for V and sigma and a fixed-center Gauss-Hermite fit for h3 and h4; the joint Gauss-Hermite fit (fit_gauss_hermite) is kept for comparison with the wider dynamics literature. On fits at the defaults the two agree to about 0.5 km/s on the validation grid. - Noise-correlation whitening (
kinextract.noise_model): AR(1) pixel-covariance whitening (whiten,FitConfig.rho), a lag-1 ACF estimator (estimate_correlation_length),measure_and_whiten, and per-bin measurement inside the robust fit (fit_losvd_robust(..., measure_rho=True)) -- see05_noise_correlation.ipynb. - IFU cubes (
kinextract.muse_cube,kinextract.binning,kinextract.template_reduction): MUSE cube I/O, robust per-bin spectrum combination with spatial-correlation-aware errors, dartboard binning (including the legacy SCObin_r.out/bin_v.outgeometry), and iterative template-subset reduction. - Schwarzschild-model inputs (
kinextract.legacy_io):write_spmc_in_laplace/transvd_bounds_laplacewrite SCOspmc_infiles straight from Laplace/NUTS percentiles. Withb_samples=/model_bins=each dynamical-model velocity bin carries at least its binned posterior scatter:model.xsums per-point errors linearly, which understated it by about 2x.
Not yet implemented: the template-continuum-trust hierarchy (library-
keyed presets for how much of a raw template's own continuum to trust --
see continuum.py's docstring), wavelength-dependent LSF matching, an
end-to-end spectrum-file I/O and orchestration layer.
Known limitations
- The penalty shrinks genuine non-Gaussian structure toward a
Gaussian. h3 and h4 are attenuated wherever the data cannot hold
them up -- at low S/N, and for h3 across a wide low-to-moderate sigma
range, where recovered h3 stays close to 0. Much of this is the data's
own limit (a Cramer-Rao analysis in
02_realistic_mock_fit.ipynb), but the error bands describe noise, not this shrinkage. - Plain Laplace bands under-cover on real data, and the systematics
model only partly fixes it. With
losvd_error_bandsthe coverage of sky-contaminated mocks is about right (0.78 for a 0.68 target, slightly conservative). On clean, high-S/N mocks built from different templates than the fit it is still about 0.42: the remaining error is template-mismatch bias in the point estimate, which no band width fixes. - Real chi2_red on real IFU data commonly runs well above 1 (double
digits at high S/N) -- systematics-dominated residuals (sky-line
residuals, template mismatch, correlated per-pixel noise), not just
photon noise. The robust fit's error inflation and the noise-model
bands exist for this; see
05_noise_correlation.ipynband07_full_galaxy_analysis.ipynb. - Below roughly the instrument's own resolution (MUSE: sigma ~36-38
km/s in the CaT window), recovered sigma depends on
lamwith no plateau, and the reported uncertainty, a spread around the fit at itslam, does not cover that dependence. Confirmed on real NGC 5102 bins: sigma varies by 9-16 km/s across a 64-65536lamladder against a Laplacesherm_stdof 1-2 km/s (dev_notes/uncertainty/FINDINGS.md, 2026-08-11). Not resolved. - Multi-template weight recovery is sensitive to template-library
conditioning -- a near-collinear template library gives genuinely
unstable weight recovery across noise realizations, diagnosable via the
condition number of the per-step NNLS design matrix. See
01_basic_mock_fit.ipynb,02_realistic_mock_fit.ipynband03_template_mismatch.ipynb. Degrades gracefully (sparser/noisier weight recovery, never a crash or NaN); mitigated by template conditioning (04_template_calibration.ipynb). - JAX runs in float64 by default (opt out with
KINEXTRACT_NO_X64=1). This is a correctness requirement: under float32 the objective in the stiff heavy-penalty regime of wide velocity grids stalls the optimizer far from the optimum, reportingsuccess=Trueregardless. - Double-peaked/multi-component LOSVDs are not specifically handled. The non-parametric spline recovery can represent them (unlike a fixed Gauss-Hermite parametrization), but no notebook in this sequence specifically validates that case.
Installation
pip install -e ".[dev]"
Optional extras: .[eso] (ESO archive downloads), .[nuts] (the NUTS/
NumPyro reference uncertainty mode), .[docs] (building the docs),
.[examples] (the colormaps of the example notebooks' moment maps).
Testing
pytest tests/
277 tests covering LOSVD recovery and the lam rule, the Newton
solver, moment estimation, multi-template fitting, robust fitting with
error inflation (serial and parallel), continuum co-fitting, Laplace
uncertainty and the noise-model error bands (serial and parallel), NUTS
reference-mode sampling, AR(1) correlated-noise whitening, wavelength
masking, spatial binning and template reduction, legacy I/O, and
input-validation regressions. These are unit/sanity tests (correct shapes,
right order of magnitude, no crashes, and several real before/after
demonstrations against known synthetic ground truth) -- the full,
wide-grid statistical validation is in dev_notes/VALIDATION_CAMPAIGN.md,
dev_notes/validation_suite/ and dev_notes/regularization_v2/.
A_w/y_w (and A_stack_w) must not contain NaN or inf -- mask bad
pixels out of the arrays before calling any fit function; a clear
ValueError is raised otherwise rather than a fit silently returning a
NaN-filled LOSVD.
A handful of tests are marked @pytest.mark.slow (genuinely
multi-minute) and are skipped by default, both via -m "not slow" (this
project's own CI path) and by a bundled pytest-astropy-ecosystem plugin
that requires --run-slow to execute any slow-marked test at all, even
under a plain pytest tests/ with no -m filter. Pass --run-slow to
actually run them, e.g. before a release: pytest tests/ --run-slow.
Quick start
One fitting engine, for any number of templates (a single template is
just template_fluxes = [template_flux]):
from kinextract import FitConfig, build_template_stack, compute_losvd_moments, fit_losvd_robust
cfg = FitConfig(vmin=-1500.0, vmax=1500.0) # the velocity grid, about V +- 5-7 sigma; FitConfig.describe() lists the rest
v_grid = cfg.build_velocity_grid()
A_stack = build_template_stack(wave, template_wave, template_fluxes, v_grid) # (n_templates, n_pix, n_bins)
fit = fit_losvd_robust(A_stack, flux, err, cfg, wave_fit=wave)
moments = compute_losvd_moments(v_grid, fit["b"]) # vherm, sherm, h3, h4
mixture_fractions = fit["w"] / fit["w"].sum() # meaningful for n_templates > 1
Error bands, with the sky-line + template-mismatch noise model (sky_profile=None without sky residuals):
from kinextract import load_muse_sky_lines, losvd_error_bands, sky_line_profile
sky = sky_line_profile(wave, *load_muse_sky_lines("MU_GSKL_140101A_sky_lines.fits"), z=z)
bands = losvd_error_bands(A_stack, flux, err, wave, fit, cfg, sky_profile=sky)
print(f"sigma = {moments['sherm']:.1f} +/- {bands['sherm_err']:.1f}")
Run FitConfig.describe() to see every tunable parameter grouped by
subsystem. docs/quickstart.rst has more, and examples/notebooks/ has
complete, runnable, annotated examples.
Metadata
Release files for kinextract 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| kinextract-2.0.0.tar.gz | 213.2 kB | With provenance | Details for kinextract-2.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| kinextract-2.0.0-py3-none-any.whl | Python 3 | none | any | Details for kinextract-2.0.0-py3-none-any.whl |
Total release size: 370.0 kB
Release files / kinextract-2.0.0.tar.gz
| Download URL | kinextract-2.0.0.tar.gz |
|---|---|
| Size | 213.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c50f54787d0470b7034ee1bfa597778664f5ad56cec82052a82c179e4b6c1517
|
|
BLAKE2b-256 checksum How to use checksums |
2bd5ed6d3a10aa98d20d37f6da2b742757aa7a2485c964a99f9f84143b7faf5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logRelease files / kinextract-2.0.0-py3-none-any.whl
| Download URL | kinextract-2.0.0-py3-none-any.whl |
|---|---|
| Size | 156.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8bb20a7b4a875826ca44b4665bfa35ad79f5fd2c4b5aedc3f728fb371ae2491c
|
|
BLAKE2b-256 checksum How to use checksums |
5e6357cae210df9304ad1e51f482d78738a7b2c55cb5d27832d11155ae946ee9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logSecurity for kinextract 2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for kinextract-2.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.