CKD Transmission Tutorial: ArtTransPure with OpaCKD =================================================== Hajime Kawahara with Claude Code, July 2 (2025) This tutorial demonstrates how to use the Correlated K-Distribution (CKD) method for atmospheric transmission calculations with ExoJAX. Transmission spectroscopy is a key technique for characterizing exoplanet atmospheres by observing starlight passing through the planetary atmosphere. .. code:: ipython3 # Import required packages import numpy as np import matplotlib.pyplot as plt from jax import config # ExoJAX imports from exojax.test.emulate_mdb import mock_mdbExomol, mock_wavenumber_grid from exojax.opacity import OpaCKD, OpaPremodit from exojax.rt import ArtTransPure from exojax.test.data import get_testdata_filename, TESTDATA_CO_EXOMOL_PREMODIT_TRANSMISSION_REF # Enable 64-bit precision for accurate calculations config.update("jax_enable_x64", True) print("ExoJAX CKD Tutorial: Transmission Spectroscopy") print("=============================================") .. parsed-literal:: ExoJAX CKD Tutorial: Transmission Spectroscopy ============================================= 1. Setup Atmospheric Model and Molecular Database ------------------------------------------------- First, we’ll set up our atmospheric model for transmission spectroscopy calculations. .. code:: ipython3 # Setup wavenumber grid and molecular database nu_grid, wav, res = mock_wavenumber_grid() print(f"Wavenumber grid: {len(nu_grid)} points from {nu_grid[0]:.1f} to {nu_grid[-1]:.1f} cm⁻¹") print(f"Spectral resolution: {res:.1f}") # Create mock H2O molecular database mdb = mock_mdbExomol("H2O") print(f"Molecular database: {mdb.nurange[0]:.1f} - {mdb.nurange[1]:.1f} cm⁻¹") # Setup atmospheric radiative transfer for transmission art = ArtTransPure( pressure_top=1.0e-8, pressure_btm=1.0e2, nlayer=50, # Fewer layers for transmission calculations integration="simpson" # Simpson integration for better accuracy ) print(f"Atmospheric layers: {art.nlayer}") print(f"Pressure range: {art.pressure_top:.1e} - {art.pressure_btm:.1e} bar") print(f"Integration method: {art.integration}") .. parsed-literal:: xsmode = modit xsmode assumes ESLOG in wavenumber space: xsmode=modit Your wavelength grid is in *** ascending *** order The wavenumber grid is in ascending order by definition. Please be careful when you use the wavelength grid. Wavenumber grid: 20000 points from 4329.0 to 4363.0 cm⁻¹ Spectral resolution: 2556525.8 xsmode = modit xsmode assumes ESLOG in wavenumber space: xsmode=modit Your wavelength grid is in *** ascending *** order The wavenumber grid is in ascending order by definition. Please be careful when you use the wavelength grid. radis== 0.15.2 HITRAN exact name= H2(16O) radis engine = vaex .. parsed-literal:: /home/kawahara/exojax/src/exojax/utils/grids.py:85: UserWarning: Both input wavelength and output wavenumber are in ascending order. warnings.warn( /home/kawahara/exojax/src/exojax/utils/grids.py:85: UserWarning: Both input wavelength and output wavenumber are in ascending order. warnings.warn( /home/kawahara/exojax/src/exojax/utils/grids.py:85: UserWarning: Both input wavelength and output wavenumber are in ascending order. warnings.warn( /home/kawahara/exojax/src/exojax/utils/grids.py:85: UserWarning: Both input wavelength and output wavenumber are in ascending order. warnings.warn( /home/kawahara/exojax/src/exojax/database/api.py:134: UserWarning: The current version of radis does not support broadf_download (requires >=0.16). warnings.warn(msg, UserWarning) /home/kawahara/exojax/src/exojax/utils/molname.py:197: FutureWarning: e2s will be replaced to exact_molname_exomol_to_simple_molname. warnings.warn( /home/kawahara/exojax/src/exojax/utils/molname.py:91: FutureWarning: exojax.utils.molname.exact_molname_exomol_to_simple_molname will be replaced to radis.api.exomolapi.exact_molname_exomol_to_simple_molname. warnings.warn( /home/kawahara/exojax/src/exojax/utils/molname.py:91: FutureWarning: exojax.utils.molname.exact_molname_exomol_to_simple_molname will be replaced to radis.api.exomolapi.exact_molname_exomol_to_simple_molname. warnings.warn( .. parsed-literal:: Molecule: H2O Isotopologue: 1H2-16O Background atmosphere: H2 ExoMol database: None Local folder: H2O/1H2-16O/SAMPLE Transition files: => File 1H2-16O__SAMPLE__04300-04400.trans Broadener: H2 Broadening code level: a1 DataFrame (self.df) available. Molecular database: 4329.0 - 4363.0 cm⁻¹ integration: simpson Simpson integration, uses the chord optical depth at the lower boundary and midppoint of the layers. Atmospheric layers: 50 Pressure range: 1.0e-08 - 1.0e+02 bar Integration method: simpson .. parsed-literal:: /home/kawahara/exojax/src/exojax/rt/common.py:40: UserWarning: nu_grid is not given. specify nu_grid when using 'run' warnings.warn( 2. Define Atmospheric and Planetary Parameters ---------------------------------------------- We’ll create atmospheric profiles and define planetary parameters for transmission calculations. .. code:: ipython3 # Create atmospheric profiles Tarr = np.linspace(1000.0, 1500.0, 50) # Temperature profile mmr_arr = np.full(50, 0.1) # Constant H2O mixing ratio mean_molecular_weight = np.full(50, 2.33) # Mean molecular weight (H2-dominated) # Planetary parameters (Jupiter-like) radius_btm = 6.9e9 # Planet radius at bottom of atmosphere (cm) gravity = 2478.57 # Surface gravity (cm/s²) # Plot atmospheric profiles fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 5)) # Temperature profile ax1.semilogy(Tarr, art.pressure) ax1.set_xlabel('Temperature (K)') ax1.set_ylabel('Pressure (bar)') ax1.set_title('Temperature Profile') ax1.grid(True, alpha=0.3) ax1.invert_yaxis() # Mixing ratio profile ax2.semilogy(mmr_arr, art.pressure) ax2.set_xlabel('H₂O Mixing Ratio') ax2.set_ylabel('Pressure (bar)') ax2.set_title('H₂O Mixing Ratio Profile') ax2.grid(True, alpha=0.3) ax2.invert_yaxis() # Mean molecular weight profile ax3.semilogy(mean_molecular_weight, art.pressure) ax3.set_xlabel('Mean Molecular Weight (amu)') ax3.set_ylabel('Pressure (bar)') ax3.set_title('Mean Molecular Weight Profile') ax3.grid(True, alpha=0.3) ax3.invert_yaxis() plt.tight_layout() plt.show() print(f"Temperature range: {np.min(Tarr):.0f} - {np.max(Tarr):.0f} K") print(f"H2O mixing ratio: {mmr_arr[0]:.1f} (constant)") print(f"Mean molecular weight: {mean_molecular_weight[0]:.2f} amu (constant)") print(f"Planet radius: {radius_btm/6.9e9:.1f} R_Jupiter") print(f"Surface gravity: {gravity:.0f} cm/s² ({gravity/2478.57:.1f} × Jupiter)") .. image:: ckd_transpure_files/ckd_transpure_5_0.png .. parsed-literal:: Temperature range: 1000 - 1500 K H2O mixing ratio: 0.1 (constant) Mean molecular weight: 2.33 amu (constant) Planet radius: 1.0 R_Jupiter Surface gravity: 2479 cm/s² (1.0 × Jupiter) 3. Setup Standard Line-by-Line Opacity Calculator ------------------------------------------------- First, we’ll compute the standard high-resolution transmission spectrum using line-by-line calculations. .. code:: ipython3 # Initialize standard opacity calculator (Premodit) base_opa = OpaPremodit(mdb, nu_grid, auto_trange=[800.0, 1600.0]) print(f"Base opacity calculator: {base_opa.__class__.__name__}") # Compute line-by-line cross-sections and transmission spectrum print("\nComputing line-by-line transmission spectrum...") xsmatrix = base_opa.xsmatrix(Tarr, art.pressure) dtau = art.opacity_profile_xs(xsmatrix, mmr_arr, base_opa.mdb.molmass, gravity) transit_lbl = art.run(dtau, Tarr, mean_molecular_weight, radius_btm, gravity) print(f"Line-by-line spectrum computed!") print(f"Transit radius ratio range: [{np.min(transit_lbl):.6f}, {np.max(transit_lbl):.6f}]") print(f"Transit depth variation: {(np.max(transit_lbl) - np.min(transit_lbl))*1e6:.0f} ppm") .. parsed-literal:: OpaPremodit: params automatically set. default elower grid trange (degt) file version: 2 Robust range: 771.9537482657882 - 1647.2060977798953 K OpaPremodit: Tref_broadening is set to 1131.3708498984759 K max value of ngamma_ref_grid : 21.825321843011604 min value of ngamma_ref_grid : 13.242701248020088 ngamma_ref_grid grid : [13.24270058 15.00453705 17.00077107 19.26258809 21.8253231 ] max value of n_Texp_grid : 0.541 min value of n_Texp_grid : 0.216 n_Texp_grid grid : [0.21599999 0.54100007] .. parsed-literal:: uniqidx: 0%| | 0/3 [00:00 Summary ------- This tutorial demonstrated how to use the CKD method with ExoJAX for transmission spectroscopy: Key Steps: ~~~~~~~~~~ 1. **Setup**: Initialize atmospheric model and molecular database for transmission 2. **Profiles**: Define temperature, mixing ratio, and planetary parameters 3. **Line-by-Line**: Compute high-resolution transmission spectrum 4. **CKD Setup**: Initialize CKD calculator and pre-compute tables 5. **CKD Calculation**: Compute band-averaged transmission spectrum using CKD 6. **Validation**: Compare CKD results with line-by-line band averages 7. **Visualization**: Plot comparison and analyze accuracy in ppm