Heat transport
The energy equation is solved implicitly on the FE grid (src/physics/Heat.jl, driven by solve_heat_step! in src/time_stepping/Driver.jl) with sources assembled in src/physics/HeatSources.jl. Temperature advection is, by default, handled in the Lagrangian frame by the markers (PIC pattern below), so the grid solve is a pure diffusion + source step.
Strong form and backward-Euler discretization
where
Testing with build_heat_forms):
Temperature uses Q1 elements (fe_order_T = 1). The system is solved with a cached MUMPS factorization (CachedMumps in state.heat_solver_cache) — the sparsity pattern is constant across steps, so only the numeric factorization repeats.
Density convention: the heat equation uses the constant Boussinesq reference density, rho_ref_heat
Stabilized FE advection (optional)
With do_supg_heat = true (or heat_stabilisation = "supg"), the FE field advects temperature directly and the cell-Péclet > 1 regime is stabilized by SUPG: the test function is augmented
with the Codina (2000) parameter
so
heat_stabilisation = "entropy" instead adds residual-driven artificial diffusion (Guermond–Pasquetti–Popov 2011; entropy_viscosity in src/physics/EntropyViscosity.jl):
with solve_heat_step!). Default is heat_stabilisation = "none" (PIC advection, diffusion-only FE operator).
Boundary conditions
Configured per wall in [boundary_temperature] (build_heat_flux_bcs in Heat.jl; Dirichlet values in src/boundary_conditions/Temperature.jl):
bcT_surface / bcT_deep | Weak-form contribution | Keys |
|---|---|---|
"dirichlet" (default) | strong, via the FE space: T_top (surface), T_bot (deep) | T_top, T_bot |
"flux" | q_surface, q_deep (W/m²) | |
"robin" | h_robin, T_amb | |
"insulating" | natural zero-flux Neumann, no contribution | — |
Lateral walls are insulating by default.
Heat sources
build_heat_source (src/physics/HeatSources.jl) assembles CellField expressions that track the current FE solution:
Radiogenic (
do_radiogenic = true): per-cell marker mean of the per-rockradiogenic_heating(W/m³; typical continental crust, oceanic crust, mantle), held in state.ht_c.Constant / functional:
H_constadds a uniform W/m³ everywhere;cfg.H_fn(set programmatically, not from TOML) evaluatesat cell centres — used by prescribed-source benchmarks. Shear heating (
do_shear_heating = true): viscous dissipation; with MILET's invariant convention this is built from the cell fields
eta_c(the effective, so only the visco-plastic part dissipates) and eii_c.Adiabatic (
do_adiabatic = falseby default):with the hydrostatic simplification : positive for downward flow (
in the y-down convention — sinking material heats). Implemented with a representative K⁻¹, rho_ref_heat,gy.Latent (phase change + melting):
, accumulated on markers by apply_marker_phase_and_melt!and passed in as the per-cell vectorH_marker_c. Marker contributions are averaged per cell, not summed, so dense marker packing cannot inflate the source. Sign convention:endothermic. See Phase changes and Melting.
PIC temperature pattern
Markers carry temperature; how they exchange it with the grid is set by marker_T_mode ([temperature], default "pic_increment" in cases/_defaults.toml). The per-step sequence in src/time_stepping/Driver.jl is:
pic_incrementonly — project markerto cell means ( project_marker_T_to_cells) and L2-fit the piecewise-constant field onto Q1 (_project_cellfield_to_T, bound-preserving: the projection is clamped to the marker-data range widened to the Dirichlet endpoints), stamping the post-advection marker temperature intostate.Th.Optional subgrid diffusion (below), then re-project so the grid solve sees the corrected field.
FE heat solve (
solve_heat_step!) →with diffusion + sources. Marker update (
update_marker_temperatures!insrc/particles/Projection.jl):
mode = :pic_increment:— the I2ELVIS/PIC pattern: markers keep their advected temperature identity and absorb only the diffusion + source increment. Required for vigorous convection (Blankenbach-class) and for cold slabs that must not lose their thermal identity. mode = :overwrite:— markers track the FE field entirely. Caveat: combined with the default diffusion-only heat operator, temperature then does not advect at all; use overwriteonly deliberately, e.g. together withdo_supg_heat = trueso the FE operator supplies the advection.
Subgrid diffusion (Gerya & Yuen 2003)
With do_subgrid_diffusion = true, apply_subgrid_diffusion! (src/particles/Projection.jl) relaxes each marker toward the grid-interpolated temperature
with subgrid_d (default 1.0). This suppresses the marker–grid temperature inconsistency that otherwise accumulates in PIC thermal convection and stalls it at fine grids. The correction only acts in pic_increment mode, is restricted to uniform Cartesian meshes, and is followed by a re-projection so it is energy-conservative on the grid.
Bound-preserving clamp
Galerkin/BE produces small over/undershoots near sharp gradients (sticky-air interface, slab tips) that compound over many PIC steps. _heat_clamp_bounds (src/time_stepping/Driver.jl) computes per-step bounds from the pre-solve field range united with the Dirichlet endpoints, widened by a 5 % slack:
The rationale: by the maximum principle, pure BE diffusion cannot exceed the pre-solve range; the slack admits legitimate source-driven growth (shear/radiogenic/latent heating) per step. The same bounds are applied to the FE dofs after the solve and to the marker temperatures in update_marker_temperatures!, so grid and markers stay consistent. (Clamping to the Dirichlet endpoints alone — an earlier scheme — flattened internal anomalies hotter than the boundary range.)
Time-step interaction
compute_dt (src/time_stepping/CFL.jl) limits
Key reference
| Config key | Default | Meaning |
|---|---|---|
do_heat | true | solve the energy equation |
T_top, T_bot | 273, 1623 K | Dirichlet surface / bottom temperature |
rho_ref_heat | 3300 | constant |
marker_T_mode | "pic_increment" | pic_increment (ΔT → markers) or overwrite (FE → markers) |
heat_stabilisation | "none" | none (PIC) | supg | entropy |
do_supg_heat | false | legacy alias for SUPG-stabilized FE advection |
do_subgrid_diffusion, subgrid_d | false, 1.0 | Gerya–Yuen marker-grid relaxation |
do_radiogenic, do_shear_heating, do_adiabatic | true, true, false | source toggles |
H_const | 0.0 | uniform extra source (W/m³) |
bcT_surface, bcT_deep | "dirichlet" | dirichlet | flux | insulating | robin |
q_surface, q_deep, h_robin, T_amb | 0, 0, 0, 298.15 | flux / Robin BC data |
radiogenic_heating (per [[rock]]) | — | |
specific_heat, thermal_conductivity (per [[rock]]) | — |