90def assemble_fv(G, ap_x, ap_y, cells, wall_coef=None):
91 """-sum_f a_f (cQ-cP)/h^2 on the index set `cells`; optional extra diagonal (wall flux)."""
93 idx = -np.ones((n, n), int)
94 idx[cells] = np.arange(cells.sum())
95 rows, cols, vals = [], [], []
96 diag = np.zeros(cells.sum())
98 for (A, sh)
in ((ap_x, (1, 0)), (ap_y, (0, 1))):
100 a = A[1:-1, :]; P = idx[:-1, :]; Q = idx[1:, :]
102 a = A[:, 1:-1]; P = idx[:, :-1]; Q = idx[:, 1:]
103 m = (a > 0) & (P >= 0) & (Q >= 0)
104 a, P, Q = a[m] / h**2, P[m], Q[m]
105 rows += [P, Q]; cols += [Q, P]; vals += [-a, -a]
106 np.add.at(diag, P, a); np.add.at(diag, Q, a)
107 if wall_coef
is not None:
108 diag += wall_coef[cells]
109 rows.append(np.arange(len(diag))); cols.append(np.arange(len(diag))); vals.append(diag)
110 K = sp.csr_matrix((np.concatenate(vals), (np.concatenate(rows), np.concatenate(cols))),
111 shape=(len(diag),) * 2)
116 """Cell-centred 5-point -lap on centre-in-fluid cells, Dirichlet 0 at the SDF crossing."""
119 idx = -np.ones((n, n), int)
120 idx[cells] = np.arange(cells.sum())
122 K = sp.lil_matrix((N, N))
123 for i, j
in zip(*np.nonzero(cells)):
125 for (di, dj)
in ((1, 0), (-1, 0), (0, 1), (0, -1)):
126 q = idx[i + di, j + dj]
128 K[r, q] -= 1 / h**2; K[r, r] += 1 / h**2
130 th = G.sdf[i, j] / (G.sdf[i, j] - G.sdf[i + di, j + dj])
131 th = max(th, theta_min)
132 far = idx[i - di, j - dj]
133 if kind ==
"lin" or far < 0:
134 K[r, r] += 1 / (th * h**2)
136 D = th * (1 + th); Nc = 2 * (th * th - 1); Nnb = th * (1 - th)
138 K[r, r] -= (Nc / D - 1) / h**2
139 K[r, far] -= (Nnb / D) / h**2
140 return K.tocsr(), idx
143def smallest_eig(K, Mdiag, k=2):
144 """Smallest eigenvalues of K v = mu M v (M diagonal > 0)."""
145 s = 1 / np.sqrt(Mdiag)
146 A = sp.diags(s) @ K @ sp.diags(s)
147 sym = abs(A - A.T).max() < 1e-9 * abs(A).max()
149 w = spla.eigsh(A, k=k, sigma=-1e-3, which=
"LM", return_eigenvectors=
False)
151 w = spla.eigs(A, k=k, sigma=-1e-3, which=
"LM", return_eigenvectors=
False).real
167def run(ND, off, Pes=(10.0, 1000.0)):
171 cells = G.kap > 1e-12
172 K, idx = assemble_fv(G, G.ax, G.ay, cells)
173 kap = G.kap[cells]; ub = G.ubar[cells]
174 for tag, M
in ((
"FV-ap/kappa", kap), (
"FV-ap/1", np.ones_like(kap))):
175 w = smallest_eig(K, M, k=2)
176 out[(
"N-eig", tag)] = w[1]
178 U = (M * ub).sum() / M.sum()
180 b = spla.spsolve((K + 1e-14 * sp.eye(K.shape[0])).tocsc(), rhs)
181 b -= (M * b).sum() / M.sum()
182 out[(
"TA", tag)] = -(M * (ub - U) * b).sum() / M.sum()
184 axb = np.zeros_like(G.ax); ayb = np.zeros_like(G.ay)
185 axb[1:-1, :] = (sc[1:, :] & sc[:-1, :]); ayb[:, 1:-1] = (sc[:, 1:] & sc[:, :-1])
186 Ks, _ = assemble_fv(G, axb, ayb, sc)
187 w = smallest_eig(Ks, np.ones(sc.sum()), k=2)
188 out[(
"N-eig",
"stair")] = w[1]
189 u_c = 2 * (1 - G.sdf * 0 - (R - G.sdf) ** 2)
191 bb = spla.spsolve((Ks + 1e-14 * sp.eye(Ks.shape[0])).tocsc(), -(u_c[sc] - U))
193 out[(
"TA",
"stair")] = -((u_c[sc] - U) * bb).
mean()
198 wallc = np.zeros((n, n))
199 for (di, dj, A, ii, jj)
in ((1, 0, G.ax, 1, 0), (-1, 0, G.ax, 0, 0), (0, 1, G.ay, 0, 1), (0, -1, G.ay, 0, 0)):
202 for i, j
in zip(*np.nonzero(sc)):
203 for (di, dj, a)
in ((1, 0, G.ax[i + 1, j]), (-1, 0, G.ax[i, j]), (0, 1, G.ay[i, j + 1]), (0, -1, G.ay[i, j])):
204 if not sc[i + di, j + dj]:
205 wallc[i, j] += a / G.h**2
206 Km, _ = assemble_fv(G, G.ax, G.ay, sc, wall_coef=wallc)
207 meth = {
"mask-ap": (Km, np.ones(sc.sum()), u_c[sc])}
208 for kind
in (
"lin",
"quad"):
210 meth[
"ghost-" + kind] = (Kg, np.ones(sc.sum()), u_c[sc])
211 wc = np.where(G.kap > 1e-12, G.Aw / (G.h**2 * np.maximum(G.dwall, 1e-3 * G.h)), 0)
213 Kf, _ = assemble_fv(G, G.ax, G.ay, cells, wall_coef=wc)
214 meth[
"FV-wall"] = (Kf, kap, ub)
215 for tag, (KK, M, u)
in meth.items():
216 out[(
"D-eig", tag)] = smallest_eig(KK, M, k=1)[0]
218 out[(f
"NuF{Pe:g}", tag)] =
graetz(KK, M, u, Pe)