Distributed (MPI) eigensolver
The distributed backend is new and not yet covered by a green integration run. Verify your results against an in-process method on a smaller problem before relying on it. The API may change.
For problems too large for the in-process backends, BiGSTARS can run the eigensolve across MPI ranks using SLEPc over PETSc. Assembly stays serial on rank 0; the shift-and-invert factorization and Krylov eigensolve are distributed.
Requirements
- A complex-scalar system build of PETSc and SLEPc (
./configure --with-scalar-type=complex), withPETSC_DIR,PETSC_ARCH, andSLEPC_DIRexported. - Julia packages
MPI,PetscWrap,SlepcWrapinstalled. Importing all three activates theBiGSTARSMPIExtextension and thesolve_mpientrypoint.
Usage
using BiGSTARS, MPI, PetscWrap, SlepcWrap
cache = discretize(prob)
# solve_mpi initializes SLEPc itself — the solver options must be in the PETSc
# options database at init time, so do NOT call SlepcInitialize yourself.
results = solve_mpi(cache, k_values;
sigma_0=0.02, nev=5, which=:LM,
tol=1e-10, mat_solver=:mumps)
# results is fully populated on rank 0; other ranks get empty markers.Run with mpiexec -n P julia --project=. script.jl.
Worked example: Eady baroclinic instability
The MPI counterpart of examples/Eady.jl — the same problem, but the eigensolve is spread across ranks. The full runnable script is examples/eady_mpi.jl.
You build the problem with the same DSL as the serial path; only the solve call changes (solve_mpi instead of solve/EigenSolver).
using BiGSTARS
using MPI, PetscWrap, SlepcWrap
using Printf
# Build the EVP (runs on every rank; only rank-0's matrices get used)
domain = Domain(x = FourierTransformed(),
y = Fourier(60, [0, 1]),
z = Chebyshev(40, [0, 1]))
prob = EVP(domain, variables=[:psi], eigenvalue=:sigma)
Y, Z = gridpoints(domain, :y, :z)
Ri = 1.0
prob[:U] = Z .- 0.5
prob[:Ri] = Ri
prob[:E] = 1e-12
prob[:dBdy] = -ones(length(Z))
prob[:dQdy] = zeros(length(Z))
@substitution Lap(A) = dx(dx(A)) + dy(dy(A)) + Ri * dz(dz(A))
@equation sigma * Lap(psi) = U * dx(Lap(psi)) + dQdy * dx(psi) - E * Lap(Lap(psi))
@bc left(sigma * dz(psi) + U * dx(dz(psi)) + dBdy * dx(psi)) = 0
@bc right(sigma * dz(psi) + U * dx(dz(psi)) + dBdy * dx(psi)) = 0
cache = discretize(prob)
# Distributed solve: one eigenproblem at k = 1.0, across all ranks.
# sigma_0 is the shift-and-invert target; the nev modes nearest it come back.
# solve_mpi initializes SLEPc itself, so don't call SlepcInitialize.
results = solve_mpi(cache, [1.0];
sigma_0=0.2, nev=6, which=:LM,
tol=1e-10, mat_solver=:mumps)
# Only rank 0 has populated results; other ranks get empty markers.
rank = MPI.Comm_rank(MPI.COMM_WORLD)
if rank == 0
r = results[1]
if r.converged
i = argmax(real.(r.eigenvalues)) # most unstable = largest growth
σ = r.eigenvalues[i]
@printf("most unstable σ = %.6f %+.6fi\n", real(σ), imag(σ))
else
println("did not converge")
end
endOne-time environment setup
# 1. Build complex-scalar PETSc + SLEPc; export PETSC_DIR / PETSC_ARCH / SLEPC_DIR
# ./configure --with-scalar-type=complex --download-mumps --download-scalapack
# 2. Bind MPI.jl to the SAME MPI used for PETSc, in an env with the weakdeps
# (the repo's test/mpi environment works):
julia --project=test/mpi -e 'using Pkg; Pkg.develop(path="."); Pkg.instantiate()'
julia --project=test/mpi -e 'using MPIPreferences; MPIPreferences.use_system_binary()'Then launch across, e.g., four ranks:
mpiexec -n 4 julia --project=test/mpi examples/eady_mpi.jlNotes
which=:LMtargets eigenvalues nearestsigma_0(shift-and-invert), matching the serial backends.:LR/:SR/:LI/:SIselect by real/imaginary extremes.mat_solverpicks the parallel direct solver for the inner solves (:mumps,:superlu_dist, or:petsc).- v1 does a single solve at
sigma_0(no adaptive-σ retry) and gathers eigenvectors to rank 0 for reconstruction.