Skip to content

Plume planforms in a 3-D shell ​

Basally heated, isoviscous convection in a full spherical shell on the Yin-Yang grid. The Rayleigh number sets how many plumes the shell holds and how thin they are. At Ra = 10⁴ a few broad upwellings, at 10⁵ more of them, at 10⁶ many thin plumes rising from a thin hot boundary layer, separated by a network of cold sheets.

The driver is examples/yinyang_convection_3d.jl. It runs on exactly two MPI ranks and is meant for a cluster; the HPC page has the batch scripts.

Animated hot-temperature isosurface: plumes rising in a 3-D Yin-Yang spherical shell at Ra = 10^6

A MILEVA run at Ra = 10⁶: the hot-temperature isosurface shows plumes rising from the core-mantle boundary and spreading beneath the surface.

MILEVA 3-D Yin-Yang spherical shell, hot-temperature isosurfaces at Ra = 10⁴, 10⁵, 10⁶. Plumes become more numerous and narrower as Ra grows.

The model ​

∇⋅(2ηε˙)−∇p=ρ0α(T−Tmid)gr^,∇⋅v=0,∂T∂t+∇⋅(vT)−T∇⋅v=κ∇2T,

in a shell with Rin=1220 km, Rout=2220 km, i.e. the Hernlund & Tackley (2008) aspect ratio f=0.5495 and a 1000 km layer, for which their Table 1 lists values for the 3-D spherical shell:

Raη [Pa s]Nu (H&T 3-D shell)Vrms (H&T 3-D shell)
1049.7×10223.8542.3
1059.7×10217.27160
1069.7×102015.9625

These are the reference values for a time-averaged comparison.

Grid and solver ​

itemvaluewhere
grid per patchNr × Nth × 3 Nth: 64 × 32 × 96 (Ra 10⁴), 64 × 48 × 144 (10⁵), 96 × 72 × 216 (10⁶)MILEVA_NR, MILEVA_NTH
radial spacingrefined toward both walls, cfg.amrStretchFactor = 2utils/setup_grid.jl
Yin-Yang overlapbasic overlap, no θ-buffer (cfg.δ_yy = 0), cfg.useMinOverlapGrid = falseYin-Yang grid
MPI layoutPhase A: rank 0 = Yin, rank 1 = Yangparallel/mpi_init.jl
Stokessolve_stokes_distributed!(...; mode = :phase_a), i.e. multigrid_stokes_mpi!solvers/solve_stokes_distributed.jl, solvers/multigrid_stokes_mpi.jl
inner solverFGMRES (tol 10−6, ≤ 300 iterations) (restart 50) with a geometric V-cycle, two pre- and two post-SCGS sweeps (multigrid_stokes_mpi.jl, not the 5/5 of the serial path), Galerkin coarse operators, mgLevels = 4Multigrid
patch coupling8 Jacobi-Schwarz rounds per solve with damped Anderson acceleration (MILEVA_NSCHWARZ)multigrid_stokes_mpi!
advectionsecond-order TVD (van Leer) on the grid, advect_temperature_3d_tvd!utils/advect_temperature.jl
diffusionbackward Euler, wall temperatures held at the faces through a half-cell ghostdiffuse_temperature(...; implicit = true)
temperature seamθ-wall cells overwritten with the peer's interpolated temperature after every stepyy_sync_T_walls_mpi! in parallel/yinyang_mpi.jl
time stepadvective CFL in all three directions, safety 0.4; dtMax = 10−4D2/κ for the conductive startcompute_timestep plus a longitude term in the driver

Why exactly two ranks. Among the modes of solve_stokes_distributed!, Phase A is the only one that solves Stokes with multigrid; the multi-rank-per-patch modes are MUMPS-direct (:direct, :b4p) or block-Jacobi Krylov (:iter, :rich). MILEVA's rule is that 3-D Stokes is always multigrid, so the driver refuses any other rank count and the job scales through Julia threads within each rank, which the SCGS smoother uses.

Initial condition. The spherical conductive profile plus a perturbation

T(r,n)=Tsurf+ΔT1/r−1/Rout1/Rin−1/Rout+Asin⁡(πξ)∑w=148sin⁡(kw⋅n+ϕw)24,ξ=r−RinD,

where n is the unit position vector in physical Cartesian coordinates, the 48 wavevectors kw have random directions and lengths between 2 and 12, and A = 20 K (MILEVA_SEED_AMP). Because the pattern is a function of physical position drawn from one seed, Yin and Yang start from the same field in their overlap, and because it has no preferred wavenumber the planform selects itself. The sin⁡(πξ) factor vanishes at both walls, so the wall heat flux starts at its conductive value: the smoke runs below start at Nu=1.00.

