Energy equation
Temperature is advanced in two parts each step: transport by the flow (on markers, or on the grid with an upwind or TVD scheme), then heating and conduction on the grid. This page covers the solver for the second part, diffuse_temperature in solvers/diffuse_temperature.jl. The equation it solves, the conservative radial fluxes and the wall conditions are described in Temperature transport and time stepping.
Schemes
Written for the discrete conduction operator
with
| Path | Selected by | Solver | |
|---|---|---|---|
| 2-D explicit | cfg.useImplicitTemp = false | 0 (forward Euler) | none, one stencil update |
| 2-D Crank-Nicolson (default) | cfg.useImplicitTemp = true, cfg.implicitTheta = 0.5 | 0.5 | sparse LU of |
| 2-D backward Euler | cfg.implicitTheta = 1.0 | 1 | sparse LU |
| 3-D, time loop | always | 0 (forward Euler) | none, one stencil update per patch |
3-D, implicit = true keyword | benchmark drivers | 1 (backward Euler) | sparse solve per patch |
The 2-D matrix

(A) Eigenvalues diffuse_temperature builds and caches (_diff_L_cache), on a 16 × 64 spherical annulus, in units of the explicit stability estimate docs/scripts/solvers_energy_sections.jl.
Accuracy and stability

Error of the three 2-D schemes after the same model time, against the time step in units of the explicit stability estimate docs/scripts/solvers_energy.jl.
The figure shows the three properties that set the default:
Order. Crank-Nicolson is second order in time, its error falling by a factor of four when the step halves; forward and backward Euler are first order. At the same step Crank-Nicolson is two to three orders of magnitude more accurate here.
Stability. Both implicit schemes are unconditionally stable, so in 2-D the time step is set by advection alone (the CFL condition of
compute_timestep), and there is no diffusive limit.Cost. One sparse solve with a five-point matrix, which is small next to the Stokes solve of the same step.
Backward Euler is more strongly damping: at very large steps Crank-Nicolson can leave a slowly decaying, sign-alternating component in the sharpest modes, which backward Euler removes. Use
3-D steps
The explicit 3-D update is stable for cfg.dtMax below that bound.
Coupling the 3-D patches
In 3-D, conduction across the patch boundaries is handled by a short Schwarz iteration of its own. yy_compute_T_wall_bcs samples the peer temperature at each patch's bc_T_jmin, bc_T_jmax keywords), cfg.nTSchwarz times per step (default 3); yy_sync_T_walls! then overwrites each patch's wall-cell row with the peer value at that cell centre. See Yin-Yang Schwarz coupling.

Largest difference between Yin's wall-row temperature and Yang's temperature at the same points, measured before the final yy_sync_T_walls!, on a 3-D shell with docs/scripts/solvers_energy_sections.jl.
Energy conservation
The solver is written so that the discrete energy budget closes: the radial fluxes are differences across cell faces divided by the exact cell measure, and the wall heat flow reported by the diagnostics is the flux the solver itself used. tests/test_nusselt_conduction.jl checks a one-step budget residual of order

(A) Change of thermal_energy over one call of diffuse_temperature minus boundary_power, weighted with the scheme's own compute_diagnostics on the solver's own conducting steady state, docs/scripts/solvers_energy_sections.jl.
Where in the code
| Topic | File | Function |
|---|---|---|
| Entry points (2-D, 3-D) | solvers/diffuse_temperature.jl | diffuse_temperature |
| Explicit update | solvers/diffuse_temperature.jl | _solve_explicit |
| θ-scheme, caches | solvers/diffuse_temperature.jl | _solve_crank_nicolson, _diff_L_cache, _diff_lu_cache |
| 3-D wall coupling | utils/yy_interp.jl | yy_compute_T_wall_bcs, yy_sync_T_walls! |
| Tests | tests/ | test_diffusion.jl, test_conduction_conservation.jl, test_nusselt_conduction.jl |