110 """Volume fractions of a sphere, R = 4, 4x4x4 sub-sampling (frozen interface for the CSF)."""
112 off = (np.arange(sub) + 0.5) / sub
113 c = np.array([N / 2 + 0.13, N / 2 + 0.27, NZ / 2 + 0.11])
114 out = np.zeros((N, N, NZ))
118 X, Y, Z = np.meshgrid(np.arange(N) + a, np.arange(N) + b, np.arange(NZ) + d,
120 out += ((X - c[0]) ** 2 + (Y - c[1]) ** 2 + (Z - c[2]) ** 2 < 16.0)
121 return out / sub ** 3
234def g2(path, dt, walls=False):
235 """Returns (ok, text)."""
236 a, b = path.build(dt, walls), path.build(dt, walls)
237 rng = np.random.default_rng(12345)
238 x, y, z = np.meshgrid(np.arange(N), np.arange(N), np.arange(NZ), indexing=
"ij")
239 seed = EPS * (1.0 - 2.0 * ((x + y + z) % 2)) + EPS * rng.uniform(-1.0, 1.0, (N, N, NZ))
240 b.set_field(
"p",
F(np.asarray(b.get_field(
"p")) + seed))
241 b.diagnostics.exchange_field(
"p")
243 u_ref = p50 = u50 =
None
245 for k
in range(1, G2_STEPS + 1):
248 if k
in (1, 50, G2_STEPS):
249 dp =
cb_amp(np.asarray(b.get_p()) - np.asarray(a.get_p()))
250 du = float(np.max(np.abs(
vel(b) -
vel(a))))
251 if not (np.isfinite(dp)
and np.isfinite(du)):
252 return False, f
"dt {dt:<5g} NON-FINITE at step {k}"
258 m =
lambda v: max(v, fl)
259 ok = (m(p100) <= 2.0 * m(p_ref)
and m(p100) <= (1 + 1e-6) * m(p50)
and
260 m(u100) <= 2.0 * m(u_ref)
and m(u100) <= (1 + 1e-6) * m(u50))
261 return ok, (f
"dt {dt:<5g}{' walled' if walls else ''} P_cb {p_ref:.2e} -> {p50:.2e} -> {p100:.2e} "
262 f
"|du| {u_ref:.2e} -> {u50:.2e} -> {u100:.2e} {'ok' if ok else 'FAIL'}")
288 s = path.build(dt,
True)
290 if state
is not None:
292 elif path.off_twin
is not None and path.off_twin.g3_state
is not None:
295 restore(s, path.off_twin.g3_state)
297 u, k, conv =
march(path, s)
301 state = {n: np.asarray(s.get_field(n))
for n
in (
"u",
"v",
"w",
"p")}
302 path.g3_state = dict(state)
303 if "p_balanced" in s.field_names():
304 state[
"p_balanced"] = np.asarray(s.get_field(
"p_balanced"))
306 info.append(f
"dt {dt:g}{seeded}: {k} steps{'' if conv else ' (NOT converged)'}")
307 scale = max(np.max(np.abs(sols[G3_DTS[0]])), 1e-300)
309 for i, di
in enumerate(G3_DTS):
310 for dj
in G3_DTS[i + 1:]:
311 d = float(np.max(np.abs(sols[di] - sols[dj])))
312 worst = max(worst, d
if np.isfinite(d)
else np.inf)
314 ok = all(
"NOT" not in t
for t
in info)
and worst <= G3_GATE * scale
315 return ok, f
"max|u(dt_i) - u(dt_j)|/max|u| = {worst / scale:.2e} [{'; '.join(info)}] " + \
316 (
"ok" if ok
else "FAIL")