Time stepping
The time loop lives in run_case (src/time_stepping/Driver.jl); the nonlinear Stokes iteration in src/time_stepping/PicardLoop.jl (with a full-Newton alternative in NewtonLoop.jl); the adaptive step size in src/time_stepping/CFL.jl.
Driver loop order
After setup (mesh → FE spaces → State → markers → initial step = 1:ntot executes in this order:
Stokes solve (
do_stokes) —picard_stokes!, or the three-field two-phase solve whendo_two_phase = true(see Two-phase flow). Uses the running-mean step sizetimesum/(step-1)as the Maxwell time-step guess for the projection's elastic weight(step 1 uses maxtmstep); the Picard loop re-projects with it each iteration.CFL —
compute_dt, then the melt-segregation limit (below);timesum += dt.Porosity evolution (two-phase only) — compaction opens/closes pores at the rate set by the solved compaction pressure.
Heat (
do_heat) — backward-Euler solve (solve_heat_step!); inmarker_T_mode = "pic_increment"the pre-solveis first rebuilt from marker temperatures via a cached projection ( _project_cellfield_to_T), optionally followed by subgrid diffusion; markers then absorb only theincrement ( update_marker_temperatures!), clamped to the bound-preserving limits of_heat_clamp_bounds(pre-solve field range ∪ Dirichlet endpoints
- 5 %·ΔT slack).
Composition FE (
do_composition_fe) — SUPG advection step forChand any extra composition fields.Marker stresses —
update_marker_stresses!(VEP, Jaumann-rotated) before advection so the rotation uses the current cell-frame velocity gradient.Advection (
do_advect) —advect_markers!(RK4), thenupdate_marker_strain!(healing + yield-gated accumulation),update_marker_grain_size!(if any rock hasgrain_G0 > 0), thenreseed_markers!.Phase changes + melting (
do_phase_change/do_melting) —apply_marker_phase_and_melt!, accumulating per-cell latent heatH_marker_latent(W/m³) for the next heat step.Volatiles (
do_volatile) — release + buoyant transport.Final projection —
project_markers_to_cells!so diagnostics and the next step start from current marker state;record_step!.I/O every
output_everysteps — grid/markers/topography VTU + PVD, benchmark observables, inventories; checkpoint everycheckpoint_everysteps.AMR every
amr_everysteps (ifdo_amr) and ALE free surface each step (ifdo_ale_free_surface, 2D) — see AMR & ALE. A successful AMR step setsskip_heat_nextso the heat solve is skipped once while the marker-temperature projection re-establishes consistency on the new mesh.
The run stops at ntot steps or when timesum reaches t_end_myr (if nonzero). With solver_type = "bamgs" the entire loop is wrapped in a single PETSc context (with_bamgs_context, see Linear solvers). Setting PROFILE_STEPS=1 in the environment prints a per-step timing breakdown.
CFL time step
compute_dt (src/time_stepping/CFL.jl) takes the most restrictive of:
Advection CFL:
, with (default 0.5), the smallest cell edge, and the largest velocity DOF magnitude (non-finite DOFs are clamped with a warning rather than propagating dt = NaN).Diffusion limit:
using the largest cell diffusivity . Backward Euler is unconditionally stable; this keeps the Fourier number ≤ maxxystepfor accuracy.Hard cap:
(default 1.5 × 10¹³ s ≈ 500 kyr). Floor:
(avoids dt = 0stalls).
Melt-segregation limit
With two-phase flow the Darcy flux can outrun the solid CFL. The Driver computes the maximum segregation speed (max_melt_segregation_speed, src/physics/TwoPhase.jl)
from the cell-averaged fluid-pressure gradient of the last
Picard iteration
The Stokes operator is nonlinear because picard_stokes! runs the fixed-point sequence
for up to n_picard iterations (default 3). Each iteration re-projects markers to cells (the viscosity update enters through compute_VEP_viscosity at the cell strain rate), rebuilds the buoyancy and optional VEP/free-slip/traction/ALA terms, refills the cached sparse system in place, and re-solves with warm starts — see caching.
Velocity-increment convergence test
Convergence is declared when the relative change of the velocity DOF vector drops below picard_tol (default 1e-3):
This is deliberately an increment test on the DOF vector, not a comparison of successive iterate norms — the earlier norm-based criterion could report convergence for two different velocity fields of equal magnitude. The outcome is recorded on the state (last_picard_iters, last_picard_rel, last_picard_ok); a post-run audit (print_convergence_summary) scans run.log for non-converged steps.
Damped Picard and full Newton
nonlinear_method = "newton"applies under-relaxation inside the Picard loop:with (default 0.5). trades convergence speed for robustness under large viscosity contrasts; recovers plain Picard. nonlinear_method = "full_newton"selectsnewton_stokes!(src/time_stepping/NewtonLoop.jl): linearization of the residualin the spirit of Fraters et al. (2019), with re-projection of at the current strain rate each iteration and an Armijo-style backtracking damp when the velocity-DOF change grows. This path is a scaffold (MUMPS-only, no caching) and is not the production default.
Config keys
[time]
maxxystep = 0.5 # CFL fraction (advection, diffusion, melt segregation)
maxtmstep = 1.5e13 # hard cap on dt (s)
ntot = 100 # number of steps
# t_end_myr = 0.0 # stop at this model time (Myr); 0 = step count only
[fe]
n_picard = 3
picard_tol = 1e-3
nonlinear_method = "picard" # picard | newton | full_newton
newton_damping = 0.5
[temperature]
marker_T_mode = "pic_increment" # pic_increment | overwrite
# do_subgrid_diffusion = false
# subgrid_d = 1.0