flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
vof_profile.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""WO-V9 — the VoF performance profile: where a two-phase step actually goes.
3
4Five cases, transcribed from the gallery drivers so the profile is of the code people run:
5
6 hysing1 Hysing et al. (2009) case 1, quasi-2D 64 x 4 x 128 (examples/rising-bubble)
7 packed the E7 packed column, 64 x 64 x 160, bubble + bed (examples/bubble-through-packing)
8 trickle the E6 trickle bed, 48 x 48 x 96, liquid distributor (examples/trickle-flow-packing)
9 droplet a 128^3 static droplet (examples/parasitic-currents)
10 scriven a 96^3 Scriven bubble, phase change (tests/study/vof_scriven.py)
11
12Each case runs `--warm` steps to fill caches and settle the adaptive dt, then `--steps` TIMED
13steps with `set_vof_timing(True)`. Reported: the step's three coarse phases (predictor / momentum
14solve / projection), the VoF stages (colour advection with its bridges split out, curvature, CSF,
15phase change), the advector's own kernels (reconstruct / fluxes / sweeps / clip / g=3 exchange),
16ms/step and the dt census (which of the two explicit limits binds).
17
18Rule 3b: every case records max pressure iterations against its cap; a capped solve is flagged.
19
20Usage:
21 PYTHONPATH=$PWD/build_cuda python tests/study/vof_profile.py --case all
22 PYTHONPATH=$PWD/build_cuda python tests/study/vof_profile.py --case hysing1 --worklist off
23"""
24import argparse
25import json
26import math
27import os
28import sys
29import time
30
31import numpy as np
32
33import peclet.flow as flow
34
35HERE = os.path.dirname(os.path.abspath(__file__))
36CACHE = os.environ.get("VOF_PROFILE_CACHE", os.path.join(HERE, ".vof_profile_cache"))
37
38
39# --------------------------------------------------------------------------- geometry helpers
40def cylinder_fractions(shape, R, cx, cz, sub=32):
41 """Volume fraction of a cylinder with its axis along y (a 2-D disc), exact in z."""
42 nx, ny, nz = shape
43 ax = (np.arange(nx)[:, None] + (np.arange(sub)[None, :] + 0.5) / sub).ravel()
44 half = np.sqrt(np.maximum(R * R - (ax - cx) ** 2, 0.0))
45 z0, z1 = cz - half, cz + half
46 C = np.zeros((nx, ny, nz))
47 for k in range(nz):
48 seg = np.maximum(np.minimum(z1, k + 1) - np.maximum(z0, k), 0.0)
49 C[:, :, k] = seg.reshape(nx, sub).mean(axis=1)[:, None]
50 return np.asfortranarray(C)
51
52
53def sphere_fractions(shape, R, c, sub=24):
54 """Liquid volume fraction of a sphere: exact in z, sub x sub subsampled in (x,y)."""
55 nx, ny, nz = shape
56 ax = (np.arange(nx)[:, None] + (np.arange(sub)[None, :] + 0.5) / sub).ravel()
57 ay = (np.arange(ny)[:, None] + (np.arange(sub)[None, :] + 0.5) / sub).ravel()
58 X, Y = ax[:, None], ay[None, :]
59 half = np.sqrt(np.maximum(R * R - (X - c[0]) ** 2 - (Y - c[1]) ** 2, 0.0))
60 z0, z1 = c[2] - half, c[2] + half
61 C = np.zeros((nx, ny, nz))
62 for k in range(nz):
63 seg = np.maximum(np.minimum(z1, k + 1) - np.maximum(z0, k), 0.0)
64 C[:, :, k] = seg.reshape(nx, sub, ny, sub).mean(axis=(1, 3))
65 return np.asfortranarray(C)
66
67
68def bubble_colour_sub(nx, nz, R, cz, sub=4):
69 """C = liquid fraction; the gas bubble is the C = 0 sphere at (nx/2, nx/2, cz)."""
70 q = (np.arange(sub) + 0.5) / sub
71 C = np.zeros((nx, nx, nz))
72 c = nx / 2.0
73 for k in range(nz):
74 pz = k + q
75 acc = np.zeros((nx, nx))
76 for a in q:
77 px = (np.arange(nx) + a)[:, None]
78 for b in q:
79 py = (np.arange(nx) + b)[None, :]
80 r2 = (px - c) ** 2 + (py - c) ** 2
81 acc += ((r2[:, :, None] + (pz - cz) ** 2) < R * R).sum(axis=2)
82 C[:, :, k] = 1.0 - acc / sub ** 3
83 return np.asfortranarray(C)
84
85
86def bed_sdf(centres, nx, nz, Rg, periodic_xy=True):
87 """Signed distance to a union of spheres, positive in the fluid; periodic in x and y."""
88 W = float(nx)
89 g, gz = np.arange(nx) + 0.5, np.arange(nz) + 0.5
90 X, Y, Z = np.meshgrid(g, g, gz, indexing="ij")
91 phi = np.full((nx, nx, nz), 1e30)
92 for k in range(len(centres)):
93 dx = X - centres[k, 0]
94 dy = Y - centres[k, 1]
95 if periodic_xy:
96 dx -= W * np.round(dx / W)
97 dy -= W * np.round(dy / W)
98 dz = Z - centres[k, 2]
99 phi = np.minimum(phi, np.sqrt(dx * dx + dy * dy + dz * dz) - Rg)
100 return np.asfortranarray(phi)
101
102
103def rsa_bed(n, W, R, z0, seed, gap=0.02):
104 """A deterministic random-sequential-addition sphere bed in a laterally periodic column.
105
106 A stand-in for the gallery pages' DEM-settled packings: same grain radius, same column, same
107 number of grains, and no `dem` import (which would put two Kokkos backends in one process).
108 The PROFILE does not depend on which loose packing it is -- only on the cut-cell count, which
109 is set by the grain count and radius.
110 """
111 rng = np.random.default_rng(seed)
112 pts = []
113 z = z0 + R
114 tries = 0
115 while len(pts) < n and tries < 400000:
116 tries += 1
117 p = np.array([rng.uniform(0, W), rng.uniform(0, W), rng.uniform(z0 + R, z0 + R + 3.6 * R)])
118 ok = True
119 for q in pts:
120 d = p - q
121 d[0] -= W * round(d[0] / W)
122 d[1] -= W * round(d[1] / W)
123 if d @ d < (2 * R * (1 + gap)) ** 2:
124 ok = False
125 break
126 if ok:
127 pts.append(p)
128 return np.array(pts)
129
130
131def cached(name, fn):
132 os.makedirs(CACHE, exist_ok=True)
133 f = os.path.join(CACHE, name + ".npy")
134 if os.path.exists(f):
135 return np.load(f)
136 a = fn()
137 np.save(f, a)
138 return a
139
140
141# --------------------------------------------------------------------------- the timing harness
142_LAST_DT = {"dt": 0.0}
143
144
145def set_dt(s, dt):
146 """Every picker goes through this so the harness can halve a rejected step's dt."""
147 _LAST_DT["dt"] = dt
148 s.set_dt(dt)
149def profile(s, nsteps, pick_dt, cap, label, extra=None):
150 """`pick_dt(s, i)` sets dt for step i and returns which limit bound it ('cfl'|'cap'|'fixed')."""
151 s.diagnostics.set_vof_timing(True)
152 binds = {"cfl": 0, "cap": 0, "fixed": 0}
153 itmax, capped = 0, 0
154 t0 = time.time()
155 retries = 0
156 for i in range(nsteps):
157 binds[pick_dt(s, i)] += 1
158 while True: # the WY cap is a hard throw, not a warning (vof_scriven's own pattern)
159 try:
160 s.step()
161 break
162 except RuntimeError as ex:
163 if "Weymouth-Yue boundedness cap" not in str(ex) or retries > 20:
164 raise
165 retries += 1
166 set_dt(s, 0.5 * _LAST_DT["dt"])
167 it = s.diagnostics.last_pressure_iterations()
168 itmax = max(itmax, it)
169 capped += int(it >= cap)
170 wall = time.time() - t0
171 tm = dict(s.diagnostics.vof_timing())
172 s.diagnostics.set_vof_timing(False)
173 tm["label"] = label
174 tm["wall"] = wall
175 tm["ms_per_step"] = 1e3 * wall / nsteps
176 tm["press_max"] = itmax
177 tm["press_cap"] = cap
178 tm["capped"] = capped
179 tm["binds_cfl"] = binds["cfl"]
180 tm["binds_capillary"] = binds["cap"]
181 tm["binds_fixed"] = binds["fixed"]
182 tm["dt_retries"] = retries
183 if extra:
184 tm.update(extra)
185 return tm
186
187
188def dt_from_limits(frac=0.4):
189 def pick(s, i):
190 L = s.vof_step_limits()
191 dt = frac * min(L["cfl_dt"], L["capillary_dt"])
192 set_dt(s, dt)
193 return "cap" if L["capillary_binds"] else "cfl"
194 return pick
195
196
197# --------------------------------------------------------------------------- the five cases
198class Scale:
199 """physical -> solver units, the gallery's Scale (cell size 1, time in seconds)."""
200
201 def __init__(self, s):
202 self.s = s
203
204 def mu(self, m):
205 return self.s ** 2 * m
206
207 def sigma(self, sg):
208 return self.s ** 3 * sg
209
210 def bodyforce(self, f):
211 return self.s * f
212
213
214HYSING1 = dict(rho1=1000.0, rho2=100.0, mu1=10.0, mu2=1.0, g=0.98, sigma=24.5)
215
216
217def case_hysing1(nx=64, momentum=True, worklist=True):
218 p = HYSING1
219 nz, ny = 2 * nx, 4
220 sc = Scale(nx / 1.0)
221 R = sc.s * 0.25
222 s = flow.Solver(nx, ny, nz)
223 s.set_rho(p["rho1"])
224 s.set_mu(sc.mu(p["mu1"]))
225 s.set_domain_bc("-z", "wall", (0, 0, 0))
226 s.set_domain_bc("+z", "wall", (0, 0, 0))
227 s.set_pressure_geometry(np.full((nx, ny, nz), 10.0, order="F"))
228 s.set_pressure_chebyshev(True, 600, 1e-12)
229 s.enable_vof()
230 s.diagnostics.set_vof_worklist(worklist)
231 s.set_vof(np.asfortranarray(
232 1.0 - cylinder_fractions((nx, ny, nz), R, nx / 2.0, sc.s * 0.5)))
233 s.set_property_model("rho", "linear", "C", [p["rho2"], p["rho1"] - p["rho2"]])
234 s.set_property_model("mu", "linear", "C",
235 [sc.mu(p["mu2"]), sc.mu(p["mu1"] - p["mu2"])])
236 s.set_surface_tension(sc.sigma(p["sigma"]))
237 if momentum:
238 s.enable_vof_momentum(p["rho2"], p["rho1"])
239 s.set_property_model("force_z", "linear", "C",
240 [-sc.bodyforce(p["rho2"] * p["g"]),
241 -sc.bodyforce((p["rho1"] - p["rho2"]) * p["g"])])
242 s.set_dt(0.5 * s.capillary_dt())
243 return s, dt_from_limits(0.4), 600, f"hysing1 {nx}x{ny}x{nz}"
244
245
246def case_packed(nx=64, nz=160, worklist=True):
247 """E7: a gas bubble released under a sphere bed in a closed, laterally periodic column."""
248 NG, RJ, FSDF = 26, 0.171875 * nx, 0.85
249 RG = FSDF * RJ
250 ZBED, RBUB, ZBUB = 0.5625 * nx, 0.15625 * nx, 0.28125 * nx
251 ctr = cached(f"bed_e7_{nx}_{nz}", lambda: rsa_bed(NG, float(nx), RJ, ZBED, seed=11))
252 SDF = cached(f"sdf_e7_{nx}_{nz}", lambda: bed_sdf(ctr, nx, nz, RG))
253 ratio, Eo, Mo, g, theta = 100.0, 10.0, 1e-3, 2e-4, 60.0
254 D = 2 * RBUB
255 rho_l, rho_g = 1.0, 1.0 / ratio
256 drho = rho_l - rho_g
257 sigma = drho * g * D * D / Eo
258 mu_l = (Mo * rho_l ** 2 * sigma ** 3 / (g * drho)) ** 0.25
259 mu_g = mu_l / ratio
260 s = flow.Solver(nx, nx, nz)
261 s.set_rho(rho_l)
262 s.set_mu(mu_l)
263 s.set_domain_bc("-z", "wall", (0, 0, 0))
264 s.set_domain_bc("+z", "wall", (0, 0, 0))
265 s.set_solid(np.asfortranarray(SDF), cutcell_pressure=True)
266 s.enable_vof()
267 s.diagnostics.set_vof_worklist(worklist)
268 s.set_vof(cached(f"c0_e7_{nx}_{nz}", lambda: bubble_colour_sub(nx, nz, RBUB, ZBUB)))
269 s.set_property_model("rho", "linear", "C", [rho_g, rho_l - rho_g])
270 s.set_property_model("mu", "linear", "C", [mu_g, mu_l - mu_g])
271 s.set_surface_tension(sigma)
272 s.set_contact_angle(theta)
273 s.enable_vof_momentum(rho_g, rho_l)
274 s.set_property_model("force_z", "linear", "C", [-rho_g * g, -(rho_l - rho_g) * g])
275 s.set_pressure_chebyshev(True, 600, 1e-12)
276 s.set_dt(0.4 * s.capillary_dt())
277 return s, dt_from_limits(0.4), 600, f"packed {nx}x{nx}x{nz}"
278
279
280def case_trickle(nx=48, nz=96, worklist=True):
281 """E6: a liquid distributor over a dry sphere bed, outflow at the bottom."""
282 RHO_L, MU_L, MU_RATIO, BO, THETA = 100.0, 50.0, 50.0, 1.2, 60.0
283 R48, ZBED0, NG = 7.7, 22.0, 34
284 sc = nx / 48.0
285 R = R48 * sc
286 ctr = cached(f"bed_e6_{nx}_{nz}", lambda: rsa_bed(NG, float(nx), R, ZBED0 * (nz / 96.0), 7))
287 SDF = cached(f"sdf_e6_{nx}_{nz}", lambda: bed_sdf(ctr, nx, nz, R))
288 ratio = 100.0
289 rho_g, mu_g = RHO_L / ratio, MU_L / MU_RATIO
290 drho = RHO_L - rho_g
291 delta, umax = 3.0 * sc, 0.3 * sc
292 g = 2 * MU_L * umax / (drho * delta ** 2)
293 dp = 2 * R
294 sigma = drho * g * dp ** 2 / BO
295 rd = 8.0 * sc
296 s = flow.Solver(nx, nx, nz)
297 s.set_rho(RHO_L)
298 s.set_mu(MU_L)
299 for f in ("-x", "+x", "-y", "+y"):
300 s.set_domain_bc(f, "periodic", (0, 0, 0))
301 s.set_domain_bc("-z", "outflow", (0, 0, 0))
302 xc = (np.arange(nx) + 0.5)[:, None]
303 yc = (np.arange(nx) + 0.5)[None, :]
304 disc = ((xc - nx / 2) ** 2 + (yc - nx / 2) ** 2) < rd ** 2
305 prof = np.zeros((nx, nx, 3))
306 prof[:, :, 2] = np.where(disc, -umax, 0.0)
307 s.set_domain_bc_profile("+z", np.ascontiguousarray(prof))
308 s.diagnostics.set_velocity_solver_params(60)
309 s.set_pressure_multigrid(True, levels=6)
310 s.set_pressure_solver_params(80)
311 s.set_solid(np.asfortranarray(SDF), cutcell_pressure=True)
312 s.enable_vof()
313 s.diagnostics.set_vof_worklist(worklist)
314 s.set_vof(np.zeros((nx, nx, nz), order="F"))
315 s.set_surface_tension(sigma)
316 s.set_contact_angle(THETA)
317 s.set_property_model("rho", "linear", "C", [rho_g, RHO_L - rho_g])
318 s.set_property_model("mu", "linear", "C", [mu_g, MU_L - mu_g])
319 s.set_property_model("force_z", "linear", "C", [0.0, -drho * g])
320 s.enable_vof_momentum(rho_g, RHO_L)
321 s.set_pressure_fcg(True, 400, 1e-11)
322 s.set_vof_inflow_profile("+z", np.ascontiguousarray(disc.astype(float)))
323 s.set_vof_backflow("-z", 0.0)
324 dt_cap = 0.5 * s.capillary_dt()
325 s.set_dt(dt_cap)
326
327 def pick(sv, i):
328 L = sv.vof_step_limits()
329 dt = min(dt_cap, 0.4 * L["cfl_dt"]) if L["cfl_dt"] > 0 else dt_cap
330 set_dt(sv, dt)
331 return "cap" if dt >= dt_cap - 1e-30 else "cfl"
332
333 return s, pick, 400, f"trickle {nx}x{nx}x{nz}"
334
335
336def case_droplet(n=128, worklist=True):
337 """A stationary droplet, D/dx = n/2, in an n^3 periodic box (the parasitic-currents case)."""
338 MU, SIGMA = 0.1, 1.0
339 R = n / 4.0
340 off = (0.13, 0.27, 0.11)
341 s = flow.Solver(n, n, n)
342 s.set_rho(1.0)
343 s.set_mu(MU)
344 s.set_pressure_geometry(np.full((n, n, n), 10.0, order="F"))
345 s.set_pressure_chebyshev(True, 500, 1e-14)
346 s.enable_vof()
347 s.diagnostics.set_vof_worklist(worklist)
348 s.set_vof(cached(f"drop_{n}", lambda: sphere_fractions(
349 (n, n, n), R, (n / 2 + off[0], n / 2 + off[1], n / 2 + off[2]))))
350 s.set_property_model("rho", "linear", "C", [1.0, 0.0])
351 s.set_surface_tension(SIGMA)
352 dt = 0.5 * s.capillary_dt()
353 s.set_dt(dt)
354
355 def pick(sv, i):
356 set_dt(sv, dt)
357 return "fixed"
358
359 return s, pick, 500, f"droplet {n}^3"
360
361
362def case_scriven(n=96, ja=0.5, ratio=10.0, r0=6.0, worklist=True):
363 """WO-P23 rung P3: a Scriven bubble growing in superheated liquid (the phase-change pipeline)."""
364 sys.path.insert(0, HERE)
365 import vof_scriven as vs
366
367 rr = 1.0 / ratio
368 beta = vs.scriven_beta(ja, rr)
369 t0 = (r0 / (2 * beta)) ** 2
370 rho_l, rho_v, cpl = 1.0, rr, 1.0
371 alpha_l = 1.0
372 k_l = alpha_l * rho_l * cpl
373 k_v = k_l / ratio
374 dT = 1.0
375 h_lv = rho_l * cpl * dT / (rho_v * ja)
376 ctr = 0.5 * n
377 s = flow.Solver(n, n, n)
378 s.set_rho(rho_l)
379 s.set_mu(1e-3)
380 for f in ("-x", "+x", "-y", "+y", "-z", "+z"):
381 s.set_domain_bc(f, "outflow")
382 s.set_pressure_geometry(np.full((n, n, n), 1.0, order="F"))
383 s.enable_vof()
384 s.diagnostics.set_vof_worklist(worklist)
385 s.set_vof(cached(f"scriven_c_{n}_{r0}", lambda: vs.sphere_colour_chunked(n, ctr, r0, 4)))
386 s.set_property_model("rho", "linear", "C", [rho_v, rho_l - rho_v])
387 s.set_pressure_fcg(True, 600, 1e-10)
388 s.add_scalar("T", k_l / (rho_l * cpl), 1, 200)
389 for f in ("-x", "+x", "-y", "+y", "-z", "+z"):
390 s.set_scalar_bc("T", f, "dirichlet", dT)
391 s.set_field("T", np.asfortranarray(cached(
392 f"scriven_T_{n}_{r0}_{ja}",
393 lambda: vs.initial_temperature(n, ctr, r0, beta, rr, dT, mode="similarity"))))
394 s.enable_phase_change(rho_v, rho_l, h_lv)
395 s.set_phase_change_thermal(True, "T", 0.0, k_v, k_l, 0.0)
396 s.diagnostics.set_phase_change_plane_dirichlet(True)
397 s.set_phase_change_energy(True, rho_v * cpl, rho_l * cpl)
398 s.diagnostics.set_phase_change_quadratic_fit(True)
399 Rd = beta * math.sqrt(alpha_l / t0)
400 dt = 0.4 * 0.2 / max(Rd * (1.0 - rr), 1e-30)
401 s.set_dt(dt)
402
403 # vof_scriven's own adaptive rule: predict, then correct from the LAST realised Courant
404 # number (the a-priori continuum interface speed under-predicts the discrete field by ~2x on
405 # the first steps, and vof_step_limits' cfl_dt is not defined before the first advection).
406 st = {"dt": 0.25 * dt}
407
408 def pick(sv, i):
409 if i > 0:
410 c = sv.diagnostics.vof_last_courant()
411 st["dt"] *= min(1.2, max(0.5, 0.2 / max(c, 1e-30)))
412 set_dt(sv, st["dt"])
413 return "cfl"
414
415 return s, pick, 600, f"scriven {n}^3 Ja={ja}"
416
417
418CASES = {
419 "hysing1": case_hysing1,
420 "packed": case_packed,
421 "trickle": case_trickle,
422 "droplet": case_droplet,
423 "scriven": case_scriven,
424}
425
426
427def show(tm):
428 n = max(tm["steps"], 1)
429 st = tm["step"] / n
430 def pc(x):
431 return 100.0 * (x / n) / st if st > 0 else 0.0
432 vof = (tm["vof_advect"] + tm["vof_momentum_advect"] + tm["curvature"] + tm["csf"]
433 + tm["phase_change"])
434 print(f"\n=== {tm['label']} ===")
435 print(f" steps {n} {tm['ms_per_step']:8.2f} ms/step (wall) "
436 f"{1e3*st:8.2f} ms/step (timed) pressure {tm['press_max']}/{tm['press_cap']}"
437 + (" *** CAPPED ***" if tm["capped"] else ""))
438 print(f" dt census: CFL binds {tm['binds_cfl']}, capillary binds {tm['binds_capillary']}, "
439 f"fixed {tm['binds_fixed']}")
440 rows = [
441 ("VoF total", vof),
442 (" colour advect", tm["vof_advect"]),
443 (" of which bridges", tm["vof_bridge"]),
444 (" momentum advect", tm["vof_momentum_advect"]),
445 (" of which bridges", tm["vof_momentum_bridge"]),
446 (" curvature", tm["curvature"]),
447 (" CSF", tm["csf"]),
448 (" phase change", tm["phase_change"]),
449 ("kernels: reconstruct", tm["k_reconstruct"]),
450 ("kernels: fluxes", tm["k_fluxes"]),
451 ("kernels: sweeps", tm["k_sweep"]),
452 ("kernels: clip", tm["k_clip"]),
453 ("kernels: g=3 exchange", tm["k_exchange"]),
454 ("kernels: freeze", tm["k_freeze"]),
455 ("curv: compact", tm["kc_compact"]),
456 ("curv: planes", tm["kc_planes"]),
457 ("curv: height", tm["kc_height"]),
458 ("curv: fallback", tm["kc_fallback"]),
459 ("curv: census", tm["kc_census"]),
460 ("predictor", tm["predictor"]),
461 ("momentum solve", tm["momentum_solve"]),
462 ("projection", tm["projection"]),
463 ("step (total)", tm["step"]),
464 ]
465 for name, v in rows:
466 print(f" {name:<28s} {1e3*v/n:9.3f} ms/step {pc(v):6.2f} %")
467 other = tm["step"] - (vof + tm["predictor"] + tm["momentum_solve"] + tm["projection"])
468 print(f" {'remainder':<28s} {1e3*other/n:9.3f} ms/step {pc(other):6.2f} %")
469
470
471def main():
472 ap = argparse.ArgumentParser()
473 ap.add_argument("--case", default="all")
474 ap.add_argument("--steps", type=int, default=20)
475 ap.add_argument("--warm", type=int, default=5)
476 ap.add_argument("--worklist", choices=("on", "off", "both"), default="on")
477 ap.add_argument("--curv-worklist", choices=("on", "off", "both"), default="on")
478 ap.add_argument("--json", default="")
479 a = ap.parse_args()
480 names = list(CASES) if a.case == "all" else a.case.split(",")
481 wls = [True, False] if a.worklist == "both" else [a.worklist == "on"]
482 cwls = [True, False] if a.curv_worklist == "both" else [a.curv_worklist == "on"]
483 out = []
484 for nm in names:
485 for cwl in cwls:
486 for wl in wls:
487 s, pick, cap, label = CASES[nm](worklist=wl)
488 s.diagnostics.set_vof_curvature_worklist(cwl)
489 label += "" if cwl else " [curv worklist OFF]"
490 for i in range(a.warm):
491 pick(s, i)
492 s.step()
493 tm = profile(s, a.steps, pick, cap,
494 label + ("" if wl else " [worklist OFF]"),
495 extra={"case": nm, "worklist": wl, "curv_worklist": cwl})
496 show(tm)
497 out.append(tm)
498 del s
499 if a.json:
500 with open(a.json, "w") as f:
501 json.dump(out, f, indent=1)
502 print(f"\nwrote {a.json}")
503
504
505if __name__ == "__main__":
506 main()
case_scriven(n=96, ja=0.5, ratio=10.0, r0=6.0, worklist=True)
rsa_bed(n, W, R, z0, seed, gap=0.02)
case_hysing1(nx=64, momentum=True, worklist=True)
case_droplet(n=128, worklist=True)
cached(name, fn)
bed_sdf(centres, nx, nz, Rg, periodic_xy=True)
dt_from_limits(frac=0.4)
bubble_colour_sub(nx, nz, R, cz, sub=4)
profile(s, nsteps, pick_dt, cap, label, extra=None)
case_packed(nx=64, nz=160, worklist=True)
set_dt(s, dt)
case_trickle(nx=48, nz=96, worklist=True)
static double mean(const std::vector< double > &a)