Changelog

The project is pre-release; there has been no tagged version yet, so the entries below group the development history by theme rather than by release.

0.1.2 (2026-10-12)

First PyPI release (pip install lazuli-exo). The v0.1.1 tag was never published; everything below the 0.1.1 heading is the state of that tag, and everything in this section landed since.

  • Docs aligned with wcc-etc: a Plotting page whose figures are drawn at build time, docs/MODEL.md included in the reference as lazuli-exo: model, tables and caveats, a conda section on the installation page, and a one-page primer plus glossary at the top of Concepts and known approximations for readers new to transmission spectroscopy. Prose pass over every page.

  • Release decisions (#31): HD 189733 b stays in the tables with its saturation warning; the internal design notes under docs/superpowers/ are no longer tracked in the public tree.

  • Release scrub (#31): numpy>=2.0 is declared (the runtime uses np.trapezoid); the test workflow runs on Python 3.10-3.13; run.sh binds to 127.0.0.1 without --debug; README and Installation document pip install lazuli-exo and the extras.

  • Plotting as methods on the result objects: obs.plot() gained show_points, units (“percent”, “ppm”, “fraction”), label and title so scenarios overlay on one Axes; new obs.plot_noise() (per-bin error against wavelength) and LightCurveSet.plot() (one curve per band, stacked). plotting.wavelength_axis is public. The plotting functions no longer apply a matplotlib style globally.

  • Docs refreshed for the parametric runtime: meta["model"] references (gone since #19) now point at meta["params"] / meta["atmosphere"]; scenarios are described as presets; the binning page describes the LSF and photon-weighted instrument response; the two resolution limits, the single remaining saturated host and the validation gates are current. Tutorial notebooks 01-05 are rebuilt from notebooks/build_0N.py scripts like 06-09, and 06 is regenerated from its builder.

Physics review of 2026-10-10 (tracked in GitHub issue #31, milestone “Quantitative atmospheres”):

  • Stellar contamination (transit light source effect, Rackham et al. 2018) as f_spot/T_spot_K/f_fac/T_fac_K keywords of simulate and observe; star.contamination and star.TLSE_PRESETS. Default off (#42).

  • Light curves use the exact Keplerian sky-projected separation (orbit.projected_separation) and stretch the orbit clock so the first-to-fourth-contact duration equals the registry \(t_{14}\); removes a 1σ depth bias for WASP-12 b (#43).

  • PHOENIX grid rebuilt from the solar-metallicity folder only (the non-solar pysynphot folders are R ≈ 740 and unreliable), log g to 5.5 and T_eff to 12000 K; the blackbody fallback is gone and every out-of-range input clamps to the nearest node with info["clamped"]. The live (simulate) and table (observe) noise paths now agree to ≈ 3 % instead of 0.5–1.6× (#44, part of #34).

  • .readthedocs.yaml so the docs build on Read the Docs.

  • Release plumbing (#31 section 1): publish.yml builds sdist + wheel from a clean checkout, checks the wheel ships exactly the tracked tables, and uploads with PyPI trusted publishing on a v* tag (manual dry run to TestPyPI); the same wheel check runs in CI. lazuli_exo.__version__ is the single version source; pyproject.toml gains readme, authors, urls and classifiers. observe/simulate reject non-positive or non-integer R and n_transits (R=0 was a ZeroDivisionError, n_transits=2.5 was applied but reported as 2).

  • Noise tables regenerated (2026-10-11) with saturation tested at the last read, the pixel-averaged PHOENIX SED, the reset-frame overhead and the narrow field pinned: single-transit errors 2-30 % larger at the median; HD 209458 b and HAT-P-11 b now desaturate fully. Tables carry package version, git commit and input hashes; index.json schema 4 adds a per-file sha256 manifest (#4, #33, #34, #37).

  • simulate(star, spectrum=(wl_um, depth)) accepts a transit spectrum from any forward model in place of the parametric atmosphere; the noise, contamination and instrument response are unchanged (meta["atmosphere"]["source"] == "user"). README and index now state the scope: quantitative instrument half, surrogate atmosphere half.

  • Live noise (slicer.depth_error, hence simulate) pins the narrow spectrograph field by default. slicersim >= 1.3.3 no longer rescales a new LazuliTarget to a field, which halved the throughput and left simulate 1.4x noisier than observe’s tables; the tables themselves are unchanged and correct.

0.1.1 (2026-10-10)

  • Absorbed the standalone lazuli-transit package as lazuli_exo.transit: FluxModel, TransitModel (jaxoplanet), the NASA Exoplanet Archive cache (download_exoplanet_archive / load_exoplanet_archive) and the matplotlib helpers. The GPL-backed MoonTransitModel was not moved; it stays in the private lazuli-transit repo so this package remains BSD-clean. New extras transit, archive; the subpackage itself still imports with numpy only. The archive cache moved from ~/.lazuli_transit/ to ~/.lazuli_exo/. wcc_etc depends on lazuli-exo[transit] instead of vendoring a copy.

Unreleased (0.1.0 development)

2026-10 (parametric model)

  • lazuli_exo.simulate(params): any transiting planet from a shared POSEIDON-derived \(\zeta\) grid (16 T × 9 [M/H] × 4 C/O × 2 fiducials, native R = 10,000, float16) plus analytic scale height, slant column, haze and an exact Ein cloud-deck operator; exact hydrostatic radius mapping. Validated against the 14 × 5 showcase tables (one documented waiver).

  • PHOENIX stellar SED mini-grid (2000–8000 K) and star.phoenix_sed.

  • slicer.depth_error: live slicersim noise with the Vega→AB conversion through sncosmo, the readout desaturation loop, provenance in info. The committed noise tables were regenerated with this conversion (1.4–1.5× more conservative for unsaturated hosts).

  • presets.showcase, orbit.transit_duration_s, forms and report (the web contract shared with the Lazuli simulators site).

  • Flask applet: Custom-planet mode over /api/simulate.

  • Tables moved into the package (src/lazuli_exo/data/tables) as package data; wheels and VCS pins ship them. Schema version 2.

  • Package renamed lazuli-exo (import lazuli_exo); repository at https://github.com/schmidt-observatories/lazuli-exo (moved from gummiks in 2026-10).

  • Tutorials 06–09 and this documentation update.

Documentation

  • Sphinx documentation site under docs/sphinx/ — user guide, quick start, auto-generated API reference, and the tutorial notebooks rendered via nbsphinx.

  • Five tutorial notebooks in notebooks/ covering the runtime end to end.

  • Verification notebook (notebooks/verification.ipynb) asserting physical sanity checks against the committed tables.

Light curves

  • Quadratically limb-darkened transits with geometry from the frozen NASA Exoplanet Archive registry — impact parameter from \(a/R_\star\) and \(\cos i\) with an eccentricity factor, and \(z(t)\) along a local circular arc at the Keplerian conjunction sky speed.

  • Limb-darkening tables per planet from exotic-ld (PHOENIX grid, 48 wavelength chunks); trapezoid fallback recorded in meta["shape"].

  • White-light and wavelength-banded light curves, with per-point noise derived from the band depth precision.

Web demo

  • Local single-page Flask + Plotly UI over the runtime (webapp/), with system-parameters and diagnostics panels.

  • observe() validation errors surface as a JSON 400 rather than an HTML 500.

Runtime

  • observe() assembling binned spectra with \(1/\sqrt{N}\) noise scaling.

  • Constant-R binning with inverse-variance noise combination.

  • Spectral-template S/N against a flat baseline, with per-band masks.

  • Spectrum plotting with simulated error bars.

  • Frozen showcase-target registry (14 planets), generated from NEA PSCompPars.

  • Package scaffold and versioned lookup-table I/O; table loads validate against the index unconditionally.

Precompute

  • POSEIDON model tables and PHOENIX stellar spectra for all targets, plus analytic flat baselines.

  • slicersim single-transit noise tables, with saturation-free per-target readout tuning and the residual saturation recorded in meta for the three bright hosts where it could not be eliminated.

  • Scenario definitions pinned in one place, including the deck_haze workaround for POSEIDON’s silent cloud_type="haze" no-op.

Renamed

  • The package carried two earlier working names before settling on lazuli-exo on 2026-10-05.