Source code for bioniumx.spectra.transmission

"""
TransmissionSpectrum — Core data class for exoplanet transmission spectroscopy.
"""
import numpy as np
from bioniumx.core import BioniumXObject

[docs] class TransmissionSpectrum(BioniumXObject): """ Represents a transmission spectrum of an exoplanet atmosphere. Stores wavelength grid, transit depth (Rp/Rs)², and uncertainties, plus all mission/target metadata. Parameters ---------- wavelength : array-like Wavelength array in microns. transit_depth : array-like Transit depth (Rp/Rs)² at each wavelength. Dimensionless. err : array-like, optional 1-sigma uncertainty on transit_depth. If None, assumed uniform 0. target_name : str, optional Exoplanet identifier (e.g., 'K2-18 b'). instrument : str, optional Observing instrument (e.g., 'JWST/NIRSpec', 'Hubble/WFC3'). resolution : float, optional Spectral resolving power R = λ/Δλ. Attributes ---------- wavelength : np.ndarray Wavelength in microns. transit_depth : np.ndarray Observed transit depth spectrum. err : np.ndarray Uncertainty array (same shape as transit_depth). meta : dict Dictionary of all metadata (target_name, instrument, etc.) Examples -------- Load from CSV and compute water detection: >>> from bioniumx.spectra import TransmissionSpectrum >>> from bioniumx.io import load_csv >>> wl, depth, err = load_csv("K2-18b_jwst.csv") >>> spec = TransmissionSpectrum(wl, depth, err=err, target_name="K2-18 b") >>> spec <TransmissionSpectrum | {'wavelength': (256,), 'transit_depth': (256,)}> References ---------- Madhusudhan et al. (2023), ApJL, 956, L18 — K2-18b carbon detection. """ _required_attrs = ["wavelength", "transit_depth"] _metadata_attrs = ["target_name", "instrument", "resolution"]
[docs] def __init__( self, wavelength, transit_depth, err=None, target_name: str = "Unknown", instrument: str = "Unknown", resolution: float = None, gti: list = None, ): self.wavelength = np.asarray(wavelength, dtype=float) self.transit_depth = np.asarray(transit_depth, dtype=float) if err is not None: self.err = np.asarray(err, dtype=float) else: self.err = np.zeros_like(self.transit_depth) self.meta = { "target_name": target_name, "instrument": instrument, "resolution": resolution, } self.gti = gti # Good Wavelength Intervals (analogous to Stingray's GTIs) self._validate()
def apply_wavelength_mask(self, wl_min: float, wl_max: float): """Return a masked copy within [wl_min, wl_max] microns.""" mask = (self.wavelength >= wl_min) & (self.wavelength <= wl_max) return TransmissionSpectrum( wavelength=self.wavelength[mask], transit_depth=self.transit_depth[mask], err=self.err[mask], **self.meta, ) def rebin(self, factor: int): """ Bin down the wavelength grid by averaging ``factor`` adjacent channels. Parameters ---------- factor : int Binning factor. Must divide len(wavelength) evenly. Returns ------- rebinned : TransmissionSpectrum New object with wavelength grid reduced by ``factor``. """ n = len(self.wavelength) // factor * factor wl = self.wavelength[:n].reshape(-1, factor).mean(axis=1) depth = self.transit_depth[:n].reshape(-1, factor).mean(axis=1) # Error propagation: σ_bin = sqrt(sum(σ²)) / factor err = np.sqrt((self.err[:n].reshape(-1, factor) ** 2).sum(axis=1)) / factor return TransmissionSpectrum(wl, depth, err=err, **self.meta) def snr(self) -> np.ndarray: """ Compute signal-to-noise ratio per channel. Returns ------- snr : np.ndarray SNR = transit_depth / err. Returns inf where err == 0. """ with np.errstate(divide="ignore", invalid="ignore"): return np.where(self.err > 0, self.transit_depth / self.err, np.inf) def plot(self, ax=None, **kwargs): """ Quick-look matplotlib plot of the transmission spectrum. Parameters ---------- ax : matplotlib.axes.Axes, optional **kwargs Passed to matplotlib errorbar. Returns ------- ax : matplotlib.axes.Axes """ import matplotlib.pyplot as plt if ax is None: _, ax = plt.subplots(figsize=(10, 4)) ax.errorbar( self.wavelength, self.transit_depth * 1e6, yerr=self.err * 1e6, fmt="o", ms=3, **kwargs ) ax.set_xlabel("Wavelength (μm)") ax.set_ylabel("Transit Depth (ppm)") ax.set_title(self.meta.get("target_name", "")) return ax