Longwave: Williams (2026) Simple Spectral Model

The AnalyticBandLongwave solver advances Schwarzschild's two-stream equations

\[\frac{d\mathscr{I}^{\uparrow}}{d\tau} = \mathscr{I}^{\uparrow} - \pi B(T), \qquad \frac{d\mathscr{I}^{\downarrow}}{d\tau} = \pi B(T) - \mathscr{I}^{\downarrow}\]

at each of Nwavenumbers = 41 evenly spaced wavenumbers between 10 and 2500 cm⁻¹ (inclusive of both endpoints, spacing 62.25 cm⁻¹) and integrates the resulting fluxes spectrally.

Absorption spectra

The scheme represents three analytic sources of clear-sky absorption:

using NumericalRadiation
using CairoMakie

longwave = AnalyticBandLongwave(Float64)
ν̃ = range(longwave.wavenumber_min, longwave.wavenumber_max, length=500)

# Replace zeros with NaN so the log-y plot shows gaps where a band is inactive.
nan_zero(v) = [x == 0 ? NaN : x for x in v]

water_vapor_line_absorption      = nan_zero([water_vapor_line_absorption_reference(ν, longwave)      for ν in ν̃])
water_vapor_continuum_absorption =          [water_vapor_continuum_absorption_reference(ν, longwave) for ν in ν̃]
carbon_dioxide_absorption        = nan_zero([carbon_dioxide_absorption_reference(ν, longwave)        for ν in ν̃])

fig = Figure(size=(760, 440))
ax  = Axis(fig[1, 1];
           xlabel = "Wavenumber ν̃ [cm⁻¹]",
           ylabel = "κ [m² kg⁻¹]",
           yscale = log10,
           title  = "Williams (2026) reference absorption (T = 260 K, p = 500 hPa)")
lines!(ax, ν̃, water_vapor_line_absorption;      label="H₂O line",      linewidth=2)
lines!(ax, ν̃, water_vapor_continuum_absorption; label="H₂O continuum", linewidth=2)
lines!(ax, ν̃, carbon_dioxide_absorption;        label="CO₂ 15 μm",      linewidth=2, linestyle=:dash)
axislegend(ax; position=:rt)

All three curves are evaluated at the paper's reference state (T, p, RH) = (260 K, 500 hPa, 100 %). At runtime williams_optical_depth_increment applies pressure broadening (κ ∝ p / p_ref), continuum temperature scaling (exp(σ_cont (T_ref − T)), Mlawer et al. 1997), and the two-stream diffusivity factor D = 1.5 (Armstrong 1968).

2 × CO₂ clear-sky forcing

A standard clear-sky CO₂-doubling benchmark. The column is a lapse-rate atmosphere from 220 K at the top to 295 K at the surface with constant specific humidity q = 5 g kg⁻¹ and surface pressure 1000 hPa.

using NumericalRadiation
using CairoMakie

Nz = 32
σᵢ = collect(range(0, 1, length=Nz + 1))   # interface sigma coordinate
grid = ColumnGrid(σᵢ)

FT = Float64
surface   = SurfaceState(FT; sea_surface_temperature=295, land_surface_temperature=285, land_fraction=0.3)
constants = PhysicalConstants(FT)
longwave  = AnalyticBandLongwave(FT)

# Sweep CO₂
carbon_dioxide_ppm = [50, 100, 200, 280, 400, 560, 800, 1120]
OLR = FT[]
for CO₂ in carbon_dioxide_ppm
    profile = AtmosphereProfile(
        temperature      = collect(range(220, 295, length=Nz)),
        humidity         = fill(0.005, Nz),
        geopotential     = zeros(Nz),
        surface_pressure = 100_000,
        CO₂              = CO₂,
    )
    Ṫ = zeros(Nz)
    diagnostics = LongwaveDiagnostics(FT)
    solve_longwave!(Ṫ, diagnostics, longwave, profile, grid, surface, constants)
    push!(OLR, diagnostics.outgoing_longwave)
end

fig = Figure(size=(820, 360))
ax1 = Axis(fig[1, 1];
           xlabel = "CO₂ [ppmv]",
           ylabel = "ℐꜛˡʷ at TOA [W m⁻²]",
           xscale = log10,
           title  = "Clear-sky outgoing longwave vs CO₂")
lines!(ax1, carbon_dioxide_ppm, OLR;   color=:black, linestyle=:dash)
scatter!(ax1, carbon_dioxide_ppm, OLR; markersize=10, color=:black)

ax2 = Axis(fig[1, 2];
           xlabel = "CO₂ [ppmv]",
           ylabel = "ℐꜛˡʷ(280) − ℐꜛˡʷ(CO₂) [W m⁻²]",
           xscale = log10,
           title  = "Clear-sky CO₂ radiative forcing")
lines!(ax2, carbon_dioxide_ppm, OLR[4] .- OLR; color=:crimson, linewidth=2)
scatter!(ax2, carbon_dioxide_ppm, OLR[4] .- OLR; markersize=10, color=:crimson)
vlines!(ax2, 560; color=:gray70, linestyle=:dot)
hlines!(ax2, 0;   color=:gray70)

ΔOLR = OLR[4] - OLR[6]
Label(fig[2, 1:2], "2× CO₂ forcing (280 → 560 ppmv) = $(round(ΔOLR, digits = 2)) W m⁻²";
      tellwidth = false, fontsize = 12)

The forcing of OLR(280) − OLR(560) is in the physically plausible range for clear-sky 2×CO₂ (2–5 W m⁻², cf. IPCC AR6 WG1 Ch. 7).