39 """C = 1 inside the markers (the dispersed phase), sub^3-subsampled fractions."""
40 q = (np.arange(sub) + 0.5) / sub
41 C = np.zeros((n, n, n))
42 for (cx, cy, cz, r)
in seeds:
43 i0, i1 = max(0, int(cx - r - 2)), min(n, int(cx + r + 3))
44 j0, j1 = max(0, int(cy - r - 2)), min(n, int(cy + r + 3))
45 k0, k1 = max(0, int(cz - r - 2)), min(n, int(cz + r + 3))
46 px = (np.arange(i0, i1)[:,
None] + q[
None, :]).ravel()
47 py = (np.arange(j0, j1)[:,
None] + q[
None, :]).ravel()
48 pz = (np.arange(k0, k1)[:,
None] + q[
None, :]).ravel()
49 d = ((px[:,
None,
None] - cx) ** 2 + (py[
None, :,
None] - cy) ** 2
50 + (pz[
None,
None, :] - cz) ** 2) < r * r
51 blk = d.reshape(i1 - i0, sub, j1 - j0, sub, k1 - k0, sub).
mean(axis=(1, 3, 5))
52 C[i0:i1, j0:j1, k0:k1] = np.maximum(C[i0:i1, j0:j1, k0:k1], blk)
53 return np.asfortranarray(C)
57 """`lattice` is WO-W12's swarm -- 4x4x4, uniformly filling the box. It is exactly the
58 configuration a CELL-COUNT ORB already balances perfectly, so it cannot show the lever; it is
59 kept as the control. `plume` is the configuration the lever is for: the same 64 markers
60 packed into the bottom third of the column, i.e. a rising swarm before it has spread."""
62 if layout ==
"lattice":
66 r = 2.0 + 3.5 * ((k % 8) / 7.0)
67 out.append(((i + 0.5) * n / 4, (j + 0.5) * n / 4, (m + 0.5) * n / 4, r))
70 rng = np.random.default_rng(3)
72 while len(out) < 64
and tries < 200000:
74 r = 2.0 + 2.0 * ((len(out) % 8) / 7.0)
75 p = (rng.uniform(r + 1, n - r - 1), rng.uniform(r + 1, n - r - 1),
76 rng.uniform(r + 1, 0.5 * n - r - 1))
77 if all((p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2 + (p[2] - q[2]) ** 2
78 > (r + q[3] + 1.5) ** 2
for q
in out):
79 out.append((p[0], p[1], p[2], r))
83def build(n, Cglob, ratio=100.0):
84 o, sz = flow.mpi_block(n, n, n)
86 rho_g, rho_l = 1.0, ratio
90 s.set_pressure_geometry(np.full(tuple(sz), 10.0, order=
"F"))
91 s.set_pressure_chebyshev(
True, 600, 1e-12)
93 s.set_vof(np.asfortranarray(
94 Cglob[o[0]:o[0] + sz[0], o[1]:o[1] + sz[1], o[2]:o[2] + sz[2]]))
95 s.set_property_model(
"rho",
"linear",
"C", [rho_l, rho_g - rho_l])
96 s.set_property_model(
"mu",
"linear",
"C", [0.05, 0.05 / ratio - 0.05])
97 s.set_surface_tension(1.0)
98 s.set_property_model(
"force_z",
"linear",
"C", [-1e-4 * rho_l, -1e-4 * (rho_g - rho_l)])
99 s.diagnostics.set_vof_curvature_worklist(os.environ.get(
"V9_CURV_WL",
"1") ==
"1")
100 s.diagnostics.set_vof_worklist(os.environ.get(
"V9_ADV_WL",
"1") ==
"1")
101 s.set_dt(0.4 * s.capillary_dt())
123 ap = argparse.ArgumentParser()
124 ap.add_argument(
"--n", type=int, default=64)
125 ap.add_argument(
"--steps", type=int, default=20)
126 ap.add_argument(
"--warm", type=int, default=5)
127 ap.add_argument(
"--w", type=float, default=8.0, help=
"interfacial work multiplier")
128 ap.add_argument(
"--layout", choices=(
"lattice",
"plume"), default=
"plume")
130 from mpi4py
import MPI
131 comm = MPI.COMM_WORLD
132 rank, size = comm.Get_rank(), comm.Get_size()
135 s, o, sz = build(n, Cglob)
136 pick = vp.dt_from_limits(0.4)
137 for i
in range(a.warm):
143 cells =
gather_ints(s, int(np.prod(np.asarray(s.get_vof()).shape)))
144 tm =
timed(s, a.steps, 600)
146 mx, mean = max(loads), sum(loads) / len(loads)
147 cmx, cmean = max(cells), sum(cells) / len(cells)
148 print(f
"\n--- {tag} (np = {size}, {n}^3, {a.layout}) ---")
149 print(f
" mixed cells per rank : {loads}")
150 print(f
" imbalance (mixed) : {mx/mean:.4f} (max {mx}, mean {mean:.1f})")
151 print(f
" cells per rank : {cells}")
152 print(f
" imbalance (cells) : {cmx/cmean:.4f}")
153 print(f
" ms/step : {tm['ms_per_step']:.3f} "
154 f
"pressure {tm['press_max']}/600"
155 + (
" CAPPED" if tm[
"capped"]
else ""))
156 k = max(tm[
"steps"], 1)
157 print(f
" VoF stage : {1e3*(tm['vof_advect']+tm['curvature']+tm['csf'])/k:.3f}"
158 f
" ms/step projection {1e3*tm['projection']/k:.3f} "
159 f
"momentum {1e3*tm['momentum_solve']/k:.3f}")
162 t_before = report(
"plain ORB (cell-count weights)")
166 Cl = np.asarray(s.get_vof())
167 mixed = ((Cl > EPS) & (Cl < 1.0 - EPS)).astype(np.float64)
168 Wglob = np.zeros((n, n, n), order=
"F")
169 Wl = np.zeros((n, n, n), order=
"F")
170 Wl[o[0]:o[0] + sz[0], o[1]:o[1] + sz[1], o[2]:o[2] + sz[2]] = mixed
171 comm.Allreduce(Wl, Wglob, op=MPI.SUM)
172 w = (1.0 + a.w * Wglob).ravel(order=
"F")
174 print(f
"\n weights: 1 + {a.w} * [mixed]; {int(Wglob.sum())} interfacial cells of "
175 f
"{n**3} ({100*Wglob.sum()/n**3:.2f} %)")
176 s.diagnostics.rebalance_by_weights(list(w))
177 t_after = report(f
"interface-weighted ORB (W = {a.w})")
179 b, af = t_before[
"ms_per_step"], t_after[
"ms_per_step"]
180 print(f
"\n ms/step {b:.3f} -> {af:.3f} ({100*(af/b-1):+.2f} %)")