DMPlex (Unstructured Meshes + FEM)
DMPlex manages unstructured meshes and provides a complete finite element workflow: mesh creation, field discretisation, residual/Jacobian assembly via pointwise callbacks, boundary conditions, VTK output, and Lagrangian mesh advection.
Overview
The typical workflow is:
- Create a mesh — from a Gmsh file or a built-in box (or triangular) mesh.
- Set up finite elements — velocity, pressure, auxiliary fields.
- Define the PetscDS — attach pointwise residual/Jacobian callbacks.
- Set boundary conditions — Dirichlet via
add_boundary!. - Configure SNES — attach the FEM assembly and null spaces to solve nonlinear problems.
- Time loop — solve, update history fields, write VTK.
A complete, worked example is in examples/ex62b.jl (2D/3D Stokes with Maxwell viscoelasticity, power-law rheology, and Lagrangian advection).
Creating a Mesh
From a Gmsh file
using PETSc, MPI
MPI.Init()
petsclib = PETSc.getlib(; PetscScalar = Float64)
PETSc.initialize(petsclib)
dm = PETSc.DMPlex(petsclib, MPI.COMM_WORLD; dm_plex_filename = "mesh.msh")Any option accepted by DMSetFromOptions can be passed as a keyword argument. Useful options include -dm_refine N (uniform refinement) and -dm_plex_dim D to force the mesh dimension.
Box mesh (built-in)
dm = PETSc.DMPlex(petsclib, MPI.COMM_WORLD, 2, true, (10, 10))
# ^dim ^simplex ^facessimplex = true produces triangles (2D) / tetrahedra (3D); false gives quads / hexahedra.
Finite Element Setup
Creating FE spaces
dim = LibPETSc.DMGetDimension(petsclib, dm)
simplex = PETSc.issimplex(dm)
# P2 velocity (dim components, degree 2)
fe_vel = PETSc.fe_create_default(petsclib, MPI.COMM_SELF, dim, dim, simplex;
degree = 2, prefix = "vel_")
# Discontinuous P1 pressure (1 component, degree 1)
let opts = PETSc.PetscOptions(petsclib; pres_petscdualspace_lagrange_continuity = 0)
push!(opts)
global fe_pres = PETSc.fe_create_default(petsclib, MPI.COMM_SELF, dim, 1, simplex;
degree = 1, prefix = "pres_")
pop!(opts)
end
# Lagrange P1 (explicit degree, no PetscOptions lookup)
fe_p1 = PETSc.fe_create_lagrange(petsclib, MPI.COMM_SELF, dim, dim, simplex, 1)
# Copy quadrature rule from velocity to pressure for consistency
PETSc.copy_quadrature!(petsclib, fe_vel, fe_pres)Attaching FE spaces to the DM
PETSc.set_field!(dm, 0, fe_vel) # field 0 = velocity
PETSc.set_field!(dm, 1, fe_pres) # field 1 = pressure
PETSc.create_ds!(dm) # finalise the PetscDSNaming objects
LibPETSc.PetscObjectSetName(petsclib, convert(Ptr{Cvoid}, fe_vel), "velocity")
PETSc.set_name!(petsclib, dm, "stokes")Pointwise Callbacks
PETSc's FEM assembly calls user-supplied C function pointers at each quadrature point. Three macros generate the required @cfunction wrappers from plain Julia functions.
@simple_fn — boundary / exact-solution functions
Signature: f(t, x, u, ctx) where x is the coordinate vector and u is the output vector.
function bg_vel(t, x, u, ctx)
u[1] = x[1] * 0.1 # Vx = exx * x
u[2] = x[2] * (-0.1) # Vy = eyy * y
end
const bg_vel_ptr = PETSc.@simple_fn(bg_vel)@residual_fn — f0 / f1 residual and post-processing functions
Signature: full PETSc pointwise signature with dim_, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, aOff, aOff_x, a, a_t, a_x, t, x, nC, cst, out. The second argument is the number of output components.
function f0_p(dim_, Nf, NfAux, uOff, uOff_x, u, u_t, u_x,
aOff, aOff_x, a, a_t, a_x, t, x, nC, cst, f0)
f0[1] = 0.0
for d in 0:dim_-1; f0[1] -= u_x[d*dim_+d+1]; end
end
const f0_p_ptr = PETSc.@residual_fn(f0_p, 1)@jacobian_fn — g0/g1/g2/g3 Jacobian functions
Same signature as @residual_fn but with an extra utShift argument before x.
function g1_pu(dim_, Nf, NfAux, uOff, uOff_x, u, u_t, u_x,
aOff, aOff_x, a, a_t, a_x, t, utShift, x, nC, cst, g1)
for d in 0:dim_-1; g1[d*dim_+d+1] = -1.0; end
end
const g1_pu_ptr = PETSc.@jacobian_fn(g1_pu, dim_*dim_)Jacobian callbacks can be computed automatically using ForwardDiff:
function g3_uu(dim_, Nf, NfAux, uOff, uOff_x, u, u_t, u_x, ...)
deviatoric_stress_tangent!(g3, dim_, u_x, ...) # uses ForwardDiff internally
endSee examples/ex62b.jl for a complete example.
PetscDS: Residuals, Jacobians, and Constants
ds = PETSc.ds(dm)
# Residual callbacks (f0 = volume source, f1 = flux)
PETSc.set_residual!(ds, 0, f0_u_ptr, f1_u_ptr) # momentum
PETSc.set_residual!(ds, 1, f0_p_ptr, C_NULL) # continuity
# Jacobian blocks (fieldI, fieldJ, g0, g1, g2, g3)
PETSc.set_jacobian!(ds, 0, 0, C_NULL, C_NULL, C_NULL, g3_uu_ptr)
PETSc.set_jacobian!(ds, 0, 1, C_NULL, C_NULL, g2_up_ptr, C_NULL)
PETSc.set_jacobian!(ds, 1, 0, C_NULL, g1_pu_ptr, C_NULL, C_NULL)
# Preconditioner Jacobian (can differ from the true Jacobian)
PETSc.set_jacobian_preconditioner!(ds, 1, 1, g0_pp_ptr, C_NULL, C_NULL, C_NULL)
# Exact solution (for manufactured solution tests / Dirichlet BCs)
PETSc.set_exact_solution!(ds, 0, exact_vel_ptr)
PETSc.set_exact_solution!(ds, 1, exact_pres_ptr)
# Constants (read by all callbacks as the `cst` argument)
PETSc.set_constants!(ds, [mu, rho, gravity, dt])Boundary Conditions
# Get the boundary label (created by Gmsh or DMPlexCreateBoxMesh)
label = PETSc.label(dm, "Face Sets")
# Dirichlet (essential) BC on velocity (field 0), boundary tag 1
PETSc.add_boundary!(dm,
LibPETSc.DM_BC_ESSENTIAL, "wall", label,
PetscInt[1], # boundary tag values
0, # field index
PetscInt[], # components (empty = all)
exact_vel_ptr)
# Split a Gmsh "boundary" label into per-face labels (for box meshes)
PETSc.create_split_boundary_labels!(dm)Auxiliary Fields
Auxiliary fields carry per-cell data (phase, history stress, etc.) that callbacks read from the a / aOff arguments.
# Clone the primary DM and set up DG-P0 auxiliary fields
dm_aux = PETSc.clone(dm)
fe_phase = PETSc.fe_create_default(petsclib, MPI.COMM_SELF, dim, 1, simplex;
degree = 0, prefix = "phase_")
fe_tau = PETSc.fe_create_default(petsclib, MPI.COMM_SELF, dim, dim*dim, simplex;
degree = 0, prefix = "tau_")
PETSc.set_field!(dm_aux, 0, fe_phase)
PETSc.set_field!(dm_aux, 1, fe_tau)
PETSc.create_ds!(dm_aux)
aux_vec = PETSc.global_vec(dm_aux)
# Attach aux_vec so callbacks see it via `a`
LibPETSc.DMSetAuxiliaryVec(petsclib, dm,
LibPETSc.DMLabel(C_NULL), PetscInt(0), PetscInt(0), aux_vec)Projection and L² Norms
# Project exact functions onto a DM vector (initialisation / BCs)
PETSc.project_function!(u, dm, 0.0, [vel_fn_ptr, pres_fn_ptr], nothing, LibPETSc.INSERT_ALL_VALUES)
# Project residual-style callbacks onto a DM vector (post-processing)
PETSc.project_field!(out_vec, dm_out, t, u, [copy_vel_ptr, copy_pres_ptr, compute_tau_3x3_ptr], LibPETSc.INSERT_ALL_VALUES)
# L² error vs. exact solution
err = PETSc.l2diff(dm, t, [exact_vel_ptr, exact_pres_ptr], nothing, u)SNES Integration and Null Spaces
# Wire FEM residual/Jacobian assembly into the SNES
PETSc.set_snes_local_fem!(dm)
# Constant-pressure null space (for incompressible flow)
null_vec = PETSc.global_vec(dm)
PETSc.project_function!(null_vec, dm, 0.0, [zero_vel_ptr, one_pres_ptr], nothing, LibPETSc.INSERT_ALL_VALUES)
LibPETSc.VecNormalize(petsclib, null_vec)
nullspace = GC.@preserve null_vec PETSc.mat_nullspace_create(petsclib, comm, (null_vec,))
PETSc.set_jacobian_nullspace!(snes, nullspace)
# Attach constant null space directly to the pressure FE
PETSc.compose_constant_nullspace!(petsclib, comm, fe_pres)
# Propagate discretisation to coarser MG levels
let cdm = dm
while convert(Ptr{Cvoid}, cdm) != C_NULL
PETSc.copy_disc!(dm, cdm)
cdm = PETSc.coarse_dm(cdm)
end
end
# Cleanup
PETSc.destroy!(petsclib, nullspace)VTK Output
# Write a named vector to a VTU file (merges parallel pieces automatically)
PETSc.save_vtk(out_vec, "solution.vtu")
# Mark tensor fields so ParaView shows them as tensors
PETSc.vtk_merge_tensor(fname, "strainrate", "tau")save_vtk calls PetscViewerVTKOpen + DMView / VecView internally and works in parallel (each rank writes its own piece; PETSc merges the XML). vtk_merge_tensor post-processes the XML header to annotate multiple tensor fields so ParaView's tensor glyph filter recognises them.
Writing a ParaView PVD animation file
pvd_entries = Tuple{Float64,String}[]
for step in 1:nsteps
# ... solve ...
fname = "out_$(lpad(step, 4, '0')).vtu"
PETSc.save_vtk(out_vec, fname)
push!(pvd_entries, (t, abspath(fname)))
# rewrite PVD after every step so it is always playable
open("sim.pvd", "w") do io
println(io, """<?xml version="1.0"?>""")
println(io, """<VTKFile type="Collection" version="0.1" byte_order="LittleEndian">""")
println(io, " <Collection>")
for (ts, vtu) in pvd_entries
rel = relpath(vtu, dirname(abspath("sim.pvd")))
println(io, """ <DataSet timestep="$ts" group="" part="0" file="$rel"/>""")
end
println(io, " </Collection>")
println(io, "</VTKFile>")
end
endLagrangian Mesh Advection
For moving-mesh simulations, update the coordinate vector directly:
# Project P2 velocity onto P1 (vertex-based) to get nodal velocities
dm_p1 = PETSc.clone(dm)
PETSc.set_field!(dm_p1, 0, PETSc.fe_create_lagrange(petsclib, MPI.COMM_SELF, dim, dim, simplex, 1))
PETSc.create_ds!(dm_p1)
vel_p1 = PETSc.global_vec(dm_p1)
PETSc.project_field!(vel_p1, dm_p1, 0.0, u, [copy_vel_ptr], LibPETSc.INSERT_ALL_VALUES)
# Move each mesh node by dt * v (the coordinate vector is owned by the DM)
coords = LibPETSc.DMGetCoordinates(petsclib, dm)
LibPETSc.VecAXPY(petsclib, coords, PetscScalar(dt), vel_p1)Pure Lagrangian advection distorts the mesh over time, degrading solver convergence. For background-deformation (bg) setups the mesh does not need to move at all. For large-displacement grav problems, periodic remeshing is needed for long runs.
Examples
ex17.jl — Linear Elasticity
examples/ex17.jl is a Julia port of PETSc's snes/tutorials/ex17.c. It solves linear isotropic elasticity −∇·σ(u) = f on (0,1)^dim with manufactured exact solutions, demonstrating:
- 2D/3D box meshes (simplex triangles/tetrahedra or quad/hex elements)
- Multiple solution types including uniform strain, axial displacement, and geological shear
- Direct, GAMG, and geometric multigrid solvers
- Near-null space (rigid-body modes) for AMG
# 2D Q1 quad mesh, quadratic MMS, direct solver
julia --project examples/ex17.jl \
-dm_plex_simplex 0 -dm_plex_box_faces 4,4 \
-sol_type elas_quad -displacement_petscspace_degree 1 -pc_type lu
# GAMG with rigid-body near-null space (recommended for large problems)
julia --project examples/ex17.jl \
-dm_plex_simplex 0 -dm_plex_box_faces 8,8 \
-sol_type elas_quad -pc_type gamg -ksp_type cg
# Geometric multigrid — 3 refinement levels, 3D
julia --project examples/ex17.jl \
-dm_plex_dim 3 -dm_plex_simplex 0 -dm_plex_box_faces 2,2,2 \
-dm_refine_hierarchy 3 -sol_type elas_quad \
-pc_type mg -pc_mg_type full \
-mg_levels_ksp_type chebyshev -mg_levels_pc_type jacobi -ksp_type cg
# MPI — 4 ranks
mpiexec -n 4 julia --project examples/ex17.jl \
-dm_plex_simplex 0 -dm_plex_box_faces 8,8 \
-sol_type elas_quad -pc_type gamg -ksp_type cgex62.jl — Isoviscous Stokes (MMS verification)
examples/ex62.jl is a Julia port of PETSc's snes/tutorials/ex62.c. It solves constant-viscosity Stokes on the unit square/cube with polynomial or trigonometric manufactured exact solutions, used for convergence verification of the P2/P1 and Q2/Q1 element pairs.
# 2D P2/P1 simplex, quadratic MMS — L² ≈ machine precision (exact for P2)
julia --project examples/ex62.jl -sol quadratic \
-vel_petscspace_degree 2 -pres_petscspace_degree 1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu -ksp_rtol 1e-9
# 2D Q2/Q1 quad, trig MMS, refinement convergence study
julia --project examples/ex62.jl -sol trig \
-dm_plex_simplex 0 -dm_refine 2 \
-vel_petscspace_degree 2 -pres_petscspace_degree 1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu -ksp_rtol 1e-9
# 3D Q2/Q1 hex, quadratic MMS
julia --project examples/ex62.jl -sol quadratic \
-dm_plex_dim 3 -dm_plex_simplex 0 -dm_plex_box_faces 3,3,3 \
-vel_petscspace_degree 2 -pres_petscspace_degree 1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu -ksp_rtol 1e-9ex62.jl uses a single boundary label (id=1) covering the entire box boundary — do not pass -dm_plex_separate_marker.
ex69.jl — Variable-Viscosity Stokes
examples/ex69.jl is a Julia port of PETSc's snes/tutorials/ex69.c. It solves variable-viscosity Stokes on (0,1)² with free-slip walls, using analytical benchmark solutions (solkx with exponential viscosity variation, solcx with a piecewise-constant jump), for convergence rate verification.
# Q2/Q1 quad, SolKx (exponential viscosity)
julia --project examples/ex69.jl \
-dm_plex_simplex 0 -dm_plex_separate_marker \
-vel_petscspace_degree 2 -pres_petscspace_degree 1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu -ksp_rtol 1e-9
# SolCx with viscosity jump etaB=1000
julia --project examples/ex69.jl \
-dm_plex_simplex 0 -dm_plex_separate_marker \
-sol_type solcx -etaB 1e3 \
-vel_petscspace_degree 2 -pres_petscspace_degree 1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu -ksp_rtol 1e-9
# Refinement study — 2 uniform refinements, Q2/Q1
julia --project examples/ex69.jl \
-dm_plex_simplex 0 -dm_plex_separate_marker -dm_refine 2 \
-vel_petscspace_degree 2 -pres_petscspace_degree 1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu -ksp_rtol 1e-9
# MPI — 4 ranks, SolCx, viscosity jump 1000
mpiexec -n 4 julia --project examples/ex69.jl \
-dm_plex_simplex 0 -dm_plex_separate_marker -dm_refine 1 \
-sol_type solcx -etaB 1e3 \
-vel_petscspace_degree 2 -pres_petscspace_degree 1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu -ksp_rtol 1e-9Expected convergence rates (L² velocity+pressure combined):
- Q2/Q1 quad: ~4× per refinement (O(h²), pressure-dominated)
- Q1/P0 quad: ~2× per refinement (O(h), velocity-dominated)
Examples: Stokes Flow (ex62b.jl)
examples/ex62b.jl solves the incompressible Stokes equations on a square/cube domain containing a circular/spherical inclusion meshed with Gmsh. It supports four solution types (-sol quadratic|trig|bg|grav), power-law and Maxwell viscoelastic rheology, time stepping, and VTK/PVD output.
Solver options
Three solver families are available, trading exactness for scalability:
| Label | Velocity block | Pressure block | Best for |
|---|---|---|---|
| (A) Direct | LU (lu) | LU | Small 2D problems, exact reference |
| (B) GAMG | CG + GAMG | Jacobi | Large 2D, 3D, moderate viscosity contrast |
| (C) GMG | FGMRES + geometric MG | Jacobi | 3D with coarse base mesh + refinement hierarchy |
All three use a Schur-complement fieldsplit preconditioner:
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur
-pc_fieldsplit_schur_factorization_type full
-pc_fieldsplit_schur_precondition a112D examples
Pure shear, direct solver (quick test, serial):
julia --project=examples examples/ex62b.jl \
-sol bg -exx_bg 1.0 -eyy_bg -1.0 -mu_inclusion 0.1 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu \
-ksp_rtol 1e-9 -snes_rtol 1e-9 -vtk_output out.vtuGravity-driven sinking cylinder, viscous, direct solver (20 time steps):
julia --project=examples examples/ex62b.jl \
-sol grav -mesh_h 0.05 -mesh_h_inclusion 0.02 \
-rho_bg 1.0 -rho_inclusion 2.0 -gravity 1.0 -mu_inclusion 0.1 \
-dt 0.02 -nsteps 20 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu \
-ksp_rtol 1e-9 -snes_rtol 1e-9 -vtk_output grav_viscous.vtuGravity-driven sinking cylinder, Maxwell viscoelastic (τ_rel = η/G = 0.2):
julia --project=examples examples/ex62b.jl \
-sol grav -mesh_h 0.05 -mesh_h_inclusion 0.02 \
-rho_bg 1.0 -rho_inclusion 2.0 -gravity 1.0 -mu_inclusion 0.1 \
-G_bg 5.0 -G_inclusion 0.5 -dt 0.01 -nsteps 50 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu \
-ksp_rtol 1e-9 -snes_rtol 1e-9 -vtk_output grav_viscoelastic.vtuPure shear, power-law inclusion (n=3), viscoelastic matrix (100 steps, GAMG):
julia --project=examples examples/ex62b.jl \
-sol bg -exx_bg 1.0 -eyy_bg -1.0 \
-mesh_h 0.04 -mesh_h_inclusion 0.015 \
-mu_inclusion 0.1 -n_inclusion 3.0 -eps0_inclusion 0.5 \
-G_bg 10.0 -G_inclusion 1.0 -dt 0.005 -nsteps 100 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_ksp_type cg -fieldsplit_velocity_pc_type gamg \
-fieldsplit_pressure_ksp_type preonly -fieldsplit_pressure_pc_type jacobi \
-ksp_type fgmres -ksp_rtol 1e-6 -snes_rtol 1e-6 -snes_max_it 20 \
-vtk_output shear_powerlaw.vtuAnalytical viscoelastic verification — Maxwell stress build-up, τ_rel = η/G = 1:
julia --project=examples examples/ex62b.jl \
-sol bg -exx_bg 0.1 -eyy_bg -0.1 \
-mesh_h 0.5 -mu_inclusion 1.0 -G_bg 1.0 -G_inclusion 1.0 \
-dt 0.1 -nsteps 30 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_pc_type lu \
-fieldsplit_pressure_ksp_rtol 1e-9 -fieldsplit_pressure_pc_type lu \
-ksp_rtol 1e-9 -snes_rtol 1e-9The code prints the numerical τ_II alongside the continuous and discrete-BE analytical solutions at each step so convergence can be verified.
3D examples
Add -dm_plex_dim 3 to switch to a sphere-in-cube geometry. The z strain rate (ezz) is set automatically to -(exx+eyy) to enforce incompressibility.
3D pure shear, algebraic multigrid (GAMG):
julia --project=examples examples/ex62b.jl \
-dm_plex_dim 3 -sol bg -exx_bg 1.0 -eyy_bg -0.5 -mu_inclusion 0.01 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_ksp_type cg -fieldsplit_velocity_pc_type gamg \
-fieldsplit_pressure_ksp_type preonly -fieldsplit_pressure_pc_type jacobi \
-ksp_type fgmres -ksp_rtol 1e-6 -snes_rtol 1e-6 -vtk_output out3d.vtu3D pure shear, geometric multigrid (coarse mesh h=0.4, 2 uniform refinements → 3 MG levels):
julia --project=examples examples/ex62b.jl \
-dm_plex_dim 3 -sol bg -exx_bg 1.0 -eyy_bg -0.5 -mu_inclusion 0.01 \
-mesh_h 0.4 -dm_refine 2 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_ksp_type fgmres -fieldsplit_velocity_pc_type mg \
-fieldsplit_velocity_mg_levels_ksp_type chebyshev \
-fieldsplit_velocity_mg_levels_pc_type sor \
-fieldsplit_velocity_mg_coarse_pc_type lu \
-fieldsplit_pressure_ksp_type preonly -fieldsplit_pressure_pc_type jacobi \
-ksp_type fgmres -ksp_rtol 1e-6 -snes_rtol 1e-6 -vtk_output out3d_mg.vtuWith -dm_refine N, PETSc builds a refinement hierarchy inside the preconditioner only. The SNES still assembles on the coarse Gmsh mesh (-mesh_h), so solution accuracy is at mesh_h resolution. Use a smaller mesh_h for finer physics; -dm_refine only controls the number of MG levels. GAMG (algebraic MG) requires no mesh hierarchy and is generally preferred for moderate viscosity contrasts.
3D gravity-driven sinking sphere, viscoelastic, GAMG (30 time steps):
julia --project=examples examples/ex62b.jl \
-dm_plex_dim 3 -sol grav \
-mesh_h 0.3 -mesh_h_inclusion 0.12 \
-rho_bg 1.0 -rho_inclusion 2.0 -gravity 1.0 -mu_inclusion 0.1 \
-G_bg 5.0 -G_inclusion 0.5 -dt 0.02 -nsteps 30 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_ksp_type cg -fieldsplit_velocity_pc_type gamg \
-fieldsplit_pressure_ksp_type preonly -fieldsplit_pressure_pc_type jacobi \
-ksp_type fgmres -ksp_rtol 1e-6 -snes_rtol 1e-6 -vtk_output sphere_sinking.vtuRunning in parallel with MPI
Every example above runs in parallel without code changes — just prepend mpiexec -n N:
mpiexec -n 4 julia --project=examples examples/ex62b.jl \
-dm_plex_dim 3 -sol grav \
-mesh_h 0.2 -mesh_h_inclusion 0.08 \
-rho_bg 1.0 -rho_inclusion 2.0 -gravity 1.0 -mu_inclusion 0.1 \
-G_bg 5.0 -G_inclusion 0.5 -dt 0.02 -nsteps 30 \
-pc_use_amat -pc_type fieldsplit -pc_fieldsplit_type schur \
-pc_fieldsplit_schur_factorization_type full -pc_fieldsplit_schur_precondition a11 \
-fieldsplit_velocity_ksp_type cg -fieldsplit_velocity_pc_type gamg \
-fieldsplit_pressure_ksp_type preonly -fieldsplit_pressure_pc_type jacobi \
-ksp_type fgmres -ksp_rtol 1e-6 -snes_rtol 1e-6 -vtk_output sphere_sinking.vtuThe mesh is partitioned automatically by PETSc's DMPlex. The direct solver (-fieldsplit_velocity_pc_type lu) works in serial only; use GAMG or GMG for parallel runs. VTK output is written per-rank and merged into a single XML collection automatically by save_vtk.
For large HPC runs, see Running on HPC Systems for MPI launch syntax on different cluster schedulers.
API Reference
PETSc.DMPlex — Method
DMPlex(
petsclib::PetscLib,
comm::MPI.Comm,
dim::Integer,
simplex::Bool,
faces::AbstractVector{<:Integer};
lower = ntuple(_ -> 0.0, dim),
upper = ntuple(_ -> 1.0, dim),
periodicity = ntuple(_ -> DM_BOUNDARY_NONE, dim),
interpolate = true,
localize_height = 0,
sparse_localize = false,
setfromoptions = true,
dmsetup = false,
prefix = "",
options...,
)Create a structured-grid box DMPLEX mesh in dim dimensions with faces[d] cells along axis d. When simplex == true each box cell is split into simplices (triangles in 2D, tetrahedra in 3D); otherwise tensor-product cells are used.
External Links
- PETSc Manual:
DMPlex/DMPlexCreateBoxMesh
PETSc.DMPlex — Method
DMPlex(
petsclib::PetscLib,
comm::MPI.Comm;
setfromoptions = true,
dmsetup = false,
prefix = "",
options...,
)Create an empty DMPLEX mesh. Use the keyword options... (or PETSc command-line options) such as dm_plex_box_faces, dm_plex_dim, dm_plex_simplex, dm_plex_filename, etc. to populate the mesh — this is the most flexible entry point and matches the DMCreate + DMSetType(DMPLEX) + DMSetFromOptions flow used in PETSc's tutorials (e.g. snes/ex12.c).
For an explicit box mesh, use the DMPlex method taking (petsclib, comm, dim, simplex, faces; ...), below.
External Links
- PETSc Manual:
DMPlex/DMPlexCreate
- PETSc Manual:
DM/DMSetType
- PETSc Manual:
DM/DMSetFromOptions
PETSc.add_boundary! — Function
add_boundary!(dm, bctype, name, label, values, field, comps,
bcfunc_ptr, bcfunc_t_ptr = C_NULL, ctx = C_NULL)Attach a boundary condition to dm, and return dm. LibPETSc.DMAddBoundary also returns the number PETSc gives the boundary. label is a Ptr{Cvoid} from label. bcfunc_ptr / bcfunc_t_ptr are @cfunction(...) results matching PETSc's boundary callback signature.
petsclib is not an argument: the dm carries it as a type parameter (§8). The v0.4 form with petsclib first still works in v0.5 and warns.
The values are label values PETSc issued and field is the number set_field! gave the field, so both keep PETSc's numbering (§12.1).
External Links
- PETSc Manual:
DM/DMAddBoundary
PETSc.add_coarsen_hook! — Function
add_coarsen_hook!(coarsenhook, dm, restricthook = C_NULL)Register a C callback invoked each time dm is coarsened (e.g. during FAS hierarchy setup). The callback comes first (§8.1), so dm is argument 2. The coarsenhook signature is:
hook(fine::Ptr{Cvoid}, coarse::Ptr{Cvoid}, ctx::Ptr{Cvoid}) → CintUse @cfunction(f, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid})) to create the pointer. restricthook (optional) is called on each nonlinear solve restriction step.
PETSc.add_natural_boundary! — Function
add_natural_boundary!(dm, ds, name, label, label_value, field, f0_ptr, f1_ptr = C_NULL)Register a natural (Neumann) boundary condition on dm following the PETSc 3.22+ pattern used in the C tutorials, and return dm. Equivalent to:
DMAddBoundary(dm, DM_BC_NATURAL, name, label, 1, &val, field, 0, NULL, NULL, NULL, NULL, &bd);
PetscDSGetBoundary(ds, bd, &wf, NULL, ...);
PetscWeakFormSetIndexBdResidual(wf, label, val, field, 0, 0, f0, 0, NULL);f0_ptr is a @bd_fn-generated C-callable function pointer implementing the boundary integrand (PetscBdPointFn signature, with the outward normal n[] between x[] and numConstants).
petsclib is not an argument: the dm carries it as a type parameter (§8). The v0.4 form with petsclib first still works in v0.5 and warns.
External Links
- PETSc Manual:
DM/DMAddBoundary
- PETSc Manual:
Dm/PetscWeakFormSetIndexBdResidual
PETSc.clone — Method
clone(dm::AbstractPetscDM) -> AbstractPetscDMReturn a new DM that is a clone of dm (same topology, no fields or DS).
The clone is a new object the caller owns, and comes back narrowed to its flavour.
External Links
- PETSc Manual:
DM/DMClone
PETSc.coarse_dm — Function
coarse_dm(dm::AbstractPetscDM) -> AbstractPetscDMReturn the coarse DM from which dm was obtained by refinement (e.g. via -dm_refine_hierarchy). Check convert(Ptr{Cvoid}, cdm) == C_NULL to detect when there is no coarser level.
The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).
External Links
- PETSc Manual:
DM/DMGetCoarseDM
PETSc.compose_constant_nullspace! — Function
compose_constant_nullspace!(petsclib, comm, fe)Create a trivial (constant) MatNullSpace and attach it to the FE object fe under the key "nullspace" via PetscObjectCompose. This signals to the fieldsplit preconditioner that the field has a constant null space.
PETSc.copy_disc! — Function
copy_disc!(src::AbstractPetscDM, dst::AbstractPetscDM)Copy the discretisation (fields, PetscDS, BCs) from src to dst. Useful when propagating FEM setup to coarser levels of a multigrid hierarchy.
External Links
- PETSc Manual:
DM/DMCopyDisc
PETSc.copy_quadrature! — Method
copy_quadrature!(petsclib, src_fe, dst_fe)Copy the quadrature rule from src_fe to dst_fe so both fields share the same integration points.
PETSc.create_ds! — Method
create_ds!(dm::AbstractPetscDM)Build the PetscDS for dm from the currently-attached fields.
External Links
- PETSc Manual:
DM/DMCreateDS
PETSc.create_split_boundary_labels! — Function
create_split_boundary_labels!(dm)Split the single "marker" label on a 2D box mesh into four per-wall labels ("markerBottom", "markerRight", "markerTop", "markerLeft", marker IDs 1–4) so that corner DOFs can belong to multiple wall labels simultaneously.
PETSc.destroy! — Method
destroy!(nullsp::LibPETSc.MatNullSpace)
destroy!(petsclib, nullsp::LibPETSc.MatNullSpace)Destroy a MatNullSpace created by mat_nullspace_create.
MatNullSpace is a bare Ptr and carries no library, so the one-argument form uses the default library. Pass petsclib explicitly when several are in use.
External Links
- PETSc Manual:
Mat/MatNullSpaceDestroy
PETSc.distribute! — Method
distribute!(dm::DMPlex; overlap = 0) -> Union{Nothing, DMPlex}Distribute the (serial) DMPLEX dm across the communicator with the given point-overlap. Returns the new distributed DM on the owning communicator, or nothing if no redistribution occurred (e.g. serial run).
The original dm is not destroyed — the caller is responsible for that.
External Links
- PETSc Manual:
DMPlex/DMPlexDistribute
PETSc.ds — Function
ds(dm::AbstractPetscDM) -> PetscDSReturn the PetscDS (discrete system) attached to dm.
The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).
External Links
- PETSc Manual:
DM/DMGetDS
PETSc.fe_create_default — Method
fe_create_default(petsclib, comm, dim, Nc, simplex; degree = 1, prefix = "", qorder = -1)Create a default PetscFE (Lagrange) for a single field with Nc components on either a simplex (simplex == true) or tensor cell.
degree is the polynomial degree (1 = P1/Q1, 2 = P2/Q2, etc.). It is injected into the options database under prefix as -${prefix}petscspace_degree before the call to PetscFECreateDefault, so any options explicitly set by the user will take precedence.
External Links
- PETSc Manual:
FE/PetscFECreateDefault
PETSc.fe_create_lagrange — Method
fe_create_lagrange(petsclib, comm, dim, Nc, simplex, degree; qorder = -1)Create a PetscFE Lagrange element of polynomial degree degree for a single field with Nc components on either a simplex (simplex == true) or tensor cell. Unlike fe_create_default, the degree is specified explicitly and does not depend on the options database.
External Links
- PETSc Manual:
FE/PetscFECreateLagrange
PETSc.issimplex — Method
issimplex(dm::DMPlex) -> BoolReturn true when the cells of dm (assumed to be a DMPLEX) are simplices.
External Links
- PETSc Manual:
DMPlex/DMPlexIsSimplex
PETSc.l2diff — Function
l2diff(dm, time, funcs, ctxs, X) -> PetscRealCompute the L² error between the global vector X and the pointwise exact functions funcs (one per field, matching PetscSimplePointFn signature). ctxs is a matching vector of context pointers, or nothing to use C_NULL for every field. Requires that set_exact_solution! has been called.
petsclib is not an argument: the dm carries it as a type parameter (§8). The v0.4 name dm_compute_l2diff shims the old form.
External Links
- PETSc Manual:
DM/DMComputeL2Diff
PETSc.label — Function
label(dm::AbstractPetscDM, name::AbstractString) -> Ptr{Cvoid}Look up a DMLabel on dm by name and return its raw pointer (Ptr{Cvoid}). Returns C_NULL if no label with that name exists.
External Links
- PETSc Manual:
DM/DMGetLabel
PETSc.mat_nullspace_create — Function
mat_nullspace_create(petsclib, comm, vecs) -> MatNullSpaceCreate a null space spanned by the given vectors. Each element of vecs must have a .ptr field holding the underlying PETSc Vec handle (CVec). The vectors should be orthonormal; call VecNormalize beforehand if needed.
PETSc.mat_nullspace_create — Method
mat_nullspace_create(petsclib, comm; has_const = true) -> MatNullSpaceCreate a null space containing the constant vector (Neumann / saddle-point problems).
PETSc.project_field! — Function
project_field!(X, dm, time, U, funcs, mode)Project a function of the fields in the input vector U into the FE space of dm, writing the result into X.
The written vector comes first and petsclib is gone (§8); v0.4 took X last and petsclib first. A shim forwards the v0.4 name dm_project_field! from the old order (§17.2). funcs is a vector of Ptr{Cvoid} function pointers (one per field in dm), each with the PetscPointFn / @residual_fn signature. U must be associated with a DM that shares the same mesh as dm (e.g. obtained via clone).
Use this to compute derived quantities (e.g. stress from displacement gradient) and project them onto a new FE field for visualisation.
External Links
- PETSc Manual:
DM/DMProjectField
PETSc.project_function! — Function
project_function!(X, dm, time, funcs, ctxs, mode)Project a collection of pointwise functions into the global vector X.
The written vector comes first and petsclib is gone (§8); v0.4 took X last and petsclib first. A shim forwards the v0.4 name dm_project_function! from the old order (§17.2). funcs is a vector of @cfunction pointers (one per field) matching PETSc's PetscSimplePointFn signature. ctxs is a matching vector of context pointers, or nothing to use C_NULL for every field.
mode is an InsertMode enum value, typically INSERT_ALL_VALUES (sets constrained DOFs too) or INSERT_VALUES (free DOFs only).
External Links
- PETSc Manual:
DM/DMProjectFunction
PETSc.save_vtk — Function
save_vtk(vec::AbstractPetscVec, filename)
save_vtk(vecs, filename)Write the global vector vec to a VTK unstructured-grid file (.vtu), readable by ParaView and VisIt. Works for both serial and MPI runs; PETSc gathers all ranks into a single file. The filename must end in .vtu. The mesh geometry is taken from the DM attached to vec by PETSc.
The second form takes any iterable of AbstractPetscVec and writes each as a separate point-data array in one file. The field name shown in ParaView/VisIt comes from the name set on the vector via set_name!.
Neither petsclib nor comm is an argument: the vector carries the library as a type parameter and PETSc is asked for its communicator (§8).
vtk_save! and vtk_save_fields! were the two v0.4 names for these two methods, and both shim the old argument list.
External Links
- PETSc Manual:
Viewer/PetscViewerVTKOpen
PETSc.set_auxiliary_vec! — Method
set_auxiliary_vec!(dm, aux_local)Attach a local auxiliary vector aux_local to dm (global label / value 0 / part 0). The auxiliary field values are forwarded to all pointwise functions as the a argument.
PETSc.set_constants! — Method
set_constants!(ds::PetscDS, constants)Set the named constants (accessible as constants[i] in pointwise functions) on the PetscDS ds. constants must be an AbstractVector whose elements are convertible to PetscScalar.
External Links
- PETSc Manual:
Dm/PetscDSSetConstants
PETSc.set_exact_solution! — Method
set_exact_solution!(ds, field, sol_ptr, ctx = C_NULL)Register the pointwise exact-solution function sol_ptr for field field (0-based) of the PetscDS ds. The function pointer must match PETSc's PetscSimplePointFn / PetscErrorCode f(dim, time, x, Nc, u, ctx) signature.
This is required for DMComputeL2Diff and DMComputeExactSolution to work.
External Links
- PETSc Manual:
Dm/PetscDSSetExactSolution
PETSc.set_field! — Function
set_field!(dm::AbstractPetscDM, field::Integer, fe; label = C_NULL)Attach a PetscFE (or other discretization object) as the field-th field of dm (0-based field index, matching PETSc).
External Links
- PETSc Manual:
DM/DMSetField
PETSc.set_jacobian! — Method
set_jacobian!(ds, fieldI, fieldJ, g0_ptr, g1_ptr, g2_ptr, g3_ptr)Attach the pointwise Jacobian point-function quadruple for the block (fieldI, fieldJ) of the PetscDS ds. Pass C_NULL for any unused term.
External Links
- PETSc Manual:
Dm/PetscDSSetJacobian
PETSc.set_jacobian_nullspace! — Function
set_jacobian_nullspace!(snes, nullsp)Retrieve the assembled Jacobian matrix from snes (after SNESSetUp) and attach nullsp to it. Must be called after SNESSetUp and before SNESSolve.
PETSc.set_jacobian_preconditioner! — Method
set_jacobian_preconditioner!(ds, field_i, field_j, g0, g1, g2, g3)Set the Jacobian preconditioner terms for the weak form between fields field_i and field_j. Each gN argument is either a Ptr{Cvoid} function pointer or C_NULL.
This is the preconditioner equivalent of set_jacobian!.
External Links
- PETSc Manual:
Dm/PetscDSSetJacobianPreconditioner
PETSc.set_name! — Method
set_name!(petsclib, obj, name)Set the name of any PETSc object (DM, Vec, FE, …) to name. obj can be any pointer type that is convertible to Ptr{Cvoid}.
Thin wrapper around PetscObjectSetName that avoids needing an explicit convert(Ptr{Cvoid}, obj) at the call site.
External Links
- PETSc Manual:
Sys/PetscObjectSetName
PETSc.set_nullspace! — Method
set_nullspace!(mat, nullsp)Attach nullsp to mat so the linear solver removes it each iteration.
PETSc.set_residual! — Method
set_residual!(ds, field, f0_ptr, f1_ptr)Attach the pointwise residual point-function pair to field field (0-based) of the PetscDS ds. f0_ptr and f1_ptr must be @cfunction(...)-style function pointers (Ptr{Cvoid}) matching PETSc's PetscPointFn signature.
External Links
- PETSc Manual:
Dm/PetscDSSetResidual
PETSc.set_snes_local_fem! — Function
set_snes_local_fem!(dm; use_obj = false, ctx = C_NULL)Tell SNES to use DMPlex's built-in FEM residual / Jacobian assembly on dm.
petsclib is not an argument: the dm carries it as a type parameter (§8). The v0.4 name plex_set_snes_local_fem! shims the old form.
External Links
- PETSc Manual:
DMPlex/DMPlexSetSNESLocalFEM
PETSc.vtk_merge_tensor — Method
vtk_merge_tensor(fname, names...)Post-process a VTK .vtu file written by PETSc's VTK viewer to merge 9 separate scalar DataArrays named name.0…name.8 (produced when a field has more than 3 components) into a single NumberOfComponents="9" DataArray, for each name given. Also sets Tensors="name1 name2 …" on the <PointData> tag so ParaView treats the arrays as 3×3 tensors. The file is rewritten in-place.
PETSc's VTK writer always splits fields with more than 3 components into separate scalar arrays; this function reassembles them for proper tensor visualisation.
PETSc.@bd_fn — Macro
@bd_fn(f, outsz)Generate a C-callable wrapper for the pure-Julia boundary pointwise function f and return its C pointer, matching the PetscBdPointFn signature. Identical to @residual_fn but with an extra outward-normal vector n between x and numConstants.
User function signature:
f(dim_, Nf, NfAux, uOff, uOff_x, u, u_t, u_x,
aOff, aOff_x, a, a_t, a_x, t, x, n, numConstants, constants, out)PetscInt, PetscReal, PetscScalar must be in scope.
PETSc.@jacobian_fn — Macro
@jacobian_fn(f, outsz)Generate a C-callable wrapper for the pure-Julia Jacobian function f and return its C pointer, matching the PetscPointJacFn signature. Identical to @residual_fn but with an extra u_tShift scalar between t and x.
User function signature:
f(dim_, Nf, NfAux, uOff, uOff_x, u, u_t, u_x,
aOff, aOff_x, a, a_t, a_x, t, u_tShift, x, numConstants, constants, out)PetscInt, PetscReal, PetscScalar must be in scope.
PETSc.@residual_fn — Macro
@residual_fn(f, outsz)Generate a C-callable wrapper for the pure-Julia residual function f and return its C pointer, matching the PetscPointFn signature. All pointer arguments are unwrapped into Julia Vectors before calling f.
outsz is an expression giving the output array length in terms of dim_, Nf, NfAux, or numConstants (e.g. Nf for f0-type functions, dim_*Nf for f1-type functions).
User function signature:
f(dim_, Nf, NfAux, uOff, uOff_x, u, u_t, u_x,
aOff, aOff_x, a, a_t, a_x, t, x, numConstants, constants, out)PetscInt, PetscReal, PetscScalar must be in scope.
PETSc.@simple_fn — Macro
@simple_fn(f)Generate a C-callable wrapper for f(t, x, u, ctx) and return its C pointer, matching the PetscSimplePointFn signature. Arguments seen by f:
t— time (PetscReal)x— coordinates (Vector{PetscReal}, lengthdim)u— output field values (Vector{PetscScalar}, lengthNc)ctx— user context (Ptr{Cvoid})
Used by project_function!, add_boundary!, set_exact_solution!. PetscInt, PetscReal, PetscScalar must be in scope.