How ExoJAX Opacity Calculators Handle Voigt Profiles, CIA, Mie, and Rayleigh Scattering
ExoJAX implements a modular opacity framework where Voigt profiles, Collision-Induced Absorption (CIA), Rayleigh scattering, and Mie scattering each inherit from specialized base classes (OpaCalc for lines, OpaCont for continua) to enable differentiable, GPU-accelerated radiative transfer calculations.
ExoJAX is a JAX-based radiative transfer code designed for exoplanet atmospheric retrievals. Its opacity calculators (opa) provide a unified interface for computing wavelength-dependent absorption and scattering cross-sections. The architecture separates line-by-line opacity—handled by OpaDirect, OpaModit, and OpaPremodit—from continuum processes managed via OpaCont subclasses, ensuring that Voigt broadening, CIA, and scattering physics can be combined seamlessly within automatic differentiation pipelines.
Voigt Profile Implementation in ExoJAX Opacity Calculators
Line opacity in ExoJAX relies on the Voigt profile—the convolution of Doppler (Gaussian) and Lorentzian (pressure) broadening. The implementation spans low-level kernel functions and high-level calculator classes.
The Folded Voigt Kernel (ditkernel.py)
The core mathematical operation resides in src/exojax/opacity/_common/ditkernel.py. The function fold_voigt_kernel_logst (and its linear-grid variant fold_voigt_kernel) pre-computes the convolution of Gaussian and Lorentzian kernels on a uniform wavenumber grid. This "folded" approach allows the DIT (Direct Integral Transform) algorithm to evaluate millions of lines simultaneously via JAX’s vectorized operations.
Line-by-Line Calculators (OpaDirect, OpaModit, OpaPremodit)
ExoJAX provides three line-by-line opacity calculators that wrap the Voigt kernel:
OpaDirect(src/exojax/opacity/lpf/api.py): Calls the vectorizedvoigtroutine fromsrc/exojax/opacity/lpf/lpf.pydirectly. Suitable for small line lists or when memory is constrained.OpaModit(src/exojax/opacity/modit/api.py): Uses the Modit algorithm to pre-compute transform matrices for the Voigt profile, accelerating repeated calculations.OpaPremodit(src/exojax/opacity/premodit/api.py): A pre-computed Modit variant that caches the DIT kernel for maximum GPU efficiency during retrievals.
All three classes inherit from OpaCalc (src/exojax/opacity/base.py) and expose the xsmatrix method, which returns the cross-section matrix for a given temperature, pressure, and mixing ratio.
Continuum Opacity Handling: CIA, Rayleigh, and Mie Scattering
Continuum processes—Collision-Induced Absorption (CIA), Rayleigh scattering, and Mie scattering—are implemented as subclasses of OpaCont in src/exojax/opacity/opacont.py. This design ensures continuum opacities share a uniform API with line opacities while implementing distinct physics.
Collision-Induced Absorption (OpaCIA)
The OpaCIA class handles CIA, where transient dipoles form during molecular collisions (e.g., H₂–H₂, H₂–He). The implementation:
- Reads tabulated CIA coefficients
σ(ν,T)from the ExoMol-CIA database. - Interpolates the coefficients to the model temperature using
jnp.interp. - Multiplies by the product of the colliding species densities (
n_i × n_j) to yield the absorption cross-section.
This occurs in src/exojax/opacity/opacont.py, where OpaCIA implements the xsmatrix method consistent with line calculators.
Rayleigh Scattering (OpaRayleigh)
OpaRayleigh computes the wavelength-dependent cross-section for Rayleigh scattering, which follows the analytic λ⁻⁴ dependence. The class:
- Retrieves the molecule’s polarizability and refractive index (e.g., for H₂, He).
- Evaluates the
rayleigh_cross_sectionroutine inopacont.py, implementing the standard formula where cross-section scales withλ⁻⁴. - Returns the cross-section matrix via
xsmatrix, scaled by the species number density.
Mie Scattering (OpaMie)
For aerosol and cloud opacity, OpaMie handles Mie scattering from spherical particles. The implementation:
- Loads pre-computed Mie tables (size distribution, complex refractive index) for compositions like MgSiO₃ or TiO₂.
- Interpolates the extinction efficiency, scattering efficiency, and asymmetry parameters onto the model’s wavenumber grid based on the specified particle size distribution.
- Scales the opacity by the particle number density to yield the final Mie cross-sections.
The OpaMie class resides in src/exojax/opacity/opacont.py and, like other continuum classes, provides the xsmatrix interface for seamless integration with radiative transfer solvers.
Practical Example: Combining Opacity Components
The following example demonstrates how to instantiate and combine ExoJAX opacity calculators for a complete atmospheric model:
import jax.numpy as jnp
from exojax.opacity import OpaPremodit, OpaCIA, OpaRayleigh, OpaMie
from exojax.utils import molname
# 1. Line opacity: Voigt-profiled molecular lines (e.g., H2O from ExoMol)
mdb = molname.read_exomol("H2O") # Returns MdbExomol object
opa_line = OpaPremodit(mdb) # Fast pre-computed Modit for Voigt profiles
# 2. Continuum opacities
opa_cia = OpaCIA("H2-H2") # Collision-Induced Absorption
opa_ray = OpaRayleigh("H2") # Rayleigh scattering
opa_mie = OpaMie("MgSiO3", particle_radius=0.1) # Mie scattering (particle radius in μm)
# 3. Define atmospheric state
pressure = jnp.logspace(5, 2, 50) # Pressure grid (Pa)
temperature = 1500.0 * jnp.ones_like(pressure)
vmr = 0.01 * jnp.ones_like(pressure) # Volume mixing ratio of H2O
# 4. Compute cross-sections
xs_line = opa_line.xsmatrix(temperature, pressure, vmr)
xs_cia = opa_cia.xsmatrix(temperature, pressure)
xs_ray = opa_ray.xsmatrix(temperature, pressure)
xs_mie = opa_mie.xsmatrix(temperature, pressure)
# 5. Total opacity (sum of all contributions)
xs_total = xs_line + xs_cia + xs_ray + xs_mie
All classes use the same xsmatrix method, enabling transparent summation of line and continuum contributions. The Voigt profile computation is encapsulated within OpaPremodit (or OpaDirect/OpaModit) and relies on the folded kernel described above.
Summary
- Voigt profiles in ExoJAX are computed via the
fold_voigt_kernel_logstfunction insrc/exojax/opacity/_common/ditkernel.py, wrapped by line calculators (OpaDirect,OpaModit,OpaPremodit) that inherit fromOpaCalc. - CIA, Rayleigh, and Mie scattering are implemented as subclasses of
OpaContinsrc/exojax/opacity/opacont.py, specificallyOpaCIA,OpaRayleigh, andOpaMie. - All opacity calculators expose a consistent
xsmatrixinterface, enabling seamless combination of line and continuum opacities within JAX-autodifferentiable radiative transfer models. - The architecture separates low-level kernel physics (DIT/Modit algorithms) from high-level opacity classes, optimizing for both GPU acceleration and code clarity.
Frequently Asked Questions
What is the difference between OpaModit and OpaPremodit?
OpaModit computes the Modit (Modified Discrete Integral Transform) transform matrices on-the-fly during the first call, making it memory-efficient but slightly slower for repeated evaluations. OpaPremodit pre-computes and caches these transform matrices during initialization, consuming more memory but delivering maximum GPU acceleration during retrievals. Both use the same underlying Voigt kernel in ditkernel.py, but OpaPremodit is preferred for atmospheric retrieval workflows where the line list is fixed.
How does ExoJAX optimize Voigt profile calculations for GPU acceleration?
ExoJAX leverages JAX’s just-in-time compilation (@jit) and vectorization to evaluate millions of spectral lines simultaneously. The fold_voigt_kernel_logst function in src/exojax/opacity/_common/ditkernel.py folds the Doppler and Lorentzian profiles into a single kernel operation that runs efficiently on GPU. Additionally, the OpaPremodit class pre-computes interpolation weights, reducing the computational graph depth during the forward pass of radiative transfer models.
Can I combine multiple CIA sources in a single opacity calculation?
Yes. Because each CIA source (e.g., H₂–H₂, H₂–He) is instantiated as a separate OpaCIA object, you can sum their contributions just like any other opacity component. For example: xs_total = opa_cia_h2h2.xsmatrix(T, P) + opa_cia_h2he.xsmatrix(T, P). Each OpaCIA instance reads its own tabulated coefficients from the ExoMol-CIA database and handles temperature interpolation independently, allowing you to build complex atmospheric models with multiple collision partners.
Where are the Mie scattering tables stored and how are they interpolated?
Mie scattering tables in ExoJAX are stored as pre-computed grids of extinction efficiency, scattering efficiency, and asymmetry parameters as functions of size parameter and complex refractive index. The OpaMie class in src/exojax/opacity/opacont.py loads these tables (typically for compositions like MgSiO₃ or TiO₂) and uses JAX’s interpolation functions to map them onto the model’s wavenumber grid based on the specified particle size distribution. The interpolated values are then scaled by particle number density to yield the final Mie opacity cross-sections.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →