36 """Periodic simple-cubic array of spheres (per_axis^3 of them), sampled at cell centres of this
37 rank's block (cell units, origin 0). Negative inside the solid."""
40 x = o[0] + np.arange(sz[0]) + 0.5
41 y = o[1] + np.arange(sz[1]) + 0.5
42 z = o[2] + np.arange(sz[2]) + 0.5
43 X, Y, Z = np.meshgrid(x, y, z, indexing=
"ij")
44 dx = (X % a) - 0.5 * a
45 dy = (Y % a) - 0.5 * a
46 dz = (Z % a) - 0.5 * a
47 return np.asfortranarray(np.sqrt(dx * dx + dy * dy + dz * dz) - r)
51 """1 + Poisson(lam) particle counts below a bed surface, 1 above — a settled bed, as
52 `CfdDem.rebalance(gamma=1)` would build it (1 + gamma * count). tilt = 0 is a FLAT bed at
53 z = bed*n (uniform in x and y, so the x/y ORB splits stay at the equal-cell positions); tilt > 0
54 is a HEAP whose surface falls linearly in x and y, z_s = bed*n*(1 + tilt*(1/2 - x/n) +
55 tilt*(1/2 - y/n)), which moves every split. Global, x-fastest (Fortran ravel), identical on
56 every rank (same seed)."""
57 rng = np.random.default_rng(seed)
58 c = (np.arange(n) + 0.5) / n
59 X, Y, Z = np.meshgrid(c, c, c, indexing=
"ij")
60 zs = bed * (1.0 + tilt * (0.5 - X) + tilt * (0.5 - Y))
61 counts = np.where(Z < zs, rng.poisson(lam, size=(n, n, n)), 0)
62 return (1.0 + counts).ravel(order=
"F")
74 print(f
"\n[probe] {tag}: per-rank blocks (origin / size / odd axes)")
75 W =
None if w
is None else w.reshape((n, n, n), order=
"F")
77 for r, (o, sz)
in enumerate(blks):
78 odd = [ax
for ax
in range(3)
if (o[ax] % 2)
or (sz[ax] % 2)]
79 ld = sz[0] * sz[1] * sz[2]
if W
is None else float(
80 W[o[0]:o[0] + sz[0], o[1]:o[1] + sz[1], o[2]:o[2] + sz[2]].sum())
82 cells.append(sz[0] * sz[1] * sz[2])
83 print(f
"[probe] rank {r}: origin {o} size {sz} odd-axes {''.join('xyz'[a] for a in odd) or '-'}"
85 loads = np.asarray(loads, dtype=float)
86 cells = np.asarray(cells, dtype=float)
87 print(f
"[probe] weight imbalance max/mean = {loads.max() / loads.mean():.3f}"
88 f
" cell imbalance max/mean = {cells.max() / cells.mean():.3f}")
93 """Per step: pressure iterations, wall time (barrier to barrier), and the max over ranks of the
94 solver's own projection and momentum timers (`last_step_timers`)."""
95 its, ts, tp, tm = [], [], [], []
98 t0 = time.perf_counter()
101 ts.append(time.perf_counter() - t0)
102 its.append(s.diagnostics.last_pressure_iterations())
103 t = s.diagnostics.last_step_timers()
104 tp.append(comm.allreduce(float(t[
"projection"]), op=MPI.MAX))
105 tm.append(comm.allreduce(float(t[
"momentum"]), op=MPI.MAX))
106 return its, ts, tp, tm
110 ap = argparse.ArgumentParser()
111 ap.add_argument(
"--n", type=int, default=96)
112 ap.add_argument(
"--levels", type=int, default=4, help=
"set_pressure_multigrid levels")
113 ap.add_argument(
"--graph-amg", action=
"store_true", help=
"set_pressure_graph_amg(True)")
114 ap.add_argument(
"--bottom", choices=(
"auto",
"smoother",
"agglomerated"), default=
"auto",
115 help=
"set_pressure_bottom (auto = the default)")
116 ap.add_argument(
"--warm", type=int, default=2)
117 ap.add_argument(
"--steps", type=int, default=5)
118 ap.add_argument(
"--bed", type=float, default=0.35, help=
"bed height fraction of z")
119 ap.add_argument(
"--lam", type=float, default=4.0, help=
"mean particles per bed cell")
120 ap.add_argument(
"--tilt", type=float, default=0.0, help=
"0 = flat bed, >0 = heap")
121 ap.add_argument(
"--seed", type=int, default=7)
123 comm = MPI.COMM_WORLD
126 o, sz = flow.mpi_block(n, n, n)
127 s = flow.Solver(tuple(sz))
129 s.set_pressure_multigrid(
True, a.levels)
131 s.diagnostics.set_pressure_graph_amg(
True)
132 s.set_pressure_bottom(a.bottom)
133 s.set_body_force((1e-4, 0.0, 0.0))
137 print(f
"[probe] n={n}^3 np={comm.size} levels={a.levels} graph_amg={a.graph_amg} "
138 f
"bottom={a.bottom} telescope={s.pressure_telescope()}")
141 describe(
"BEFORE (equal-weight aligned ORB)", comm,
blocks(comm, s),
None, n)
143 its0, ts0, tp0, tm0 =
run_steps(comm, s, a.steps)
147 print(
"\n[probe] ---- rebalance_by_weights ----")
150 t0 = time.perf_counter()
151 s.diagnostics.rebalance_by_weights(w.tolist())
153 trb = time.perf_counter() - t0
155 its1, ts1, tp1, tm1 =
run_steps(comm, s, a.steps)
158 print(f
"\n[probe] rebalance took {trb:.2f} s")
159 print(f
"[probe] BEFORE pressure iters/step {its0} step s {[round(t, 3) for t in ts0]}"
160 f
" median {np.median(ts0):.3f} projection(max rank) {np.median(tp0):.3f}"
161 f
" momentum(max rank) {np.median(tm0):.3f}")
162 print(f
"[probe] AFTER pressure iters/step {its1} step s {[round(t, 3) for t in ts1]}"
163 f
" median {np.median(ts1):.3f} projection(max rank) {np.median(tp1):.3f}"
164 f
" momentum(max rank) {np.median(tm1):.3f}")
165 print(f
"[probe] SUMMARY n={n} np={comm.size} levels={a.levels} graph_amg={a.graph_amg}"
166 f
" bottom={a.bottom} tilt={a.tilt} seed={a.seed}"
167 f
" iters {np.mean(its0):.1f} -> {np.mean(its1):.1f}"
168 f
" step {np.median(ts0):.3f} -> {np.median(ts1):.3f} s"
169 f
" projection {np.median(tp0):.3f} -> {np.median(tp1):.3f} s"
170 f
" momentum {np.median(tm0):.3f} -> {np.median(tm1):.3f} s")