Analytic temperature Jacobians
For thermal (longwave) problems the C++ backend can return the analytic
derivatives of every output field with respect to every temperature degree of
freedom, at a small fraction of the cost of the forward solve. This is
enabled by compute_temperature_jacobian and follows the linearisation of
the interaction principle of Spurr & Christi (2006), specialised to the
temperature variable.
What is differentiated
Four output fields are differentiated:
for the upward flux, downward flux, actinic mean intensity, and flux
divergence (heating rate). They fill the four RTOutput arrays
flux_up_temperature_jac, flux_down_temperature_jac,
mean_intensity_temperature_jac, and flux_divergence_temperature_jac.
Each array is indexed [interface][dof]:
interface \(k = 0, \dots, N_{\text{lyr}}\) — the output level.
dof \(m\) — the temperature it is differentiated against:
dof 0 .. num_layersare the level temperatures \(\partial/\partial T_m\);dof num_layers+1is the independent surface skin temperature \(\partial/\partial T_s\).
When the solver is driven by planck_levels instead of temperatures, the
arrays hold \(\partial/\partial B_m\) (the Planck chain rule is skipped).
Why it is cheap and exact
Temperature enters the adding–doubling equations only through the Planck emission source. Every reflection and transmission operator is temperature-independent,
The Jacobian is therefore obtained by re-running the same forward linear operators with differentiated right-hand sides. It is exact to machine precision within the discrete-ordinate approximation itself. The direct solar beam carries no temperature derivative, so the solar source vectors are ignored throughout the Jacobian pass.
The implementation works in two stages: it first forms derivatives with respect to the level Planck values \(B_m\), then applies a single per-column Planck factor \(\dd B_m / \dd T_m\) at the end.
Source seeds
Each layer’s thermal source is written compactly as
with layer mean \(\bar B_l = \tfrac12(B_l + B_{l+1})\) and gradient \(B'_l = (B_{l+1} - B_l)/\tau_l\). The coefficient vectors \(\vect{a}_l, \vect{b}_l\) are temperature-independent (they are the doubling vectors \(\vect{y}_l, \vect{z}_l\) in the scattering branch, and the closed-form absorption coefficients otherwise). Because \(\bar B_l\) and \(B'_l\) are linear in the two bounding level values, defining
gives the complete layer-level seed as just two \(N\)-vectors:
Layer \(l\) seeds only columns \(m = l\) and \(m = l+1\). The surface seed is \(\partial\vect{s}^{\uparrow}/\partial B_s = (1-\alpha)\vect{1}\); the boundary intensities contribute \(\partial\vect{I}^{\downarrow}_{\text{top}}/\partial B_0 = \vect{1}\) and, for the diffusion lower boundary, \(\partial I^{\uparrow}_{\text{bot},i}/\partial B_L = 1 + \mu_i/\tau_{L-1}\) and \(\partial I^{\uparrow}_{\text{bot},i}/\partial B_{L-1} = -\mu_i/\tau_{L-1}\).
Propagation through adding
The source-combination law (3) is linear in the sources with temperature-independent operators, so it applies verbatim to the derivatives:
All \(N_{\text{lyr}}+2\) derivative columns are propagated together as an
\(N \times (N_{\text{lyr}}+2)\) block, so each operator application is a
single matrix product against all degrees of freedom at once. One bottom-up
sweep produces the source derivatives of every rbase composite and one
top-down sweep every rtop composite.
At each interface, the intensity solve \((\mat{I} - \mat{R}\mat{R})\,\vect{I} = \text{rhs}\) uses a temperature-independent matrix, so the forward LU factorisation is cached and reused to back-substitute \(\partial\vect{I}/\partial B_m\) against \(\partial(\text{rhs})/\partial B_m\). The flux and mean-intensity derivatives are then the same quadrature reductions as the forward pass.
Heating-rate Jacobian
Differentiating (6) gives
i.e. the mean-intensity Jacobian scaled by \(4\pi(1-\omega_{l(k)})\), with a diagonal Planck-cooling correction \(-4\pi(1-\omega_{l(k)})\) on the level-\(k\) column (from \(\partial B_k/\partial B_m = \delta_{km}\)). The surface column carries no diagonal term because \(B_k\) is a level quantity.
Planck chain rule
Converting from \(B\)-Jacobians to \(T\)-Jacobians is a single per-column scaling,
using the band-integrated Planck temperature derivative
Level columns use \(\dd B_m/\dd T_m\) at temperature[m]; the surface
column uses \(\dd B_s/\dd T_s\) at surface_temperature.
Depth limit of the heating Jacobian
The heating Jacobian \(\mat{H} = \partial(\nabla\!\cdot\!F)/\partial T\) is built from the even (zeroth) angular moment \(J\) and carries a local Planck-cooling diagonal, which makes it diagonally dominant in the mid optical-depth range. This is in deliberate contrast to the net-flux Jacobian \(\mat{K} = \partial F/\partial T\), built from the odd (first) moment, whose diagonal cancels by symmetry (it is identically zero on a uniform optical-depth grid) and which is therefore dense and long-ranged. When a temperature corrector needs a well-conditioned operator, it should drive the collocated heating residual \(\mat{H}\), not the flux residual.
However, \(\mat{H}\) degenerates at large optical depth. Using the linear-in-\(\tau\) source, the self-response of the mean intensity to the local level temperature has the closed form
where \(a\) and \(b\) are the optical thicknesses of the layers just above and below level \(i\), and \(E_n\) are the exponential integrals. In the two limits,
\(G \to 0\) as \(c \to 0\) — optically thin: \(J\) is insensitive to the local temperature (the “top collapse”);
\(G \to 1\) as \(c \to \infty\) — optically thick: \(J\) is slaved to the local Planck function.
The heating diagonal is therefore
The Planck-cooling diagonal is cancelled by the self-response \(\partial J_i/\partial B_i \to 1\), and the whole operator collapses as \(1/(2\Delta\tau)\). The crossover is governed by the local layer optical thickness, not the cumulative optical depth: by \(\Delta\tau \approx 3\) the diagonal is roughly 16 % of its ceiling, and by \(\Delta\tau \approx 10\) the heating Jacobian is effectively zero.
Note
This is transport physics, not a numerical defect: at large optical depth the field is in local thermodynamic equilibrium with the source (\(J \to B\)), so there is no local radiative restoring force on temperature. A radiative-equilibrium temperature corrector must combine the heating Jacobian (which “owns the middle of the optical-depth range”) with a deep, flux-based diffusion treatment and a thin-top Planck inversion.
Cost, caching, and scope
No forward work is repeated. The one-time \(O(N_{\text{lyr}} N^3)\) doubling and adding costs are reused. The extra work is two source-only adding sweeps plus one differentiated output pass, each \(O(N_{\text{lyr}}^2 N^2)\) for the full block — typically a single-digit percentage on top of the forward solve.
Caching. On the templated CPU path the forward build captures the LU factorisations of the adding matrices and interface matrices, so the Jacobian never re-factorises.
Validation. The feature is checked against central finite differences to \(\sim 10^{-3}\) relative error and cross-checked against the sibling DisORT++ solver to \(\sim 10^{-5}\), over the absorption and scattering branches, both boundary conditions, both surface modes, and delta-M on/off.
Scope. Implemented on the templated CPU path (with caching) and the dynamic fallback (Eigen-based, no caching). A CUDA port is deferred. Only temperature (Planck-source) Jacobians are provided; optical-depth, SSA, and phase-function Jacobians are out of scope.