Diagnostics ​

Every MILEVA_LOGEVERY steps the driver appends to diagnostics_3d.csv:

columndefinition
t_difftime in units of D2/κ
Vrms_nd∫|v|2dV/∫dVD/κ, cell-centred velocities, dV=r2sin⁡θdrdθdϕ
Nu_top, Nu_botarea-weighted mean wall gradient (Tbc−Twallcell)/(half cell) over the spherical conductive gradient ΔT/[r2(1/Rin−1/Rout)] at the same wall
Tmean_ndvolume-mean (T−Tsurf)/ΔT
fgmres_iters, final_resinner FGMRES iterations and relative residual of the last Schwarz round
schwarz_ressmallest relative velocity change between Schwarz rounds; the solver returns that iterate
divMaxthe solver's own nondimensional divergence diagnostic (unpack_stokes_3d)

The sums run over both patches with one MPI.Allreduce. Yin and Yang overlap by about 6 % of the sphere and that overlap is counted twice, so these are patch-weighted means rather than exact sphere integrals.

Running it ​

The Rayleigh number and everything else are environment variables (the full list is in the driver header). Locally, with the mpiexec that MPI.jl ships:

bash
MILEVA_RA=1e5 JULIA_NUM_THREADS=4 \
  julia --project=. -e 'using MPI; run(`$(mpiexec()) -n 2 $(Base.julia_cmd()) --project=. examples/yinyang_convection_3d.jl`)'

On a cluster, the array script runs the three Rayleigh numbers and chains segments across the queue's time limit:

bash
julia --project=. hpc/plan_3d.jl 32           # memory per rank for each case
hpc/submit_3d_chain.sh 4                       # 4 chained 48 h segments of the array

A segment stops cleanly before its time limit (MILEVA_WALLTIME_H), writing a checkpoint per rank; the next resumes with MILEVA_RESUME=latest. A case that reaches its end (MILEVA_TMAX diffusion times, default 0.2, or MILEVA_MAXSTEPS) writes FINISHED in its output directory and later segments skip it. Open the .pvd in ParaView; each step is a .vtm holding the Yin and Yang .vts blocks, with fields T, eta, V and mpi_patch.

Cost ​

Measured on a laptop with 2 threads per rank:

grid per patchDOFs per patchStokes per stepFGMRES its per solveSchwarz residual
8 × 8 × 24 (smoke)6 5281.2 to 2.0 s34 to 352×10−3 to 3×10−3 (Ra 10⁴)
16 × 16 × 48 (smoke), 8 rounds50 68811 to 13 s36 to 379×10−3
16 × 16 × 48 (smoke), 4 rounds50 6886 s36 to 375×10−2

The first step of every run also pays about 20 s of compilation. Halving the Schwarz rounds halves the cost and leaves the seam five times less converged; in those four steps Vrms differed by under 1 %. The cost per Schwarz round is roughly linear in the DOF count, which puts the default grids at minutes per step on two threads per rank. The Ra = 10⁵ default grid has 1.8 million DOFs per patch and needs about 1.8 GB per rank. A short probe on the cluster gives the time per step:

bash
MILEVA_MAXSTEPS=5 MILEVA_LOGEVERY=1 sbatch --array=1 --time=02:00:00 hpc/yinyang_convection_3d.slurm

and scale the seconds per step it reports by the step count the CFL condition implies.

Notes ​

  • Temperature lives on the grid and is advected by TVD; there is no marker field in this example.

  • Both patches are written in full, so in ParaView the overlap shows two values at one place, and the diagnostics count the overlap twice, as described above. Next to a patch's φ-walls, read temperatures from the peer patch, which holds them in its interior. See Yin-Yang grid for how the patches are coupled.

Smoke test ​

A quick check on 2 ranks × 2 threads:

bash
MILEVA_NR=8 MILEVA_NTH=8 MILEVA_MGLEVELS=2 MILEVA_MAXSTEPS=3 MILEVA_LOGEVERY=1 \
  julia --project=. -e 'using MPI; run(`$(mpiexec()) -n 2 $(Base.julia_cmd()) --project=. examples/yinyang_convection_3d.jl`)'

3 steps at Ra = 10⁵ in 37 s of wall time. FGMRES converged every inner solve (34 to 35 iterations to a relative residual below 10−6); temperature and velocity were finite on both patches; Nu started at 1.00 and Vrms grew from 43 to 59 over the three steps as the perturbation began to grow. A resume from the step-3 checkpoint continued the PVD collection, a wall-time stop wrote a checkpoint and no FINISHED, and the dry run of hpc/yinyang_convection_3d.slurm through mpiexecjl ran and finished.