41def run(nx, ny, nz, arm, L, cfg):
42 """One rung. `arm=False` is the cell-unit cubic control; `arm=True` arms the cube L^3."""
43 R = (PHI * 3.0 / (4.0 * np.pi)) ** (1.0 / 3.0) * L
44 levels = max(2, int(np.floor(np.log2(nx))) - 1)
45 kw = {}
if not arm
else {
"extent": (L, L, L),
"origin": (0.0, 0.0, 0.0)}
46 s = sdflow.Solver((nx, ny, nz), **kw)
47 s.set_rho(cfg[
"rho"]); s.set_mu(cfg[
"mu"]); s.set_dt(cfg[
"dt"])
48 s.set_body_force((cfg[
"F"], 0.0, 0.0))
49 s.set_advection(
False)
50 s.diagnostics.set_velocity_solver_params(cfg[
"vel_sweeps"])
51 s.set_pressure_multigrid(
True, levels=levels)
52 s.set_pressure_pcg(
True, cfg[
"pcg_maxit"], cfg[
"pcg_rtol"])
53 cx, cy, cz = s.cell_centers()
55 X, Y, Z = np.meshgrid(cx, cy, cz, indexing=
"ij")
56 sdf = np.sqrt((X - c) ** 2 + (Y - c) ** 2 + (Z - c) ** 2) - R
57 s.set_solid(np.asfortranarray(sdf), cutcell_pressure=
True)
60 prev, steps, p_iters = 0.0, 0, []
61 for it
in range(cfg[
"max_steps"]):
64 p_iters.append(s.diagnostics.last_pressure_iterations())
65 if it % cfg[
"check_every"] == cfg[
"check_every"] - 1:
66 m = float(s.get_u().
mean())
67 if it >= cfg[
"min_steps"]
and abs(m - prev) < cfg[
"conv_tol"] * (abs(m) + 1e-30):
70 wall = time.time() - t0
71 umean = float(s.get_u().
mean())
72 K = cfg[
"F"] * L ** 3 / (6.0 * np.pi * cfg[
"mu"] * R * umean)
73 half = p_iters[len(p_iters) // 2:]
74 return {
"K": K,
"iters": float(np.median(half)),
"steps": steps,
75 "div": float(s.max_open_divergence()),
"spacing": tuple(s.spacing),
76 "aniso": len(set(s.spacing)) > 1,
"wall": wall}
80 ap = argparse.ArgumentParser()
81 ap.add_argument(
"--grids", default=
"16,24,32,48,64")
83 grids = [int(g)
for g
in a.grids.split(
",")]
86 print(f
"Zick & Homsy sphere, phi = {PHI}, K_ref = {K_REF}")
87 print(f
"{'N':>4} {'cells (stretched)':>20} {'spacing':>20} "
88 f
"{'K_cubic':>10} {'K_stretch':>10} {'err_c %':>9} {'err_s %':>9} "
89 f
"{'it_c':>5} {'it_s':>5} {'steps_s':>8}")
90 Kc, Ks, itc, its = [], [], [], []
93 c =
run(N, N, N,
False, float(N), cfg)
94 s =
run(N, 2 * N, N // 2,
True, float(N), cfg)
95 Kc.append(c[
"K"]); Ks.append(s[
"K"]); itc.append(c[
"iters"]); its.append(s[
"iters"])
96 ec = 100.0 * abs(c[
"K"] - K_REF) / K_REF
97 es = 100.0 * abs(s[
"K"] - K_REF) / K_REF
98 sp =
"(%.4g, %.4g, %.4g)" % s[
"spacing"]
99 print(f
"{N:>4} {f'{N}x{2*N}x{N//2}':>20} {sp:>20} "
100 f
"{c['K']:>10.5f} {s['K']:>10.5f} {ec:>9.4f} {es:>9.4f} "
101 f
"{c['iters']:>5.1f} {s['iters']:>5.1f} {s['steps']:>8d}")
103 print(
" FAIL: the stretched rung did not arm an anisotropic metric")
105 if s[
"iters"] > c[
"iters"] + 2:
106 print(f
" FAIL: pressure iters/step {s['iters']} > cubic {c['iters']} + 2")
109 pc, Kc_inf = fit_order(grids, Kc)
110 ps, Ks_inf = fit_order(grids, Ks)
111 ec_inf = abs(Kc_inf - K_REF) / K_REF
112 es_inf = abs(Ks_inf - K_REF) / K_REF
114 print(f
" cubic order p = {pc:.4f} K_inf = {Kc_inf:.5f} |K_inf - K_ref|/K_ref = "
115 f
"{100*ec_inf:.4f} %")
116 print(f
" stretched order p = {ps:.4f} K_inf = {Ks_inf:.5f} |K_inf - K_ref|/K_ref = "
117 f
"{100*es_inf:.4f} %")
119 if not (es_inf <= 0.02):
120 print(f
" FAIL: extrapolated K_s,inf off by {100*es_inf:.4f} % (bound 2 %)"); ok =
False
121 if not (1.5 <= ps <= 2.5):
122 print(f
" FAIL: stretched order {ps:.4f} outside [1.5, 2.5]"); ok =
False
123 print(
"PASS" if ok
else "FAIL")
124 return 0
if ok
else 1