flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
run_mpi.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Distributed timing of the bubble column from ckpt_t43 (host MPI x OpenMP; the VoF
3step-performance harness, WO-0 -- doc/vof_step_performance_design.md).
4
5 export OMPI_MCA_hwloc_base_binding_policy=none
6 PYTHONPATH=<PECLET_FLOW_MPI build> OMP_NUM_THREADS=3 OMP_PROC_BIND=false \
7 mpirun -np 8 --bind-to none python tests/study/vof_perf/run_mpi.py N [--warm W] [--cheb]
8 [--rtol R] [--dump out.npz] [--ckpt PATH] [--case-dir DIR]
9
10Same case as run_peclet.build(), on this rank's ORB block; set_superficial_velocity closes the
11column (a build older than it shifts u on the host: global mean via Allreduce). --ckpt and
12--case-dir default as in prof.py. Prints one line on rank 0: ms/step and mean pressure iterations.
13
14 --rtol R MG-PCG relative tolerance (default 1e-8: the case's registered tolerance, register
15 2026-10-03; the solver default stays 1e-10)
16 --dump F save u, v, w, p, C, the timed steps' dt (dts) and pressure iterations (iters) in
17 prof.py's format (compare with cmp.py); the fields are gathered onto rank 0 at np > 1,
18 the VoF block colours (col<id>) are stored at np = 1 only
19
20np = 1 is SINGLE-RANK: at size 1 neither mpi_block nor init_mpi is called (sec. 14.3 H-0 of
21doc/vof_step_performance_design.md). init_mpi at size 1 keeps the distributed path on purpose (the
22register's np = 1 bit-exactness gate needs it), so a one-rank benchmark that called it timed the
23distributed code -- GridHalo self-copies, the box smoother, no single-rank fusions.
24"""
25import os
26import sys
27import time
28
29import numpy as np
30from mpi4py import MPI
31
32
33def arg(name, default):
34 return sys.argv[sys.argv.index(name) + 1] if name in sys.argv else default
35
36
37CASE_DIR = arg("--case-dir", os.path.expanduser(
38 "~/Codes/peclet-examples/benchmarks/bubble-column/scripts"))
39CKPT = arg("--ckpt", os.path.expanduser(
40 "~/Codes/bubble_column_perf/ckpt_t43.npz"))
41sys.path.insert(0, CASE_DIR)
42import case # noqa: E402
43import peclet.flow as pf # noqa: E402
44import run_peclet as rp # noqa: E402
45
46N = int(sys.argv[1])
47WARM = int(sys.argv[sys.argv.index("--warm") + 1]) if "--warm" in sys.argv else 5
48RTOL = float(arg("--rtol", 1e-8))
49DUMP = arg("--dump", None)
50comm = MPI.COMM_WORLD
51rank = comm.Get_rank()
52SINGLE = comm.Get_size() == 1
53S = case.CELLS_PER_D
54NX, NY, NZ = case.NX, case.NY, case.NZ
55
56step, t, blocks, vel, pres, series = rp.load_ckpt(CKPT)
57if SINGLE: # H-0: one rank runs the single-rank solver (no mpi_block, no init_mpi)
58 org, siz = (0, 0, 0), (NX, NY, NZ)
59else:
60 org, siz = pf.mpi_block(NX, NY, NZ)
61sl = tuple(slice(o, o + n) for o, n in zip(org, siz))
62s = pf.Solver(*siz)
63if not SINGLE:
64 s.init_mpi(NX, NY, NZ)
65s.set_rho(case.RHO_L)
66s.set_mu(S * S * case.MU_L)
67s.set_domain_bc("-y", "wall", (0, 0, 0))
68s.set_domain_bc("+y", "wall", (0, 0, 0))
69s.set_pressure_geometry(np.full(tuple(siz), 10.0, order="F"))
70s.enable_vof()
71s.set_vof(np.zeros(tuple(siz), order="F"))
72s.set_property_model("rho", "linear", "C", [case.RHO_L, case.RHO_G - case.RHO_L])
73s.set_property_model("mu", "linear", "C", [S * S * case.MU_L, S * S * (case.MU_G - case.MU_L)])
74s.set_surface_tension(S ** 3 * case.SIGMA)
75s.set_pressure_chebyshev(True, 800, 1e-10)
76s.enable_vof_blocks_from_colors([b for b, _ in blocks], [c for _, c in blocks])
77s.enable_vof_block_csf()
78vol = comm.allreduce(sum(b["volume"] for b in s.diagnostics.vof_block_stats()), op=MPI.SUM)
79alpha = vol / (NX * NY * NZ)
80rho_av = case.RHO_L + (case.RHO_G - case.RHO_L) * alpha
81s.set_property_model("force_x", "linear", "C",
82 [S * (case.RHO_L - rho_av) * case.G, S * (case.RHO_G - case.RHO_L) * case.G])
83_closure = getattr(s, "set_superficial_velocity", None) or getattr(s, "set_bulk_velocity", None)
84if _closure is not None:
85 _closure(True, "x", 0.0)
86HOST_FLUX = _closure is None # a build older than the device constraint: shift on the host
87if "--cheb" not in sys.argv: # MG-PCG, as run_peclet.build() (933cc79); --cheb keeps Chebyshev
88 s.set_pressure_pcg(True, 800, RTOL)
89for c, a in enumerate(vel):
90 s.set_velocity(c, np.asfortranarray(a[sl]))
91s.set_field("p", np.asfortranarray(pres[sl]))
92s.diagnostics.set_vof_step_parity(step)
93
94
95def one():
96 L = s.vof_step_limits()
97 dt = min(0.25 * L["cfl_dt"], 0.25 * L["capillary_dt"])
98 s.set_dt(dt)
99 s.step()
100 if HOST_FLUX:
101 u = s.get_u()
102 mean = comm.allreduce(float(u.sum()), op=MPI.SUM) / comm.allreduce(u.size, op=MPI.SUM)
103 s.set_velocity(0, np.asfortranarray(u - mean))
104 return dt
105
106
107for _ in range(WARM):
108 one()
109comm.Barrier()
110w0 = time.perf_counter()
111it, dts = [], []
112for _ in range(N):
113 dts.append(one())
114 it.append(s.diagnostics.last_pressure_iterations())
115comm.Barrier()
116wall = time.perf_counter() - w0
117if rank == 0:
118 print(f"np {comm.Get_size()} block {list(siz)} steps {N} {1000 * wall / N:.2f} ms/step "
119 f"pressure iters mean {np.mean(it):.2f} alpha {alpha:.5f}"
120 f"{' (single-rank)' if SINGLE else ''}", flush=True)
121if DUMP:
122 def gather(a): # this rank's block -> the global array on rank 0
123 parts = comm.gather((org, np.asarray(a)), root=0)
124 if rank != 0:
125 return None
126 g = np.zeros((NX, NY, NZ), order="F")
127 for o, b in parts:
128 g[o[0]:o[0] + b.shape[0], o[1]:o[1] + b.shape[1], o[2]:o[2] + b.shape[2]] = b
129 return g
130 fields = {k: gather(f()) for k, f in (("u", s.get_u), ("v", s.get_v), ("w", s.get_w),
131 ("p", lambda: s.get_field("p")),
132 ("C", lambda: s.get_field("C")))}
133 cols = ({f"col{b['id']}": np.asarray(s.vof_block_color(b["id"]))
134 for b in s.diagnostics.vof_block_stats()} if SINGLE else {})
135 if rank == 0:
136 np.savez(DUMP, **fields, dts=np.array(dts), iters=np.array(it, dtype=np.int64), **cols)
137 print("dumped", DUMP, f"({len(cols)} block colours)", flush=True)
arg(name, default)
Definition run_mpi.py:33
gather(a)
Definition run_mpi.py:122
one()
Definition run_mpi.py:95
static std::vector< double > slice(const std::vector< double > &g, int ox, int oy, int oz, int lnx, int lny, int lnz)