flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
vof_profile_mpi.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""WO-V9 item 3 — the g = 3 colour-halo cost under MPI, and what it is a fraction of.
3
4The colour advector owns its own g = 3 block with its own `GridHaloTopology`, and exchanges it
5once per sweep (three times a step, plus the momentum advector's two sibling fields per sweep).
6That is a deeper halo than the solver's own G = 2 and it is exchanged more often than anything
7else in the step, which is why PARIS's partial-column-sum trick (exchange sums over the swept
8column instead of the colour band, halving the depth) exists. This script measures what the
9trick would be BUYING before anyone writes it.
10
11Run:
12 mpirun -np 1 python tests/study/vof_profile_mpi.py --case hysing1 --steps 20
13 mpirun -np 2 python tests/study/vof_profile_mpi.py --case hysing1 --steps 20
14 mpirun -np 4 python tests/study/vof_profile_mpi.py --case packed --steps 20
15
16Needs a `-DPECLET_FLOW_MPI=ON` module on PYTHONPATH.
17"""
18import argparse
19import math
20import os
21import sys
22import time
23
24import numpy as np
25
26import peclet.flow as flow
27
28HERE = os.path.dirname(os.path.abspath(__file__))
29sys.path.insert(0, HERE)
30import vof_profile as vp # noqa: E402
31
32
33def slice_block(glob, origin, size):
34 o, n = origin, size
35 return np.asfortranarray(glob[o[0]:o[0] + n[0], o[1]:o[1] + n[1], o[2]:o[2] + n[2]])
36
37
38def build_hysing1(nx=64):
39 p = vp.HYSING1
40 gnz, gny = 2 * nx, 4
41 sc = vp.Scale(nx / 1.0)
42 R = sc.s * 0.25
43 o, n = flow.mpi_block(nx, gny, gnz)
44 s = flow.Solver(*n)
45 s.set_rho(p["rho1"])
46 s.set_mu(sc.mu(p["mu1"]))
47 s.set_domain_bc("-z", "wall", (0, 0, 0))
48 s.set_domain_bc("+z", "wall", (0, 0, 0))
49 s.init_mpi(nx, gny, gnz)
50 s.set_pressure_geometry(np.full(tuple(n), 10.0, order="F"))
51 s.set_pressure_chebyshev(True, 600, 1e-12)
52 s.enable_vof()
53 C = 1.0 - vp.cylinder_fractions((nx, gny, gnz), R, nx / 2.0, sc.s * 0.5)
54 s.set_vof(slice_block(C, o, n))
55 s.set_property_model("rho", "linear", "C", [p["rho2"], p["rho1"] - p["rho2"]])
56 s.set_property_model("mu", "linear", "C", [sc.mu(p["mu2"]), sc.mu(p["mu1"] - p["mu2"])])
57 s.set_surface_tension(sc.sigma(p["sigma"]))
58 s.enable_vof_momentum(p["rho2"], p["rho1"])
59 s.set_property_model("force_z", "linear", "C",
60 [-sc.bodyforce(p["rho2"] * p["g"]),
61 -sc.bodyforce((p["rho1"] - p["rho2"]) * p["g"])])
62 s.set_dt(0.5 * s.capillary_dt())
63 return s, 600, f"hysing1 {nx}x{gny}x{gnz}"
64
65
66def build_packed(nx=64, gnz=160):
67 NG, RJ, FSDF = 26, 0.171875 * nx, 0.85
68 RG = FSDF * RJ
69 ZBED, RBUB, ZBUB = 0.5625 * nx, 0.15625 * nx, 0.28125 * nx
70 ctr = vp.cached(f"bed_e7_{nx}_{gnz}", lambda: vp.rsa_bed(NG, float(nx), RJ, ZBED, seed=11))
71 SDF = vp.cached(f"sdf_e7_{nx}_{gnz}", lambda: vp.bed_sdf(ctr, nx, gnz, RG))
72 C0 = vp.cached(f"c0_e7_{nx}_{gnz}", lambda: vp.bubble_colour_sub(nx, gnz, RBUB, ZBUB))
73 ratio, Eo, Mo, g, theta = 100.0, 10.0, 1e-3, 2e-4, 60.0
74 D = 2 * RBUB
75 rho_l, rho_g = 1.0, 1.0 / ratio
76 drho = rho_l - rho_g
77 sigma = drho * g * D * D / Eo
78 mu_l = (Mo * rho_l ** 2 * sigma ** 3 / (g * drho)) ** 0.25
79 mu_g = mu_l / ratio
80 o, n = flow.mpi_block(nx, nx, gnz)
81 s = flow.Solver(*n)
82 s.set_rho(rho_l)
83 s.set_mu(mu_l)
84 s.set_domain_bc("-z", "wall", (0, 0, 0))
85 s.set_domain_bc("+z", "wall", (0, 0, 0))
86 s.init_mpi(nx, nx, gnz)
87 s.set_solid(slice_block(SDF, o, n), cutcell_pressure=True)
88 s.enable_vof()
89 s.set_vof(slice_block(C0, o, n))
90 s.set_property_model("rho", "linear", "C", [rho_g, rho_l - rho_g])
91 s.set_property_model("mu", "linear", "C", [mu_g, mu_l - mu_g])
92 s.set_surface_tension(sigma)
93 s.set_contact_angle(theta)
94 s.enable_vof_momentum(rho_g, rho_l)
95 s.set_property_model("force_z", "linear", "C", [-rho_g * g, -(rho_l - rho_g) * g])
96 s.set_pressure_chebyshev(True, 600, 1e-12)
97 s.set_dt(0.4 * s.capillary_dt())
98 return s, 600, f"packed {nx}x{nx}x{gnz}"
99
100
101BUILD = {"hysing1": build_hysing1, "packed": build_packed}
102
103
104def main():
105 ap = argparse.ArgumentParser()
106 ap.add_argument("--case", default="hysing1")
107 ap.add_argument("--steps", type=int, default=20)
108 ap.add_argument("--warm", type=int, default=5)
109 a = ap.parse_args()
110 s, cap, label = BUILD[a.case]()
111 rank, size = s.rank(), s.size()
112 pick = vp.dt_from_limits(0.4)
113 for i in range(a.warm):
114 pick(s, i)
115 s.step()
116 tm = vp.profile(s, a.steps, pick, cap, f"{label} np={size}")
117 # C sum is a global reduction the solver already does; use it as the run's sanity read-out
118 d = s.diagnostics.vof_diagnostics()
119 if rank == 0:
120 n = max(tm["steps"], 1)
121 st = tm["step"] / n
122 ex = tm["k_exchange"] / n
123 print(f"\n=== {tm['label']} (rank 0 of {size}) ===")
124 print(f" {tm['ms_per_step']:8.2f} ms/step wall {1e3*st:8.2f} ms/step timed "
125 f"pressure {tm['press_max']}/{tm['press_cap']}"
126 + (" CAPPED" if tm["capped"] else ""))
127 print(f" g=3 colour exchange {1e3*ex:9.3f} ms/step "
128 f"{100.0*ex/st if st else 0:6.2f} % of the step")
129 for k in ("vof_advect", "vof_momentum_advect", "vof_bridge", "vof_momentum_bridge",
130 "curvature", "csf", "k_reconstruct", "k_fluxes", "k_sweep", "k_clip",
131 "predictor", "momentum_solve", "projection", "step"):
132 print(f" {k:<24s} {1e3*tm[k]/n:9.3f} ms/step "
133 f"{100.0*(tm[k]/n)/st if st else 0:6.2f} %")
134 print(f" sum(C) = {d['sum']!r} dt census cfl {tm['binds_cfl']} / capillary "
135 f"{tm['binds_capillary']}")
136
137
138if __name__ == "__main__":
139 main()
slice_block(glob, origin, size)
build_packed(nx=64, gnz=160)