28def model(N, rho, mu, walls=False):
29 n, I = N * N, np.eye(N * N)
33 for x, y
in itertools.product(range(N), repeat=2):
34 S[x + N * y, (x + dx) % N + N * ((y + dy) % N)] = 1
36 Sm, Sp = [sh(-1, 0), sh(0, -1)], [sh(1, 0), sh(0, 1)]
37 o = [np.ones(n), np.ones(n)]
43 for S, op
in ((Sm[a], o[a][i]), (Sp[a], (Sp[a] @ o[a])[i])):
44 j = int(np.argmax(S[i]))
50 rf = [0.5 * (rho + Sm[a] @ rho)
for a
in range(2)]
51 G = [I - Sm[a]
for a
in range(2)]
52 D = [Sp[a] - I
for a
in range(2)]
53 Pi = [0.5 * (I + Sm[a])
for a
in range(2)]
54 R = [0.5 * (I + Sp[a])
for a
in range(2)]
55 Lpi = np.linalg.pinv(sum(D[a] @ np.diag(o[a] / rf[a]) @ G[a]
for a
in range(2)), rcond=1e-12)
60 n, I, rho, o, rf, G, D, Pi, R, L, Lpi = (m[k]
for k
in "n I rho o rf G D Pi R L Lpi".split())
61 Ai, Z, T = np.linalg.inv(np.diag(rho / dt) - L), np.zeros((n, n)), np.zeros((3 * n, 3 * n))
62 Fp = [np.diag(o[a] / rf[a]) @ G[a]
for a
in range(2)]
65 blk = [Z, Z, Z
if scheme ==
"V8" else -Ai @ np.diag(rho) @ R[a] @ Fp[a]]
66 blk[a] = Ai @ np.diag(rho / dt)
68 C2F = np.diag(1 / rf[a]) @ Pi[a] @ np.diag(rho)
if scheme ==
"D3" else Pi[a]
69 f = [np.diag(o[a]) @ C2F @ b
for b
in blk]
71 f[2] = f[2] - dt * Fp[a]
73 div = [D[0] @ uf[0][k] + D[1] @ uf[1][k]
for k
in range(3)]
74 phi = [Lpi @ d
for d
in div]
77 acc = -Fp[a] @ phi[k] + (-dt * Fp[a]
if (scheme ==
"V8" and k == 2)
else 0)
78 T[a * n:(a + 1) * n, k * n:(k + 1) * n] = (us[a][k] + R[a] @ acc
if scheme ==
"V8"
79 else us[a][k] - R[a] @ Fp[a] @ phi[k])
81 T[2 * n:, k * n:(k + 1) * n] = phi[k] / dt - kap * div[k] + (I
if k == 2
else 0)
91 """Cell density field (x fastest, index x + N*y)."""
92 x, y = np.meshgrid(np.arange(N) + 0.5, np.arange(N) + 0.5, indexing=
"xy")
96 r = np.where((y >= N / 4) & (y < 3 * N / 4), ratio, 1.0)
98 r = np.where((x - 0.47 * N) ** 2 + (y - 0.53 * N) ** 2 < (0.3 * N) ** 2, ratio, 1.0)
100 rng = np.random.default_rng(seed)
101 f = rng.standard_normal((N, N))
103 f = 0.2 * (f + np.roll(f, 1, 0) + np.roll(f, -1, 0) + np.roll(f, 1, 1) +
105 r = np.where(f > 0.0, ratio, 1.0)
106 return r.reshape(-1).astype(np.float64)
110 np.set_printoptions(precision=6)
112 print(
"1. V8 (retired), uniform rho, periodic, kappa = mu: radius vs 8 mu dt")
114 for dt
in (0.01, 0.1, 0.2, 1.0):
115 print(f
" dt {dt:<5g} radius {radius(step_matrix(m, dt, mu, 'V8')):.6f} "
116 f
"(8 mu dt = {8 * mu * dt:.4f})")
117 print(
"2. D2 (arithmetic centre->face, predictor form), ratio 1e4, kappa = mu")
118 for kind
in (
"disk",
"random"):
119 for walls
in (
False,
True):
122 print(f
" {kind:6s} walls={walls!s:5s} dt 3: {rr[0]:.6f} dt 10: {rr[1]:.6f}")
123 print(
"3. D3 (chosen: momentum-weighted centre->face), kappa = mu and 0")
125 for kind
in (
"uniform",
"slab",
"disk",
"random"):
126 for walls
in (
False,
True):
127 for ratio
in (10.0, 1e3, 1e6):
129 for dt
in (0.01, 1.0, 100.0, 1e4):
130 for kap
in (mu, 0.0):
132 print(f
" worst radius over uniform/slab/disk/random x periodic/walled x ratio 10..1e6 x "
133 f
"dt 0.01..1e4 x kappa {{mu, 0}}: {worst:.9f}")