Source code for bioniumx.molecules.catalog

"""
Biosignature molecule catalog and templates.
"""
import numpy as np


BIOSIGNATURE_MOLECULES = {
    "H2O": {"type": "solvent/habitability", "wavelength_range": (0.9, 3.0)},
    "CH4": {"type": "biosignature/methanogenesis", "wavelength_range": (1.6, 3.5)},
    "CO2": {"type": "background/habitability", "wavelength_range": (2.0, 4.5)},
    "O2": {"type": "biosignature/photosynthesis", "wavelength_range": (0.7, 1.3)},
    "O3": {"type": "biosignature/photochemical", "wavelength_range": (9.0, 10.0)},
    "N2O": {"type": "biosignature/denitrification", "wavelength_range": (3.8, 4.6)},
    "NH3": {"type": "biosignature/cold-planets", "wavelength_range": (10.0, 11.0)},
    "PH3": {"type": "biosignature/reducing", "wavelength_range": (4.0, 4.5)},
}


[docs] def get_template(molecule: str, resolving_power: float = 100): """ Get a theoretical transmission/emission template for a molecule. This function uses the RADIS library to query the Harvard HITRAN database in real-time, computing the Voigt-broadened high-resolution cross section at typical exoplanetary conditions (T=1000K). Parameters ---------- molecule : str The chemical formula (e.g., 'H2O', 'CH4', 'CO2'). resolving_power : float, optional The spectral resolving power R = λ/Δλ of the requested template. Default 100. Returns ------- wavelength : np.ndarray Template wavelength grid (microns). depth : np.ndarray Template absorbance cross-section. Raises ------ ValueError If the molecule is not in the catalog. """ if molecule not in BIOSIGNATURE_MOLECULES: raise ValueError(f"Molecule {molecule} not found in catalog.") wmin, wmax = BIOSIGNATURE_MOLECULES[molecule]["wavelength_range"] # Generate a logarithmic wavelength grid based on resolving power n_points = int(resolving_power * np.log(wmax / wmin)) wl_grid = np.geomspace(wmin, wmax, n_points) try: from radis import calc_spectrum except ImportError: raise ImportError( "The 'radis' package is required to fetch real HITRAN physics. " "Install it via `pip install bionium-x[science]` or `pip install radis`." ) # Convert wavelength bounds from microns to wavenumbers (cm^-1) # nu (cm^-1) = 10000 / lambda (um) nu_min = 10000.0 / wmax nu_max = 10000.0 / wmin print(f"RADIS: Fetching high-resolution HITRAN data for {molecule}...") # Calculate the spectrum using Harvard's HITRAN database # Tgas = 1000 K (typical Hot Jupiter/sub-Neptune) s = calc_spectrum( wavenum_min=nu_min, wavenum_max=nu_max, molecule=molecule, isotope="1", pressure=0.1, # bar (typical transit probing pressure) Tgas=1000.0, # K databank="hitran", truncation=5.0, neighbour_lines=5.0, warnings={"AccuracyError": "ignore", "MissingSelfBroadeningWarning": "ignore"} ) # Retrieve the computed spectrum arrays # We want wavelength (nm) and absorbance, then convert nm to um wl_radis, absorbance = s.get("absorbance", wunit="nm") wl_radis = wl_radis / 1000.0 # Interpolate the ultra-high-resolution RADIS spectrum down to our requested resolving power depth_interp = np.interp(wl_grid, wl_radis, absorbance) # Normalize to 0-1 for template cross-correlation if np.max(depth_interp) > 0: depth_interp = depth_interp / np.max(depth_interp) return wl_grid, depth_interp