Skip to content

Distributed MPI

MPI partitions local work and the global skeleton matrix across ranks. CPU spawn workers instead contribute to a global matrix owned by one coordinator. Choose MPI when you need the distributed algebra, then supply your existing problem definition on each rank.

Assign cells once

from mpi4py import MPI
from pymhm.execution.mpi import solve_distributed

comm = MPI.COMM_WORLD
owned_cells = problem.context.hierarchy.items[comm.rank::comm.size]
solution = solve_distributed(
    problem.local_provider,
    owned_cells,
    trace_size=problem.trace_size,
    comm=comm,
    local_solver="scipy",
)

This fragment assumes a bound problem with zero additional global/boundary loads. Define problem before it, using the API workflow. Every rank calls the collective solver, including ranks with no cells. The provider creates native local meshes on COMM_SELF in their owning rank. The application assigns each physical macrocell to exactly one rank; the solver does not infer a mesh partition.

The skeleton numbering must agree across ranks. PETSc adds reduced blocks by global index and communicates shared rows to their owners. MUMPS performs the distributed global LU. Retained modes are numbered by rank and local cell order, so compare reconstructed fields by macrocell identity rather than assuming serial and MPI retained-vector indices coincide.

Supply boundary and gauge data with ownership

solution = solve_distributed(
    problem.local_provider,
    owned_cells,
    trace_size=problem.trace_size,
    comm=comm,
    boundary_load=(owned_face_indices, owned_boundary_moments),
    fixed=common_prescribed_trace_coefficients,
    moments=rank_owned_physical_moments,
    global_equation=rank_owned_global_equation,
)

boundary_load and global_equation are additive rank-owned contributions. Supply each physical term once; replicating a nonzero term on every rank counts it repeatedly. Boundary load signs follow the declared hybrid equation. fixed and physical moment targets are common metadata and must agree on every rank. A pure Neumann problem also requires compatible source/outward-flux data and a physical pressure gauge.

Only rank-owned local responses and reconstructed fields are retained. For a field norm, integrate squared contributions on the owned meshes and combine them with comm.allreduce(..., op=MPI.SUM) before taking the square root. Use collective synchronization and the largest rank duration for a complete wall-time measurement.

Launch the native environment

Save the problem definition and collective calls in run_mpi.py:

OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 pixi run --locked -e introduction mpiexec -n 4 python run_mpi.py

Respect the scheduler's CPU allocation and set native thread limits before imports. This path requires MPI, petsc4py and a real PETSc build with MUMPS. An unavailable MUMPS backend produces an explicit error.

The distributed interface uses real binary64 data and does not support recursive MultiscaleProblem local operators. Inspect distributed assembly for the numerical data contract and explicit additional forms. For rank-owned CUDA devices, continue with the GPU guide.