flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
balanced_force_cost.py
Go to the documentation of this file.
1#!/usr/bin/env python
2"""Cost of the balanced-force projection (doc/collocated_varrho_forces.md ยง4.8, gate G7) -- an
3INSTRUMENT, not a gate. Two cases: MOVING (below) and STATIC (the same drop at rest, 50 steps:
4with the increment solve and its full-RHS stop, WO-P5, a static interface must cost 0
5pre-projection iterations after the first step).
6
7A moving ratio-1000 drop on 32^3 (periodic, R = 8, sigma = 1, mu = 0.1, a uniform translation
8U = 0.02 seeded in the velocity, dt = 0.5 capillary_dt), on both grids, stepped with the
9balanced-force projection OFF and ON. Reports the median wall time per step over steps
10[WARM, STEPS) and the mean pressure / balanced-force iteration counts. G7: ON <= 1.5x OFF, and
11mean balanced-force iterations <= mean main pressure iterations.
12
13The staggered grid runs with enable_vof_momentum (ratio 1000 with motion); the collocated grid is
14V8 (the option is its default; OFF is set explicitly).
15
16Run: OMP_NUM_THREADS=8 OMP_PROC_BIND=false PYTHONPATH=<build> python tests/study/balanced_force_cost.py
17Timings on a shared host are noisy: OFF and ON are interleaved per repeat and the median taken.
18"""
19import sys
20import time
21
22import numpy as np
23
24import peclet.flow as pf
25
26N, R, SIGMA, MU, RATIO, U0 = 32, 8.0, 1.0, 0.1, 1000.0, 0.02
27STEPS, WARM, REPEATS = 30, 5, 3
28
29
30def sphere_c(sub=4):
31 off = (np.arange(sub) + 0.5) / sub
32 c = np.array([N / 2 + 0.13, N / 2 + 0.27, N / 2 + 0.11])
33 out = np.zeros((N, N, N))
34 for a in off:
35 for b in off:
36 for d in off:
37 X, Y, Z = np.meshgrid(np.arange(N) + a, np.arange(N) + b, np.arange(N) + d,
38 indexing="ij")
39 out += ((X - c[0]) ** 2 + (Y - c[1]) ** 2 + (Z - c[2]) ** 2 < R * R)
40 return np.asfortranarray(out / sub ** 3)
41
42
43def build(cls, on, u0, kappa=None):
44 s = cls(N, N, N)
45 s.set_rho(RATIO)
46 s.set_mu(MU)
47 s.set_dt(1.0)
48 s.set_pressure_geometry(np.asfortranarray(np.full((N, N, N), 10.0)))
49 s.enable_vof()
50 s.set_vof(sphere_c())
51 s.set_property_model("rho", "linear", "C", [1.0, RATIO - 1.0])
52 s.set_surface_tension(SIGMA)
53 if kappa is not None: # a constant curvature: the drop at rest is an EXACT equilibrium
54 s.diagnostics.set_vof_kappa_constant(kappa)
55 if cls is pf.Solver:
56 s.enable_vof_momentum(1.0, RATIO)
57 s.set_dt(0.5 * s.capillary_dt())
58 if u0 != 0.0:
59 s.set_field("u", np.asfortranarray(np.full((N, N, N), u0)))
60 s.set_balanced_force_projection(on)
61 s.set_pressure_chebyshev(True, 500, 1e-9)
62 return s
63
64
65def run(cls, on, u0, steps, kappa=None):
66 s = build(cls, on, u0, kappa)
67 t, pit, bit = [], [], []
68 for k in range(steps):
69 t0 = time.perf_counter()
70 s.step()
71 t1 = time.perf_counter()
72 if k >= WARM:
73 t.append(t1 - t0)
74 pit.append(s.diagnostics.last_pressure_iterations())
75 bit.append(s.diagnostics.last_balanced_force_iterations())
76 u = max(np.max(np.abs(s.get_u())), np.max(np.abs(s.get_v())), np.max(np.abs(s.get_w())))
77 if not np.isfinite(u):
78 raise RuntimeError("non-finite velocity")
79 return np.median(t), np.mean(pit), np.mean(bit)
80
81
82def main():
83 ok = True
84 # static: the drop at rest, height-function curvature (parasitic currents keep the force
85 # changing at a small relative level); static-k: the same with a constant curvature 1/R, an
86 # exact equilibrium, where the increment solve must skip after the first step.
87 for case, u0, steps, kap in (("moving", U0, STEPS, None), ("static", 0.0, 50, None),
88 ("static-k", 0.0, 50, 1.0 / R)):
89 for name, cls in (("staggered", pf.Solver), ("collocated", pf.SolverColocated)):
90 res = {False: [], True: []}
91 for _ in range(REPEATS):
92 for on in (False, True):
93 res[on].append(run(cls, on, u0, steps, kap))
94 off = np.median([r[0] for r in res[False]])
95 onn = np.median([r[0] for r in res[True]])
96 p_off, p_on = res[False][0][1], res[True][0][1]
97 b_on = res[True][0][2]
98 ratio = onn / off
99 good = ratio <= 1.5 and b_on <= p_on
100 ok &= good
101 print(f"{case:8s} {name:10s} step OFF {1e3 * off:7.2f} ms ON {1e3 * onn:7.2f} ms "
102 f"ratio {ratio:.3f} iters: main OFF {p_off:.1f}, main ON {p_on:.1f}, balanced-force ON {b_on:.1f} "
103 f"{'ok' if good else 'MISS'}")
104 print("G7 PASS" if ok else "G7 MISS")
105 return 0 if ok else 1
106
107
108if __name__ == "__main__":
109 sys.exit(main())
run(cls, on, u0, steps, kappa=None)