Offline mesh partitioning (sequential_mesh_dump / sequential_mesh_load)
Overview
The sequential_mesh_dump / sequential_mesh_load pair implements an
offline mesh partitioning workflow:
Preprocessing — run once, on any machine (workstation, login node, …):
python create_dump.py -np N
Creates one NetCDF4 file per rank containing mesh topology and the halo communication structure. No quantity data is written.
Simulation — run as many times as needed, on the HPC cluster:
mpiexec -np N python -u run_evolve.py
Each rank reads its own file independently (no rank-0 bottleneck), sets its own initial conditions from a function or DEM, and proceeds directly to
domain.evolve().
This differs from the sdpl (sequential_distribute_dump /
sequential_distribute_load) workflow, which stores the full domain
including quantities.
When to use this approach
Situation |
Recommendation |
|---|---|
Full domain + quantities fits in rank-0 RAM |
|
Mesh fits in rank-0 RAM but quantity arrays do not |
|
Mesh is too large for rank-0 RAM at runtime but a preprocessing node has sufficient memory |
This workflow |
Many scenario / ensemble variants on the same mesh |
This workflow — dump once, load-and-evolve many times |
Initial conditions come from a function or per-rank DEM read |
This workflow |
Memory comparison
For a mesh with N triangles and P quantities:
Workflow |
Rank-0 peak RAM |
Required at runtime |
|---|---|---|
|
O(N) × (mesh + P qty) |
MPI job running |
|
O(N) × mesh only |
MPI job running |
|
O(N) × mesh only (preprocessing) |
Only per-rank partition |
API
- anuga.sequential_mesh_dump(domain, numprocs, partition_dir='.', name=None, verbose=False, parameters=None)[source]
Partition a domain mesh and write one NetCDF4 file per rank.
Saves mesh topology and halo structure only — no quantities. Suitable as an offline preprocessing step before large parallel runs. After loading with
sequential_mesh_load()the caller sets initial conditions viadomain.set_quantity()before evolving.Files are written to
<partition_dir>/<name>_mesh_P<numprocs>_<rank>.nc.Parameters
- domainDomain or Basic_mesh
Source mesh. Quantities present on the domain are ignored.
- numprocsint
Number of partitions to create.
- partition_dirstr or path-like
Output directory, created if it does not exist.
- namestr, optional
Base name for output files. Defaults to
domain.get_name()when available, otherwise'mesh'.- verbosebool
Print progress messages if True.
- parametersdict, optional
Forwarded to
partition_mesh()andbuild_submesh(). Recognised keys include'partition_scheme'('metis','morton', or'hilbert'),'ghost_layer_width', and'cache_dir'.
- anuga.sequential_mesh_load(name, partition_dir='.', verbose=False)[source]
Load this rank’s mesh partition and return a bare
Parallel_domain.Reads the NetCDF4 file written by
sequential_mesh_dump()for the calling MPI rank. No quantities are set; calldomain.set_quantity()anddomain.set_boundary()before evolving.Parameters
- namestr
Base name passed to
sequential_mesh_dump().- partition_dirstr or path-like
Directory containing the partition files.
- verbosebool
Print progress messages if True.
Returns
- Parallel_domain
Domain with mesh topology and halo structure initialised. All quantities are zero; boundary conditions are unset (
None).
File format
Each partition file <name>_mesh_P<N>_<rank>.nc is a self-contained
NETCDF4 file. It can be inspected with ncdump -h.
Global attributes (scalar metadata)
Attribute |
Description |
|---|---|
|
This partition’s rank and total partition count. |
|
Triangles owned by this rank (excludes ghost layer). |
|
Nodes owned by this rank (excludes ghost layer nodes). |
|
Total triangles in the global mesh. |
|
Total nodes in the global mesh. |
|
Depth of the halo ghost layer (typically 2). |
|
|
Variables
Variable |
Shape |
Description |
|---|---|---|
|
(node, 2) |
Node (x, y) coordinates. Includes ghost nodes. |
|
(tri, 3) |
Triangle connectivity (local node indices). Includes ghost triangles. |
|
(tri,) |
Local-to-global triangle index map. |
|
(node,) |
Local-to-global node index map. |
|
(bnd,) |
Boundary edge records: triangle index, edge index (0–2), and string tag. |
|
CSR arrays |
Full-send communication pattern (which local triangles to send to which neighbour ranks, with their global IDs). |
|
CSR arrays |
Ghost-receive communication pattern. |
Preprocessing example
# create_partitions.py — run once on a workstation or login node
# python create_partitions.py -np 64
import argparse
import anuga
from anuga import rectangular_cross_domain # or your mesh builder
parser = argparse.ArgumentParser()
parser.add_argument('-np', '--numprocs', type=int, default=8)
args = parser.parse_args()
domain = rectangular_cross_domain(500, 500, len1=10.0, len2=10.0)
domain.set_name('flood_mesh')
anuga.sequential_mesh_dump(
domain,
numprocs=args.numprocs,
partition_dir='Partitions',
parameters={'partition_scheme': 'metis', 'ghost_layer_width': 2},
verbose=True,
)
# Writes: Partitions/flood_mesh_mesh_P<N>_<rank>.nc
When building a mesh from a DEM or polygon file, replace
rectangular_cross_domain with your usual
create_domain_from_regions() call. The set_quantity calls for
initial conditions are not needed here — each rank will set them
independently at runtime.
Parallel load-and-evolve example
# run_evolve.py — run with: mpiexec -np N python -u run_evolve.py
import anuga
from anuga import myid, numprocs, finalize, barrier, Reflective_boundary
barrier()
domain = anuga.sequential_mesh_load(name='flood_mesh',
partition_dir='Partitions',
verbose=(myid == 0))
barrier()
# --- quantities: each rank evaluates independently ---
domain.set_quantity('elevation', lambda x, y: 0.1 * x)
domain.set_quantity('stage', expression='elevation + 0.5')
domain.set_quantity('friction', 0.03)
# --- boundary conditions ---
Br = Reflective_boundary(domain)
domain.set_boundary({'left': Br, 'right': Br, 'top': Br, 'bottom': Br})
# --- evolve ---
domain.set_name('flood_mesh')
domain.set_flow_algorithm('DE0')
for t in domain.evolve(yieldstep=60.0, finaltime=3600.0):
if myid == 0:
domain.print_timestepping_statistics()
domain.sww_merge(delete_old=True)
finalize()
Using a DEM for elevation
Because each rank calls set_quantity with its own local coordinates,
you can read a DEM file directly without any rank-0 bottleneck:
from anuga import Quantity
import anuga
# file_function reads only the region covered by this rank's triangles
elev_func = anuga.file_function('topography.asc', domain,
quantities=['elevation'])
domain.set_quantity('elevation', elev_func)
Alternatively, pass a callable that reads from the DEM independently on each rank — the call happens only on the local mesh centroids.
Scenario ensembles on the same mesh
A common flood risk workflow: one mesh, many hydrological scenarios.
# Step 1 — partition once
python create_partitions.py --mesh flood_mesh --np 32
# Step 2 — run each scenario in parallel (same partition files)
for SCENARIO in Q10 Q100 Q500 QPMF; do
mpiexec -np 32 python run_evolve.py --scenario $SCENARIO
done
The partition files are reused across all scenarios. Only the
set_quantity calls (initial water level, rainfall rate, etc.) differ
between runs.
Example scripts
Ready-to-run examples are in examples/parallel/:
Script |
Description |
|---|---|
|
Creates a rectangular-cross mesh and writes partition files.
Command line: |
|
Loads partition files and runs the evolve loop.
Command line: |