38 """The oracle's sphere / bed cases (anderson_oracle.main's geometry), returning (solver,
39 oracle kwargs, settings, K-of-monitor)."""
40 st = AO.settings_of(settings)
41 if a.case ==
"sphere":
43 R = (3.0 * phi / (4.0 * math.pi)) ** (1.0 / 3.0) * L
45 def sdf_fn(cx, cy, cz):
46 X, Y, Z = np.meshgrid(cx, cy, cz, indexing=
"ij")
47 return np.sqrt((X - 0.5) ** 2 + (Y - 0.5) ** 2 + (Z - 0.5) ** 2) - R
50 return L ** 3 / (6.0 * math.pi * a.mu * R * u)
52 from scipy.spatial
import cKDTree
53 z = np.load(a.arrangement)
54 cen, rad, L = np.asarray(z[
"centres"], float), np.asarray(z[
"radii"], float), float(z[
"L"])
56 def sdf_fn(cx, cy, cz):
57 X, Y, Z = np.meshgrid(cx, cy, cz, indexing=
"ij")
58 pts = np.stack([X.ravel(order=
"C"), Y.ravel(order=
"C"), Z.ravel(order=
"C")], axis=1)
59 d, idx = cKDTree(np.mod(cen, L), boxsize=L).query(np.mod(pts, L), k=min(4, len(cen)))
60 return (d - rad[idx]).min(axis=1).reshape(X.shape, order=
"C")
63 return L ** 3 / (3.0 * math.pi * a.mu * 2.0 * float(rad.mean()) * u * len(rad))
65 dt = a.dt
if a.dt
is not None else a.beta * h * h / a.mu
66 s, okw = AO.build_solver(flow, a.scheme, a.N, sdf_fn, L, rho=1.0, mu=a.mu, F=1.0, dt=dt,
67 advection=a.advection, settings=st)
77 names = [
"u",
"v",
"w",
"p"]
78 views = [s.diagnostics.field_view(n)
for n
in names]
79 fluid =
inner(s.diagnostics.field_view(
"sdf")) > 0.0
81 save_at = {int(k)
for k
in a.save.split(
",")}
if a.save
else set()
82 x = [
inner(v)
for v
in views]
84 for k
in range(1, a.steps + 1):
86 y = [
inner(v)
for v
in views]
87 r = [y[f] - x[f]
for f
in range(4)]
88 rp = r[3][fluid] - r[3][fluid].
mean()
89 rows.append((k, *[float(np.sum(r[f] ** 2))
for f
in range(3)], cP * cP * float(np.sum(rp ** 2)),
90 float(sum(np.sum(y[f] ** 2)
for f
in range(3))), float(s.get_u().
mean())))
92 saved[f
"r{k}"] = np.stack(r)
95 W = np.sqrt(rows[:, 1:5].sum(1) / rows[:, 5])
96 V = np.sqrt(rows[:, 1:4].sum(1) / rows[:, 5])
97 P = np.sqrt(rows[:, 4] / rows[:, 5])
98 ref = a.ref
if a.ref
else rows[-1, 6]
99 print(f
"reference monitor {ref:.12e} (K {K(ref):.8f})")
100 marks = [k
for k
in (1, 10, 50, 100, 150, 200, 250, 300, 325, 400, 500, 600, 800, 1000, 1200,
101 1500, 2000)
if k <= a.steps]
103 print(f
"step {k:5d} W {W[k-1]:.3e} vel {V[k-1]:.3e} pres {P[k-1]:.3e} "
104 f
"monitor err {abs(rows[k-1, 6] / ref - 1):.1e}")
105 for k0, k1
in zip(marks[:-1], marks[1:]):
107 rt =
lambda q: (q[k1 - 1] / q[k0 - 1]) ** (1.0 / (k1 - k0))
108 print(f
"rate {k0:4d}-{k1:4d}: W {rt(W):.6f} vel {rt(V):.6f} pres {rt(P):.6f}")
114 from scipy.sparse
import coo_matrix
115 from scipy.sparse.csgraph
import connected_components
117 idx = np.arange(fluid.size).reshape(n, order=
"F")
118 opn = [np.roll(np.asarray(o).reshape(n, order=
"F"), -1, axis=ax)
119 for ax, o
in enumerate((s.get_ox(), s.get_oy(), s.get_oz()))]
126 opp = [np.roll(np.asarray(o).reshape(n, order=
"F"), -1, axis=ax)
127 for ax, o
in enumerate((s.get_ox_proj(), s.get_oy_proj(), s.get_oz_proj()))]
131 node |= o | np.roll(o, 1, axis=ax)
132 for th, nodes
in ((0.0, node), (0.0, fluid), (1e-2, fluid), (5e-2, fluid), (0.1, fluid),
136 o = opp[ax]
if nodes
is node
else opn[ax]
137 m = (o > th) & nodes & np.roll(nodes, -1, axis=ax)
139 Jj.append(np.roll(idx, -1, axis=ax)[m])
140 Ii, Jj = np.concatenate(Ii), np.concatenate(Jj)
141 A = coo_matrix((np.ones(Ii.size), (Ii, Jj)), shape=(fluid.size, fluid.size))
142 _, lab = connected_components(A, directed=
False)
143 fl = lab.reshape(n, order=
"F")[fluid]
144 sizes = np.bincount(fl)
145 main = int(np.argmax(sizes))
148 v = saved[key][3][fluid]
150 line.append(f
"{key}: off-main {float(np.sum(v[fl != main] ** 2) / np.sum(v ** 2)):.3f}")
151 tag =
"operator graph, " if nodes
is node
else ""
152 print(f
"{tag}aperture > {th:g}: {len(np.unique(fl))} fluid components, main {sizes[main]} "
153 f
"of {fl.size} fluid cells ({fl.size - sizes[main]} off main); " +
"; ".join(line))
156 rp = r[3][fluid] - r[3][fluid].
mean()
157 ev = float(sum(np.sum(r[f] ** 2)
for f
in range(3)))
158 ep = cP * cP * float(np.sum(rp ** 2))
159 e = np.sort(rp ** 2)[::-1]
160 n90 = int(np.searchsorted(np.cumsum(e) / e.sum(), 0.9)) + 1
161 print(f
"{key}: pressure share of W^2 {ep / (ev + ep):.6f}; 90% of the pressure energy in "
162 f
"{n90} of {rp.size} fluid cells")
167 okw = dict(okw, metric=a.metric, ritz_scope=
"mixed")
168 acc = AO.AndersonOracle(s, window=a.window, **okw)
170 for k
in range(1, a.steps + 1):
172 if acc.status !=
"active":
173 print(f
"status {acc.status} at step {k}: {acc.reason}")
176 vel = sum(float(np.dot(R[f, acc.inner], R[f, acc.inner]))
for f
in acc.vel)
177 rp = R[3, acc.fluid] - R[3, acc.fluid].
mean()
178 pres = okw[
"c_p"] ** 2 * float(np.dot(rp, rp))
179 U2 = acc.usq(acc.Gprev)
180 rows.append((k, math.sqrt((vel + pres) / U2), math.sqrt(vel / U2), float(s.get_u().
mean()),
182 rows = np.array(rows)
184 np.savez(a.out, rows=rows)
185 ref = a.ref
if a.ref
else rows[-1, 3]
186 err = np.abs(rows[:, 3] / ref - 1)
189 bad = np.where(err > tol)[0]
190 return bad[-1] + 2
if bad.size
else 1
193 i = np.where(rows[:, col] <= tol)[0]
194 return int(i[0]) + 1
if i.size
else None
195 print(f
"{a.case} {a.scheme} N={a.N} m={a.window} metric={a.metric}: reference {ref:.12e}; "
196 f
"monitor within 3e-5 from step {stays(3e-5)}, within 1e-5 from {stays(1e-5)}; velocity "
197 f
"residual <= 3e-7 at {first(2, 3e-7)}; W residual <= 3e-7 at {first(1, 3e-7)}")
202 okw = dict(okw, metric=a.metric, ritz_scope=
"mixed")
203 mons, holder = [], {}
205 def cb(steps, phase):
206 mons.append((steps, phase, float(s.get_u().
mean())))
207 res = AO.march_to_steady(s,
lambda: float(s.get_u().
mean()), rtol=st[
"rtol"],
208 max_steps=st[
"max_steps"], window=a.window, callback=cb, oracle_kw=okw,
209 holder=holder, budget=a.budget, rev=1)
210 print(f
"{a.case} {a.scheme} N={a.N} m={a.window}: converged={res['converged']} "
211 f
"{res['reason']} steps={res['steps']} accelerated={res['accelerated_steps']} "
212 f
"K={K(res['monitor']):.10f}")
214 for k, ph, m
in mons:
215 if not runs
or runs[-1][0] != ph:
216 runs.append((ph, []))
217 runs[-1][1].append((k, m))
218 for ph, rows
in runs:
219 if ph ==
"accelerate":
220 print(f
" accelerate {rows[0][0]}-{rows[-1][0]}")
222 out, prev, dprev = [],
None,
None
223 for k, m
in [rows[i]
for i
in range(4, len(rows), 5)]:
228 bound = abs(d) / (1 - max(R, 0.997 ** 5))
if R < 1
else math.inf
229 out.append(f
"[{k}: d/|m| {d / abs(m):+.1e} R {R:+.3f} bound/|m| "
230 f
"{bound / abs(m):.1e}]")
232 out.append(f
"[{k}: d/|m| {d / abs(m):+.1e}]")
235 print(f
" {ph} {rows[0][0]}-{rows[-1][0]}: " +
" ".join(out))
239 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..",
"..",
241 from _bootstrap
import ensure_flow
243 ap = argparse.ArgumentParser()
244 ap.add_argument(
"command", choices=(
"slowmode",
"phase-a",
"certify"))
245 ap.add_argument(
"--case", choices=(
"sphere",
"bed"), default=
"sphere")
246 ap.add_argument(
"--scheme", choices=(
"staggered",
"collocated"), required=
True)
247 ap.add_argument(
"--N", type=int, default=16)
248 ap.add_argument(
"--phi", type=float, default=0.125)
249 ap.add_argument(
"--mu", type=float, default=1.0)
250 ap.add_argument(
"--beta", type=float, default=6.0)
251 ap.add_argument(
"--dt", type=float, default=
None)
252 ap.add_argument(
"--advection", default=
None)
253 ap.add_argument(
"--arrangement", default=
None)
254 ap.add_argument(
"--window", type=int, default=5)
255 ap.add_argument(
"--metric", choices=(
"V",
"W"), default=
"V")
256 ap.add_argument(
"--budget", type=int, default=
None)
257 ap.add_argument(
"--steps", type=int, default=300)
258 ap.add_argument(
"--save", default=
"", help=
"slowmode: steps whose residual field is kept")
259 ap.add_argument(
"--ref", type=float, default=
None, help=
"reference monitor value")
260 ap.add_argument(
"--out", default=
None)
262 {
"slowmode": cmd_slowmode,
"phase-a": cmd_phase_a,
"certify": cmd_certify}[a.command](flow, a)