Начальный коммит: Vulkan-редактор SimVulcan + исследование KBC-LBM
Состояние на момент заведения репозитория. C++ приложение (src/, shaders/, tests/) — минимальный редактор 3D-моделей на Vulkan 1.3: орбитальная камера, три опорные сетки через начало координат, загрузка .obj с режимами отображения. Весь Vulkan изолирован в src/vk/. Исследование (docs/) — оригинальные статьи по KBC (docs/origins) и Python-решатель D2Q9 KBC-N1 с AMR 2x и SDF+Bouzidi (docs/theory). В решателе перед коммитом исправлены дефекты, найденные сверкой с первоисточниками: относительный порог знаменателя энтропийного стабилизатора (абсолютный вырождал KBC в LBGK на 77-99% узлов), заворот вход/выход в углах домена, диагностика средней плотности по фиктивным узлам тела, зашитый refine=2. Подробности — docs/theory/solver_2x_sdf/README.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# Демо-1: вихрь Тейлора–Грина (валидация вязкости и H-теоремы) на компонентах solver_2x_sdf.
|
||||
# Аналитика: E(t) = E0·exp(−4νk²t). Меряем ν по наклону ln E(t) — должен совпасть с заданным.
|
||||
# Артефакты: figures/07_taylor_green.png, figures/07b_tgv_decay_slices.png, anim/tgv_decay.gif,
|
||||
# out/tgv.txt. Запуск: python tgv_gpu.py
|
||||
import math
|
||||
import numpy as np
|
||||
import _common as cm
|
||||
|
||||
cp = cm.cp; xp = cm.xp; E = cm.E; Coll = cm.Coll; Strm = cm.Strm
|
||||
plt = cm.plt; vorticity = cm.vorticity
|
||||
|
||||
def run_tgv(N=96, U0=0.04, nu=0.0125, steps=10000, rec=50, n_snap=10, frame_every=42):
|
||||
k = 2.0*math.pi/N
|
||||
xs = cp.arange(N, dtype=cm.DTYPE)
|
||||
X, Yc = cp.meshgrid(xs, xs) # X — ось 1, Yc — ось 0
|
||||
ux = -U0*cp.cos(k*X)*cp.sin(k*Yc)
|
||||
uy = U0*cp.sin(k*X)*cp.cos(k*Yc)
|
||||
rho = cp.ones((N, N), cm.DTYPE)
|
||||
f = E.feq(rho, cm.B.pack((ux.astype(cm.DTYPE), uy.astype(cm.DTYPE))))
|
||||
beta = cm.nu_to_beta(nu)
|
||||
snap_at = sorted(set(int(round(v)) for v in np.linspace(0, steps, n_snap)))
|
||||
snaps, frames, ts, KE, Hs = [], [], [], [], []
|
||||
prog = cm.Viz.make_progress("tgv")
|
||||
for t in range(steps + 1):
|
||||
rho, u = E.macros(f)
|
||||
f = Coll.kbc_collide(f, E.feq(rho, u), beta)
|
||||
f = Strm.stream(f) # периодический перенос — ровно случай TGV
|
||||
if (t in snap_at) or (t % rec == 0) or (t % frame_every == 0):
|
||||
_, uu = E.macros(f)
|
||||
if t in snap_at:
|
||||
snaps.append((t, cm.to_cpu(uu)))
|
||||
if t % frame_every == 0:
|
||||
frames.append((t, cm.to_cpu(uu)))
|
||||
if t % rec == 0:
|
||||
ts.append(t)
|
||||
KE.append(float((0.5*(uu[0]**2 + uu[1]**2)).mean()))
|
||||
Hs.append(float(cm.H_function(f).mean()))
|
||||
prog(t, steps)
|
||||
return dict(N=N, k=k, nu=nu, U0=U0, ts=np.array(ts), KE=np.array(KE),
|
||||
H=np.array(Hs), u=cm.to_cpu(E.macros(f)[1]), snaps=snaps, frames=frames)
|
||||
|
||||
tgv = run_tgv()
|
||||
mask = tgv["KE"] > 0
|
||||
slope = np.polyfit(tgv["ts"][mask], np.log(tgv["KE"][mask]), 1)[0]
|
||||
nu_meas = -slope/(4.0*tgv["k"]**2)
|
||||
rel_err = abs(nu_meas - tgv["nu"])/tgv["nu"]
|
||||
print(f"TGV: ν задано={tgv['nu']:.5f}, ν измерено={nu_meas:.5f}, отн. ошибка={rel_err*100:.2f}%")
|
||||
assert rel_err < 0.06, "TGV: измеренная вязкость отклонилась слишком сильно"
|
||||
dH = np.diff(tgv["H"])
|
||||
print(f"TGV: H-функция монотонно невозрастает: max(ΔH)={dH.max():.2e}")
|
||||
|
||||
# --- статическая панель: ω, затухание энергии, H(t) -------------------------------------------
|
||||
fig, axs = plt.subplots(1, 3, figsize=(13.5, 4.0))
|
||||
om = vorticity(tgv["u"])
|
||||
im = axs[0].imshow(om, origin="lower", cmap="RdBu_r")
|
||||
axs[0].set_title("Завихренность $\\omega$ (TGV, срез)")
|
||||
axs[0].set_xlabel("$x$ [lu]"); axs[0].set_ylabel("$y$ [lu]")
|
||||
plt.colorbar(im, ax=axs[0], fraction=0.046)
|
||||
axs[1].semilogy(tgv["ts"], tgv["KE"], "o", ms=3, label="KBC (измерено)")
|
||||
axs[1].semilogy(tgv["ts"], tgv["KE"][0]*np.exp(-4*tgv["nu"]*tgv["k"]**2*tgv["ts"]),
|
||||
"-", color="#d1495b", label="$E_0 e^{-4\\nu k^2 t}$ (аналитика)")
|
||||
axs[1].set_title("Затухание кин. энергии"); axs[1].set_xlabel("$t$ [ts]")
|
||||
axs[1].set_ylabel("$E(t)$"); axs[1].legend()
|
||||
axs[2].plot(tgv["ts"], tgv["H"], color="#2e7d32")
|
||||
axs[2].set_title("H-функция (энтропия) во времени")
|
||||
axs[2].set_xlabel("$t$ [ts]"); axs[2].set_ylabel("$\\langle H\\rangle$")
|
||||
fig.suptitle("Демо-1: вихрь Тейлора–Грина (KBC, D2Q9)", y=1.04, fontweight="bold")
|
||||
cm.finish(fig, "07_taylor_green.png")
|
||||
|
||||
# --- 10 срезов распада с единой шкалой цвета ---------------------------------------------------
|
||||
snaps = tgv["snaps"]
|
||||
om0 = np.abs(vorticity(snaps[0][1])).max()
|
||||
KE0 = 0.5*np.mean(snaps[0][1][0]**2 + snaps[0][1][1]**2)
|
||||
fig, axs = plt.subplots(2, 5, figsize=(15, 6.3), constrained_layout=True)
|
||||
im = None
|
||||
for ax, (tt, uu) in zip(axs.ravel(), snaps):
|
||||
im = ax.imshow(vorticity(uu), origin="lower", cmap="RdBu_r", vmin=-om0, vmax=om0)
|
||||
KEf = 0.5*np.mean(uu[0]**2 + uu[1]**2)/KE0
|
||||
ax.set_title(f"t={tt} · $E/E_0$={KEf:.2f}", fontsize=10)
|
||||
ax.set_xticks([]); ax.set_yticks([])
|
||||
fig.colorbar(im, ax=axs, fraction=0.025, pad=0.01, label="завихренность $\\omega$ [1/ts]")
|
||||
fig.suptitle("Распад вихря Тейлора–Грина: завихренность на равных интервалах времени "
|
||||
"(единая шкала цвета)", fontweight="bold")
|
||||
cm.finish(fig, "07b_tgv_decay_slices.png")
|
||||
|
||||
# --- гифка затухания ---------------------------------------------------------------------------
|
||||
_fr = tgv["frames"]
|
||||
_om0 = np.abs(vorticity(_fr[0][1])).max()
|
||||
_KE0 = 0.5*np.mean(_fr[0][1][0]**2 + _fr[0][1][1]**2)
|
||||
|
||||
def _draw(fig, i):
|
||||
t, u = _fr[i]
|
||||
ax = fig.add_subplot(111)
|
||||
ax.imshow(vorticity(u), origin="lower", cmap="RdBu_r", vmin=-_om0, vmax=_om0)
|
||||
ke = 0.5*np.mean(u[0]**2 + u[1]**2)/_KE0
|
||||
ax.set_title(f"Тейлор–Грин · t={t} · $E/E_0$={ke:.2f}", fontsize=10)
|
||||
ax.set_xticks([]); ax.set_yticks([])
|
||||
fig.tight_layout()
|
||||
|
||||
cm.render_gif(len(_fr), _draw, "tgv_decay.gif", (4.6, 4.4), fps=24)
|
||||
|
||||
cm.write_out("tgv.txt", [
|
||||
f"N={tgv['N']} U0={tgv['U0']} steps=10000",
|
||||
f"nu_given={tgv['nu']:.5f} nu_measured={nu_meas:.5f} rel_err_pct={rel_err*100:.2f}",
|
||||
f"H_monotone_max_dH={dH.max():.3e}",
|
||||
"PASS TGV: вязкость по затуханию энергии и H-теорема",
|
||||
])
|
||||
print("DONE (tgv)")
|
||||
Reference in New Issue
Block a user