Quick start

This page shows minimal working examples for each backend. The User guide explains every configuration option in detail.

All backends share the same mental model:

  1. Build a configuration object describing the atmosphere (number of layers, number of quadrature streams, optical depths, single-scattering albedos, phase-function moments, boundary conditions, and source terms).

  2. Call solve (single spectral point) or solve_batch (a whole spectrum).

  3. Read the fluxes and mean intensities out of the result, indexed by interface from the top of the atmosphere (index 0) to the surface (index num_layers).

C++: a solar problem

#include "adding_doubling.hpp"

// A 5-layer atmosphere with 8-stream quadrature.
adrt::ADConfig cfg(5, 8);
cfg.solar_flux     = 1.0;
cfg.solar_mu       = 0.5;    // cos(solar zenith angle)
cfg.surface_albedo = 0.3;
cfg.allocate();

for (int l = 0; l < 5; ++l) {
  cfg.delta_tau[l]          = 0.2;
  cfg.single_scat_albedo[l] = 0.9;
}
cfg.setHenyeyGreenstein(0.7);

adrt::RTOutput result = adrt::solve(cfg);
// result.flux_up[0]   -> upward flux at the top of the atmosphere
// result.flux_down[5] -> downward flux at the surface

C++: a thermal problem

adrt::ADConfig cfg(10, 8);
cfg.use_thermal_emission = true;
cfg.wavenumber_low       = 500.0;    // cm^-1
cfg.wavenumber_high      = 1500.0;
cfg.allocate();

for (int l = 0; l <= 10; ++l)
  cfg.temperature[l] = 250.0 + 10.0 * l;      // level temperatures [K]
for (int l = 0; l < 10; ++l) {
  cfg.delta_tau[l]          = 0.3;
  cfg.single_scat_albedo[l] = 0.0;            // pure absorption
}

adrt::RTOutput result = adrt::solve(cfg);

By default the surface emits at the bottom level temperature and the model top emits downward at temperature[0]. Both boundaries can be decoupled, following the DisORT convention:

cfg.surface_temperature = 320.0;  // skin temperature, distinct from temperature[num_layers]
cfg.top_temperature     = 0.0;    // cold space: no downwelling at TOA

JAX: single wavenumber

from src_jax import ADConfig, solve

cfg = ADConfig()
cfg.num_layers     = 5
cfg.num_quadrature = 8
cfg.solar_flux     = 1.0
cfg.solar_mu       = 0.5
cfg.surface_albedo = 0.3
cfg.allocate()

for l in range(5):
    cfg.delta_tau[l]          = 0.2
    cfg.single_scat_albedo[l] = 0.9
cfg.set_henyey_greenstein(0.7)

result = solve(cfg)
# result.flux_up[0]   -> upward flux at TOA
# result.flux_down[5] -> downward flux at BOA

JAX: batched across a spectrum

import numpy as np
from src_jax import BatchConfig, solve_batch

bcfg = BatchConfig()
bcfg.num_wavenumbers = 1000
bcfg.num_layers      = 50
bcfg.num_quadrature  = 8
bcfg.num_moments_max = 16
bcfg.surface_albedo  = 0.1
bcfg.solar_flux      = 1.0    # optional solar beam
bcfg.solar_mu        = 0.5

delta_tau = np.random.uniform(0.01, 0.5, (1000, 50))
ssa       = np.full((1000, 50), 0.9)
pmom      = np.zeros((50, 16))         # shared across wavenumbers
for l in range(50):
    for m in range(16):
        pmom[l, m] = 0.7 ** m          # Henyey-Greenstein g = 0.7
planck    = np.zeros((1000, 51))       # zero = no thermal emission

flux_up, flux_down = solve_batch(bcfg, delta_tau, ssa, pmom, planck)
# flux_up.shape  -> (1000,)  TOA upward flux per wavenumber
# flux_down.shape -> (1000,) TOA downward flux per wavenumber

CUDA: batched host convenience call

#include "cuda_solver.cuh"

adrt::cuda::BatchConfig cfg;
cfg.num_wavenumbers = 4096;
cfg.num_layers      = 60;
cfg.num_quadrature  = 8;
cfg.num_moments_max = 16;
cfg.surface_albedo  = 0.1;

// Flat SoA arrays: array[wav * nlay + layer], etc.
auto res = adrt::cuda::solveBatchHost(
    cfg, delta_tau, single_scat_albedo,
    phase_moments, /*shared=*/true,
    planck_levels);
// res.flux_up[w], res.flux_down[w], res.flux_direct[w]

See Backends & API reference for the full API of each backend.