Skip to content

Governing equations

MILET solves creeping (zero-Reynolds-number) thermo-mechanical flow: a mixed Stokes problem for velocity and pressure with visco-elasto-plastic (VEP) rheology, coupled to an energy equation (Heat transport) and a marker-in-cell material description (Markers). This page covers the Stokes system as implemented in src/physics/Stokes.jl, the buoyancy term from src/physics/Gravity.jl / src/time_stepping/PicardLoop.jl, and the compressible-form variants in src/physics/Compressibility.jl.

Strong form

In the default Boussinesq formulation (formulation = "boussinesq"):

(2ηVEPε(u))+p=ρg(momentum)u=0(mass)

with ε(u)=12(u+uT) the symmetric strain-rate tensor and ηVEP the effective visco-elasto-plastic viscosity (see Rheology). MILET uses a y-positive-down convention, so gravity is gy = +9.81 in the [gravity] TOML section.

Weak form

build_stokes_forms (src/physics/Stokes.jl) assembles the bilinear and linear forms. Multiplying momentum by a test function vV, integrating the stress term by parts, and testing continuity with qQ:

a((u,p),(v,q))=Ω2ηVEPε(u):ε(v)dΩΩp(v)dΩΩq(u)dΩ+ΓfsΓγ(un)(vn)dΓl((v,q))=ΩρgvdΩ+Ωχσold:ε(v)dΩ+ΓtΓtvdΓ

The system is discretized with inf-sup stable Q2–Q1 Taylor–Hood elements (fe_order_v = 2, fe_order_p = 1 in the [fe] section; see Discretization). ηVEP enters as a piecewise-constant CellField projected from the markers (project_markers_to_cells! in src/particles/Projection.jl). assemble_stokes wraps the forms into a Gridap AffineFEOperator.

Free-slip penalty

Walls flagged free_slip (bc_left, bc_right, bc_surface, bc_deep, plus bc_front/bc_back in 3D) contribute the penalty term

Γγ(un)(vn)dΓ,

which weakly enforces no-penetration (un=0) while leaving the tangential component free. build_free_slip_bcs sets the penalty scale per wall as

γ=free_slip_penaltyηrefhx,

where ηref is the volume-mean viscosity of the current iteration (floored at eta_min) and hx the cell width — so the dimensionless config key free_slip_penalty (default 1000) is portable across viscosity regimes and resolutions.

Traction (Neumann) walls

Walls flagged traction impose σn=t via the RHS boundary integral ΓtvdΓ. build_traction_bcs composes the global-frame traction vector from the per-wall config keys t_traction_<wall>_n (normal, positive outward) and t_traction_<wall>_t (tangential), where <wall> is one of left | right | surface | deep. Walls flagged prescribed use strong Dirichlet values instead (src/boundary_conditions/Velocity.jl).

VEP elastic memory source

For Maxwell visco-elasticity the previous-step deviatoric stress enters the RHS as

Ωχσold:ε(v)dΩ,χ=1Z,

where Z=GΔt/(ηVP+GΔt) is the visco-elasticity factor and χ the elastic-memory weight (derivation in Rheology — Maxwell elasticity). σold is a cell-piecewise SymTensorValue field built from the marker-averaged stress components by build_sigma_old_cellfield. picard_stokes! only constructs this term when any cell has χ>0 (i.e. some rock has shear_modulus G>0); with χ=0 everywhere the system reduces to pure visco-plastic Stokes.

After each Stokes solve, update_marker_stresses! applies the marker-level stress update (Gerya 2010, eq. 12.13)

σnew=2ηVEPε˙(u)+χσold

followed by an explicit Jaumann co-rotation increment Δt(WσσW) with spin tensor W=12(uuT); the 3D variant _update_marker_stresses_3d! carries all six deviatoric components and the full three-component spin. recover_invariants! computes cell-mean ε˙II=12ε:ε and σII=2ηε˙II for diagnostics and for the next nonlinear iteration.

Buoyancy: Boussinesq perturbation form

buoyancy_cellfield (src/physics/Gravity.jl) builds the body-force CellField used in l. By default the perturbation form is used:

fbuoy=(ρρ¯)g,

where ρ¯ is the volume-weighted mean cell density of the current Picard iteration (a plain arithmetic mean would bias toward refined regions on AMR meshes and produce spurious buoyancy). The hydrostatic component ρ¯g is absorbed into a reference pressure that is dropped from the system entirely — this prevents direct-solver pivoting on the singular saddle point from leaking the hydrostatic gradient into the velocity field (observed as 104× overestimated plume velocities before the fix; see the commentary in src/time_stepping/PicardLoop.jl).

Options:

Config keyDefaultEffect
body_force_fullfalsetrue → use full ρg (I2ELVIS/FD convention), no subtraction
buoyancy_from_Tfalsetrue → build ρ(Th)=ρ0(1α(ThTref)) from the continuous FE temperature (resolves thermal boundary layers that the cell-constant marker projection under-drives at high Ra); Tref = T_ref_density
gravity.model"vertical_const""vertical_const" (constant (gx,gy[,gz]) from gx,gy,gz) or "radial" (gmagr^ toward the origin, magnitude g_mag) — gravity_fn in src/physics/Gravity.jl

ALA / TALA continuity

Setting formulation = "ala" or "tala" (in the [rheology] section) replaces incompressibility with the anelastic mass balance

(ρadiu)=0,

which build_stokes_forms expands by the product rule into the test equation

q(ρadiu+uρadi).

picard_stokes! constructs the 1-D adiabatic reference profile (adiabatic_density_profile in src/physics/Compressibility.jl)

ρadi(y)=ρsurfaceey/H

(exponential with depth, y down) from the config keys rho_ref_ala (default 3300) and the scale height H_scale (default 2.9e6 m), and passes it as the ρ_adi_cell kwarg to assemble_stokes. TALA (truncated ALA) uses the same continuity but drops the dynamic-pressure contribution to density. Valid values of formulation are checked by ala_continuity_modification: boussinesq | ala | tala | isothermal_compression. A linearized adiabatic temperature profile helper (adiabatic_temperature_profile, T(y)=Ts(1+αgy/cp)) is also provided.

Nonlinear (Picard) iteration

The viscosity depends on strain rate, η=η(ε˙II), so the Stokes operator is nonlinear. picard_stokes! (src/time_stepping/PicardLoop.jl) iterates:

  1. project markers → cell fields (η, ρ, χ, σold),

  2. assemble and solve the linear Stokes system with frozen η(k),

  3. recover ε˙II(k+1),

  4. test u(k+1)u(k)/u(k+1)<picard_tol.

Controls in [fe]: n_picard (default 3), picard_tol (default 1e-3), nonlinear_method = "picard" | "newton" and newton_damping (α(0,1] under-relaxation; α=1 recovers Picard). Sparse patterns, factorizations, and multigrid hierarchies are cached across iterations — see Linear solvers and Geometric multigrid.