52 from mpi4py
import MPI
57 from peclet
import flow
59 gx, gy, gz = args.grid
60 origin, size = flow.mpi_block(gx, gy, gz)
62 cells = lnx * lny * lnz
63 hi = w.allreduce(cells, op=MPI.MAX)
64 lo = w.allreduce(cells, op=MPI.MIN)
65 split = [w.allreduce(1
if size[k] < (gx, gy, gz)[k]
else 0, op=MPI.SUM)
for k
in range(3)]
70 s = flow.Solver(lnx, lny, lnz)
71 s.init_mpi(gx, gy, gz)
75 s.set_pressure_multigrid(
True, args.levels)
76 s.set_pressure_pcg(
True, 200, 1e-6)
80 s.set_pressure_geometry(np.asfortranarray(np.full((lnx, lny, lnz), 1e30)))
84 f
"RESULT np={w.size} block={lnx}x{lny}x{lnz} imbalance={hi / lo:.3f} "
85 f
"split=({split[0]},{split[1]},{split[2]})",
106 ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
107 ap.add_argument(
"--grid", required=
True, help=
"GNX,GNY,GNZ")
108 ap.add_argument(
"--np", default=
"1", help=
"comma-separated rank counts")
109 ap.add_argument(
"--levels", type=int, default=5, help=
"multigrid levels requested (default 5)")
110 ap.add_argument(
"--mode", default=
"0", help=
"comma-separated: 0/aligned, coarse, or a depth")
111 ap.add_argument(
"--walls", action=
"store_true", help=
"no-slip -y/+y (the channel's domain-BC MG path)")
112 ap.add_argument(
"--orb-only", action=
"store_true", help=
"partition only; no Solver, so any size")
113 ap.add_argument(
"--verbose", action=
"store_true", help=
"also print the per-level dims/ratios")
114 ap.add_argument(
"--mpirun", default=
"mpirun")
115 ap.add_argument(
"--child", action=
"store_true", help=argparse.SUPPRESS)
116 args = ap.parse_args()
117 args.grid = [int(v)
for v
in args.grid.split(
",")]
121 gx, gy, gz = args.grid
122 print(f
"grid {gx}x{gy}x{gz} = {gx * gy * gz / 1e6:.1f} Mcells "
123 f
"halvings x/y/z = {halvings(gx)}/{halvings(gy)}/{halvings(gz)}"
124 f
"{' <-- an axis with 0 halvings NEVER coarsens' if 0 in (halvings(gx), halvings(gy), halvings(gz)) else ''}")
125 print(f
"multigrid levels requested: {args.levels}\n")
126 print(f
"{'mode':<20} {'np':>4} {'block':>18} {'imbalance':>10} {'split x,y,z':>12} {'levels':>7}")
128 for tok
in args.mode.split(
","):
129 label, decomp =
mode_spec(tok.strip(), args.levels)
130 for np_
in [int(v)
for v
in args.np.split(
",")]:
131 env = dict(os.environ, PECLET_FLOW_MG_DEBUG=
"1", PECLET_FLOW_DECOMP_LEVELS=decomp)
132 cmd = [args.mpirun,
"--oversubscribe",
"-np", str(np_), sys.executable, HERE,
"--child",
133 "--grid",
",".join(map(str, args.grid)),
"--levels", str(args.levels)]
135 cmd.append(
"--walls")
137 cmd.append(
"--orb-only")
139 out = subprocess.run(cmd, env=env, capture_output=
True, text=
True, timeout=900).stdout
140 except subprocess.TimeoutExpired:
141 print(f
"{label:<20} {np_:>4} TIMED OUT")
143 res = re.search(
r"RESULT np=(\d+) block=(\S+) imbalance=(\S+) split=\((\S+)\)", out)
145 print(f
"{label:<20} {np_:>4} FAILED (rerun by hand for the error)")
147 lev = re.search(
r"-> (\d+) levels", out)
148 cf = re.search(
r"coarse-first depth (\d+) \(refine (\S+),", out)
149 note = f
" [coarse-first depth {cf.group(1)}, refine {cf.group(2)}]" if cf
else ""
150 print(f
"{label:<20} {res.group(1):>4} {res.group(2):>18} {res.group(3):>10} "
151 f
"{res.group(4):>12} {lev.group(1) if lev else '-':>7}{note}")
153 for line
in out.splitlines():
154 if line.startswith(
"[mg] L"):
156 print(
"\nreminders: an ODD axis never coarsens (measured 3.2x slower on one GPU);")
157 print(
" in MPI a level coarsens an axis only if EVERY rank's block is even on it,")
158 print(
" so the achievable depth is set by the per-rank block, not the global grid.")