Note
This page is generated from the Jupyter notebook
notebooks/06_parametric_model.ipynb in the
repository. Download it and run it interactively to experiment.
6. The parametric model
lazuli_exo.simulate(params) builds a spectrum for any transiting planet. The atmosphere comes from one POSEIDON-derived grid of the photospheric log-pressure,
tabulated for two fiducial planets (1 R\(_J\) at g = 10 m s\(^{-2}\); 0.3 R\(_J\) at g = 30 m s\(^{-2}\)) on a (T, [M/H], C/O) grid. For another planet the runtime shifts \(\zeta\) onto its pressure scale along the slant-column axis \(x = \tfrac12\ln[(R_p H)/(R_p H)_0]\), applies a gray deck and a power-law haze analytically per wavelength, and maps to depth with the exact isothermal hydrostatic relation \(R(\lambda) = R_p / (1 - (H/R_p)(\zeta - \tilde\zeta))\), so \(R_p\) is the band-median transit radius. The star is a PHOENIX mini-grid; slicersim computes the noise live. This notebook shows the grid and validates the reconstruction against the 14 committed POSEIDON planets.
[1]:
import json
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
try:
plt.style.use("gks")
except OSError:
pass
import lazuli_exo
from lazuli_exo import tables, presets, binning
from lazuli_exo.atmosphere import transmission_depth, grid_wavelengths
FIGS = Path("figs"); FIGS.mkdir(exist_ok=True)
g = tables.load_zeta_grid()
print("zeta grid:", g["zeta"].shape, g["zeta"].dtype, "| T", g["T_K"].min(), "-", g["T_K"].max(), "K;",
"log_met", g["log_met"].min(), "-", g["log_met"].max(), "; C/O", list(g["c_to_o"]))
for f in g["meta"]["fiducials"]:
print(f"fiducial: R_p = {f['R_p_m']/7.1492e7:.2f} RJ, g = {f['g_ms2']} m/s2, x = {f['x_column']:+.3f}")
print("POSEIDON", g["meta"]["poseidon_version"], "created", g["meta"]["created"],
"| file", round((tables.resolve_tables_dir(None) / tables.ZETA_GRID).stat().st_size / 1e6, 1), "MB")
zeta grid: (16, 9, 4, 2, 16650) float16 | T 300.0 - 3000.0 K; log_met -1.0 - 3.0 ; C/O [np.float64(0.3), np.float64(0.55), np.float64(0.8), np.float64(1.0)]
fiducial: R_p = 1.00 RJ, g = 10.0 m/s2, x = +0.000
fiducial: R_p = 0.30 RJ, g = 30.0 m/s2, x = -1.151
POSEIDON 1.4.0 created 2026-10-04 | file 25.3 MB
The opacity shape, \(\zeta(\lambda)\)
Higher \(\zeta\) means the atmosphere becomes opaque higher up (lower pressure). Water bands and the alkali lines stand out; the Rayleigh slope rises to the blue. Shown binned to R = 300 for legibility (the grid itself is R = 10,000).
[2]:
wl = g["wavelength_um"]
edges300 = binning.constant_R_edges(0.35, 1.849, 300); wc300 = binning.bin_centers(edges300)
def b300(z): return binning.bin_model(wl, z.astype(float), edges300)
iT = {T: int(np.argmin(abs(g["T_K"] - T))) for T in (600, 1200, 2000)}
im0 = int(np.argmin(abs(g["log_met"] - 0.0))); ico = int(np.argmin(abs(g["c_to_o"] - 0.55)))
fig, ax = plt.subplots(1, 2, figsize=(11, 3.8), sharey=True)
for T, i in iT.items():
ax[0].plot(wc300, b300(g["zeta"][i, im0, ico, 0]), lw=0.9, label=f"T = {g['T_K'][i]:.0f} K")
for met in (0.0, 1.0, 2.0):
im = int(np.argmin(abs(g["log_met"] - met)))
ax[1].plot(wc300, b300(g["zeta"][iT[1200], im, ico, 0]), lw=0.9, label=f"[M/H] = {met:+.0f}")
for a in ax:
a.set_xlabel("wavelength [µm]"); a.legend(frameon=False)
ax[0].set_ylabel(r"$\zeta = -\ln P_{\tau=1}$ [bar]"); ax[0].set_title("solar, C/O = 0.55, 1 R$_J$ node")
ax[1].set_title("1200 K, C/O = 0.55")
fig.tight_layout(); fig.savefig(FIGS / "06_zeta_grid.png", dpi=150)
Mean molecular weight from chemistry
mu=None takes the equilibrium-chemistry value stored with the grid. It rises steeply above 100x solar, which is what shrinks the scale height of a high-metallicity atmosphere.
[3]:
fig, ax = plt.subplots(figsize=(5.5, 3.5))
for T in (400, 1000, 2000):
i = int(np.argmin(abs(g["T_K"] - T)))
ax.plot(g["log_met"], g["mu"][i, :, ico], "o-", ms=3, label=f"T = {g['T_K'][i]:.0f} K")
ax.set_xlabel("[M/H] (log)"); ax.set_ylabel(r"$\mu$ [amu]"); ax.legend(frameon=False)
fig.tight_layout(); fig.savefig(FIGS / "06_mu.png", dpi=150)
print({float(m): round(float(g["mu"][iT[1200], k, ico]), 2) for k, m in enumerate(g["log_met"])})
{-1.0: 2.31, -0.5: 2.31, 0.0: 2.32, 0.5: 2.36, 1.0: 2.47, 1.5: 2.82, 2.0: 3.84, 2.5: 6.28, 3.0: 10.64}
Reconstruction vs. the POSEIDON tables: WASP-39 b
The committed table and the parametric reconstruction from the registry values, binned to R = 100. Two conventions differ and are handled here as in the validation script: the tables put the archive radius at 10 bar, so the model is given each table’s own band-median radius; and POSEIDON realizes a nominal 1 mbar deck at 1.376 mbar on its 100-layer grid.
[4]:
import sys; sys.path.insert(0, str(Path("..") / "precompute"))
from validate_zeta_grid import DECK_REALIZED_LOG_OFFSET, R_J_M, R_SUN_M
tdir = tables.resolve_tables_dir(None)
edges = binning.constant_R_edges(0.40, 1.70, 100); wc = binning.bin_centers(edges)
fig, axes = plt.subplots(2, 2, figsize=(11, 6.5), sharex=True)
for ax, scen in zip(axes.ravel(), ["clear", "cloudy", "high_metallicity", "hazy"]):
wl_t, d_t, _ = tables.load_model(Path("..") / "precompute" / "tables", "WASP-39 b", scen)
p = presets.showcase("WASP-39 b", scen)
kw = {k: p[k] for k in ('T_eq_K', 'R_p_RJ', 'M_p_MJ', 'R_s_RSun', 'log_met', 'c_to_o', 'mu', 'log_P_cloud_bar', 'log_a_haze', 'haze_slope')}
band = (wl_t >= 0.4) & (wl_t <= 1.7)
kw["R_p_RJ"] = float(np.sqrt(np.median(d_t[band])) * p["R_s_RSun"] * R_SUN_M / R_J_M)
if kw["log_P_cloud_bar"] is not None:
kw["log_P_cloud_bar"] += DECK_REALIZED_LOG_OFFSET
d_m, info = transmission_depth(wl_t, **kw)
bt = binning.bin_model(wl_t, d_t, edges); bm = binning.bin_model(wl_t, d_m, edges)
ax.plot(wc, bt * 1e6, lw=1.2, label="POSEIDON table")
ax.plot(wc, bm * 1e6, lw=1.2, ls="--", label="parametric")
ax.set_title(f"{scen}: amplitude ratio {np.ptp(bm)/np.ptp(bt):.2f}, offset {np.mean(bm-bt)*1e6:+.0f} ppm")
ax.legend(frameon=False, fontsize=8)
for ax in axes[1]: ax.set_xlabel("wavelength [µm]")
for ax in axes[:, 0]: ax.set_ylabel("depth [ppm]")
fig.tight_layout(); fig.savefig(FIGS / "06_wasp39_validation.png", dpi=150)
Validation table (all 14 planets)
Produced by precompute/validate_zeta_grid.py (issue #22 gate). Each model-vs-table comparison is made at R = 100 after removing a constant offset, against the single-transit per-bin sigma, and must pass three gates: G2, the worst binned residual is below one sigma; G5, the residual spectrum’s own template S/N (how much structure the error has) is below 3; G3, the model’s template S/N is within 10 % of the table’s. flat is gated on its absolute amplitude instead. For the three planets
whose POSEIDON tables were computed with the isotherm clamped to 300 K, the like-for-like row (T_eq = 300 K in the parametric model too) is the gated one. Expected failures are waivers in the script, each naming the gate and the issue that removes it (#35).
[5]:
import pandas as pd
rows = json.loads(Path("figs/06_validation.json").read_text())["models"]
df = pd.DataFrame(rows)[["planet", "scenario", "T_eq_used_K", "like_for_like", "gated", "H_over_Rp",
"amp_table_ppm", "amp_model_ppm", "amp_ratio", "rms_ppm", "worst_sig", "err_snr", "snr_ratio", "failed", "waived"]]
df["waived"] = df["waived"].notna()
pd.set_option("display.max_rows", 100); pd.set_option("display.width", 200)
print(df.round({"H_over_Rp": 4, "amp_table_ppm": 0, "amp_model_ppm": 0, "amp_ratio": 2, "rms_ppm": 0, "worst_sig": 2, "err_snr": 1, "snr_ratio": 3}).to_string(index=False))
gated = df[df.gated]
passed = gated.failed.str.len() == 0
print(f"\n{int(passed.sum())}/{len(gated)} gated comparisons pass all gates; "
f"waived: {gated[~passed & gated.waived][['planet', 'scenario']].values.tolist()}")
for r in rows:
if r["gated"] and r["waived"] and r["failed"]:
print("WAIVED", r["planet"], r["scenario"], r["failed"], "->", r["waived"])
assert all(not r["unwaived"] for r in rows if r["gated"]), "validation gate failed"
fig, ax = plt.subplots(figsize=(6, 3.5))
for scen, m in zip(["clear", "cloudy", "high_metallicity", "hazy"], "osd^"):
s = gated[gated.scenario == scen]
ax.plot(s.amp_table_ppm, s.amp_ratio, m, ms=5, label=scen)
ax.axhline(1, color="0.6", lw=0.8); ax.set_xscale("log")
ax.set_xlabel("POSEIDON feature amplitude [ppm]"); ax.set_ylabel("parametric / POSEIDON")
ax.legend(frameon=False, fontsize=8); fig.tight_layout(); fig.savefig(FIGS / "06_validation_ratios.png", dpi=150)
planet scenario T_eq_used_K like_for_like gated H_over_Rp amp_table_ppm amp_model_ppm amp_ratio rms_ppm worst_sig err_snr snr_ratio failed waived
GJ 1214 b clear 567.000 False True 0.0111 1797.0 1726.0 0.96 81.0 0.92 4.0 0.961 [G5] True
GJ 1214 b high_metallicity 567.000 False True 0.0067 1632.0 1533.0 0.94 56.0 0.28 3.6 0.919 [G5] True
GJ 1214 b cloudy 567.000 False True 0.0116 321.0 270.0 0.84 6.0 -0.05 0.2 0.978 [] False
GJ 1214 b hazy 567.000 False True 0.0113 2063.0 2059.0 1.00 18.0 -0.08 1.2 0.975 [] False
GJ 1214 b flat 567.000 False True 0.0105 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
GJ 3470 b clear 593.500 False True 0.0119 1009.0 982.0 0.97 11.0 0.34 1.5 0.980 [] False
GJ 3470 b high_metallicity 593.500 False True 0.0072 713.0 700.0 0.98 12.0 0.22 2.1 0.946 [] False
GJ 3470 b cloudy 593.500 False True 0.0123 259.0 246.0 0.95 2.0 -0.06 0.2 0.981 [] False
GJ 3470 b hazy 593.500 False True 0.0121 1021.0 1019.0 1.00 7.0 -0.11 1.1 0.982 [] False
GJ 3470 b flat 593.500 False True 0.0111 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
HAT-P-11 b clear 838.000 False True 0.0087 424.0 420.0 0.99 2.0 -0.05 0.4 1.002 [] False
HAT-P-11 b high_metallicity 838.000 False True 0.0057 339.0 335.0 0.99 2.0 0.09 0.5 0.988 [] False
HAT-P-11 b cloudy 838.000 False True 0.0090 88.0 88.0 1.00 0.0 -0.02 0.1 0.990 [] False
HAT-P-11 b hazy 838.000 False True 0.0088 398.0 396.0 1.00 1.0 -0.06 0.3 0.994 [] False
HAT-P-11 b flat 838.000 False True 0.0082 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
HD 189733 b clear 1209.000 False True 0.0025 773.0 766.0 0.99 2.0 -0.17 1.0 0.991 [] False
HD 189733 b high_metallicity 1209.000 False True 0.0015 556.0 551.0 0.99 1.0 0.06 0.4 0.995 [] False
HD 189733 b cloudy 1209.000 False True 0.0025 142.0 145.0 1.02 1.0 0.08 0.3 1.020 [] False
HD 189733 b hazy 1209.000 False True 0.0025 685.0 684.0 1.00 2.0 -0.26 0.9 0.994 [] False
HD 189733 b flat 1209.000 False True 0.0024 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
HD 209458 b clear 1459.000 False True 0.0058 1074.0 1063.0 0.99 3.0 -0.37 1.5 0.993 [] False
HD 209458 b high_metallicity 1459.000 False True 0.0035 834.0 829.0 0.99 1.0 0.09 0.7 0.995 [] False
HD 209458 b cloudy 1459.000 False True 0.0059 262.0 264.0 1.01 1.0 0.29 0.8 1.020 [] False
HD 209458 b hazy 1459.000 False True 0.0058 1005.0 1003.0 1.00 2.0 -0.46 1.3 0.996 [] False
HD 209458 b flat 1459.000 False True 0.0056 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
K2-18 b clear 284.000 False False 0.0044 135.0 127.0 0.94 2.0 -0.09 0.4 0.938 [] False
K2-18 b clear 300.001 True True 0.0046 135.0 134.0 0.99 0.0 -0.01 0.0 0.992 [] False
K2-18 b high_metallicity 284.000 False False 0.0025 126.0 117.0 0.93 3.0 -0.10 0.4 0.924 [] False
K2-18 b high_metallicity 300.001 True True 0.0026 126.0 123.0 0.98 1.0 -0.04 0.1 0.977 [] False
K2-18 b cloudy 284.000 False False 0.0045 14.0 13.0 0.94 0.0 -0.01 0.0 0.940 [] False
K2-18 b cloudy 300.001 True True 0.0047 14.0 13.0 0.98 0.0 -0.00 0.0 0.978 [] False
K2-18 b hazy 284.000 False False 0.0044 152.0 144.0 0.95 2.0 -0.00 0.3 0.930 [] False
K2-18 b hazy 300.001 True True 0.0047 152.0 152.0 1.01 1.0 -0.03 0.2 0.982 [] False
K2-18 b flat 284.000 False False 0.0044 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
K2-18 b flat 284.000 True True 0.0044 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
LHS 1140 b clear 226.000 False False 0.0041 309.0 229.0 0.74 21.0 -0.80 3.2 0.742 [G5, G3] False
LHS 1140 b clear 300.001 True True 0.0054 309.0 306.0 0.99 1.0 -0.03 0.1 0.992 [] False
LHS 1140 b high_metallicity 226.000 False False 0.0023 285.0 210.0 0.73 22.0 -0.74 3.1 0.733 [G5, G3] False
LHS 1140 b high_metallicity 300.001 True True 0.0031 285.0 280.0 0.98 1.0 -0.07 0.2 0.977 [] False
LHS 1140 b cloudy 226.000 False False 0.0042 28.0 22.0 0.79 1.0 -0.08 0.2 0.795 [G3] False
LHS 1140 b cloudy 300.001 True True 0.0055 28.0 27.0 0.98 0.0 -0.01 0.0 0.978 [] False
LHS 1140 b hazy 226.000 False False 0.0041 344.0 260.0 0.76 22.0 -0.01 2.4 0.737 [G3] False
LHS 1140 b hazy 300.001 True True 0.0055 344.0 344.0 1.00 2.0 -0.07 0.4 0.979 [] False
LHS 1140 b flat 226.000 False False 0.0040 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
LHS 1140 b flat 226.000 True True 0.0040 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
TOI-270 d clear 383.000 False True 0.0109 304.0 301.0 0.99 1.0 -0.04 0.1 0.991 [] False
TOI-270 d high_metallicity 383.000 False True 0.0065 286.0 284.0 0.99 2.0 -0.08 0.3 0.977 [] False
TOI-270 d cloudy 383.000 False True 0.0114 40.0 38.0 0.96 0.0 -0.03 0.1 0.951 [] False
TOI-270 d hazy 383.000 False True 0.0112 335.0 336.0 1.00 2.0 -0.08 0.4 0.981 [] False
TOI-270 d flat 383.000 False True 0.0106 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
TRAPPIST-1 e clear 249.700 False False 0.0213 1828.0 1453.0 0.79 96.0 -1.21 4.7 0.791 [G2, G5, G3] False
TRAPPIST-1 e clear 300.001 True True 0.0255 1828.0 1794.0 0.98 9.0 -0.11 0.5 0.977 [] False
TRAPPIST-1 e high_metallicity 249.700 False False 0.0118 1546.0 1225.0 0.79 88.0 -1.02 4.0 0.781 [G2, G5, G3] False
TRAPPIST-1 e high_metallicity 300.001 True True 0.0142 1546.0 1499.0 0.97 11.0 -0.18 0.6 0.961 [] False
TRAPPIST-1 e cloudy 249.700 False False 0.0237 274.0 211.0 0.77 15.0 -0.23 0.8 0.775 [G3] False
TRAPPIST-1 e cloudy 300.001 True True 0.0284 274.0 250.0 0.91 7.0 -0.12 0.4 0.904 [] False
TRAPPIST-1 e hazy 249.700 False False 0.0225 2295.0 1863.0 0.81 110.0 -0.00 3.5 0.801 [G5, G3] False
TRAPPIST-1 e hazy 300.001 True True 0.0270 2295.0 2270.0 0.99 14.0 -0.14 0.6 0.967 [] False
TRAPPIST-1 e flat 249.700 False False 0.0189 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
TRAPPIST-1 e flat 249.700 True True 0.0189 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
WASP-107 b clear 736.000 False True 0.0160 5721.0 5629.0 0.98 58.0 2.99 12.4 1.034 [G2, G5] True
WASP-107 b high_metallicity 736.000 False True 0.0102 4491.0 4281.0 0.95 51.0 -1.13 7.0 0.961 [G2, G5] True
WASP-107 b cloudy 736.000 False True 0.0166 2112.0 2035.0 0.96 43.0 2.69 9.3 1.064 [G2, G5] True
WASP-107 b hazy 736.000 False True 0.0164 5772.0 5740.0 0.99 51.0 2.62 10.7 1.013 [G2, G5] True
WASP-107 b flat 736.000 False True 0.0141 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
WASP-12 b clear 2601.000 False True 0.0073 897.0 893.0 1.00 7.0 0.08 0.8 1.001 [] False
WASP-12 b high_metallicity 2601.000 False True 0.0050 911.0 853.0 0.94 13.0 -0.52 1.7 0.938 [] False
WASP-12 b cloudy 2601.000 False True 0.0075 73.0 89.0 1.21 7.0 0.17 0.9 1.333 [G3] True
WASP-12 b hazy 2601.000 False True 0.0074 1076.0 1069.0 0.99 5.0 0.13 0.7 0.989 [] False
WASP-12 b flat 2601.000 False True 0.0068 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
WASP-121 b clear 2409.000 False True 0.0075 1144.0 1132.0 0.99 8.0 0.18 1.2 0.986 [] False
WASP-121 b high_metallicity 2409.000 False True 0.0048 1032.0 963.0 0.93 14.0 -0.84 2.6 0.946 [] False
WASP-121 b cloudy 2409.000 False True 0.0077 203.0 216.0 1.06 5.0 0.17 1.0 1.080 [] False
WASP-121 b hazy 2409.000 False True 0.0076 1162.0 1164.0 1.00 5.0 -0.18 1.0 0.991 [] False
WASP-121 b flat 2409.000 False True 0.0072 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
WASP-39 b clear 1166.000 False True 0.0114 3451.0 3397.0 0.98 15.0 0.38 1.7 0.984 [] False
WASP-39 b high_metallicity 1166.000 False True 0.0069 2384.0 2393.0 1.00 5.0 0.11 0.6 1.003 [] False
WASP-39 b cloudy 1166.000 False True 0.0118 1039.0 1023.0 0.98 5.0 -0.10 0.5 0.986 [] False
WASP-39 b hazy 1166.000 False True 0.0116 3198.0 3194.0 1.00 11.0 -0.29 1.4 0.992 [] False
WASP-39 b flat 1166.000 False True 0.0111 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
WASP-96 b clear 1285.000 False True 0.0067 1282.0 1264.0 0.99 4.0 0.11 0.4 0.987 [] False
WASP-96 b high_metallicity 1285.000 False True 0.0040 912.0 905.0 0.99 2.0 0.02 0.1 0.998 [] False
WASP-96 b cloudy 1285.000 False True 0.0068 311.0 312.0 1.00 1.0 0.03 0.1 1.008 [] False
WASP-96 b hazy 1285.000 False True 0.0068 1167.0 1166.0 1.00 4.0 0.09 0.3 0.994 [] False
WASP-96 b flat 1285.000 False True 0.0066 0.0 0.0 NaN 0.0 0.00 0.0 NaN [] False
63/70 gated comparisons pass all gates; waived: [['GJ 1214 b', 'clear'], ['GJ 1214 b', 'high_metallicity'], ['WASP-107 b', 'clear'], ['WASP-107 b', 'high_metallicity'], ['WASP-107 b', 'cloudy'], ['WASP-107 b', 'hazy'], ['WASP-12 b', 'cloudy']]
WAIVED GJ 1214 b clear ['G5'] -> pending #35 (T nodes across the alkali transition)
WAIVED GJ 1214 b high_metallicity ['G5'] -> pending #35 (T nodes across the alkali transition)
WAIVED WASP-107 b clear ['G2', 'G5'] -> pending #35 (x extrapolation)
WAIVED WASP-107 b high_metallicity ['G2', 'G5'] -> pending #35 (x extrapolation)
WAIVED WASP-107 b cloudy ['G2', 'G5'] -> pending #35 (x extrapolation)
WAIVED WASP-107 b hazy ['G2', 'G5'] -> pending #35 (x extrapolation)
WAIVED WASP-12 b cloudy ['G3'] -> 73 ppm feature, 19 ppm absolute error (noise floor 178 ppm/transit): POSEIDON's discretized deck sits between the literal 1 mbar and the 1.376 mbar realized at 300 K; at 2600 K the x-extrapolation is also +0.09 high. Clear spectrum matches POSEIDON to 1.00.
A custom planet end to end
TOI-270 d with a heavier atmosphere (μ = 5), a cloud deck at 10 mbar, 20 transits. simulate needs slicersim and sncosmo at call time; the noise comes out of a live slicersim run on the PHOENIX SED, with the archive J (Vega) converted to AB.
[6]:
p = presets.showcase("TOI-270 d", "clear")
p.update(name="TOI-270 d (custom)", scenario="custom", mu=5.0, log_P_cloud_bar=-2.0)
obs = lazuli_exo.simulate(p, R=60, n_transits=20)
a, n = obs.meta["atmosphere"], obs.meta["noise"]
print(f"g = {a['g_ms2']:.1f} m/s2, H = {a['H_m']/1e3:.0f} km, H/Rp = {a['H_over_Rp']:.4f}, "
f"mu = {a['mu_used']} ({a['mu_source']}), T_clamped = {a['T_clamped']}")
print(f"J = {n['mag_input']} Vega -> {n['mag_ab']:.2f} AB (offset {n['vega_ab_offset']:.3f}); "
f"nmd {n['nmd_used']}, saturated px {n['nsaturated_detpx']}, slicersim {n['slicersim_version']}")
print("feature amplitude %.0f ppm, median error %.0f ppm" % (a["feature_amplitude_ppm"], np.median(obs.depth_err) * 1e6))
print(obs.detect())
fig, ax = plt.subplots(figsize=(8, 3.8))
obs.plot(ax=ax, seed=1)
fig.tight_layout(); fig.savefig(FIGS / "06_custom_toi270d.png", dpi=150)
g = 9.9 m/s2, H = 64 km, H/Rp = 0.0049, mu = 5.0 (user), T_clamped = False
J = 9.099 Vega -> 10.00 AB (offset 0.899); nmd [2, 2, 0], saturated px 0, slicersim 1.3.4
feature amplitude 195 ppm, median error 16 ppm
{'template_snr_diagonal': 5.596534132052765, 'band_snr': {'Na': 0.36388598240478454, 'K': 0.7947699031560366, 'H2O': 4.416474956416279, 'CH4': 2.3193116746856535, 'CO2': 1.2551630076562657}}
Summary
One ζ grid (16 × 9 × 4 × 2 nodes at R = 10,000, ≈25 MB) plus analytic column, scale-height, deck and haze terms reproduces the per-planet POSEIDON tables to within the stated tolerances.
R_pis the band-median transit radius; the tables’ 10 bar reference-pressure convention only shows up as a constant offset.Noise is live slicersim with the Vega→AB conversion applied and reported.