Состояние на момент заведения репозитория. 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>
96 lines
5.6 KiB
Python
96 lines
5.6 KiB
Python
# Серия по блокировке: D фиксирован, Ny растёт → β=D/Ny падает; экстраполяция β→0 даёт
|
|
# прямое сравнение с БЕЗГРАНИЧНОЙ литературой (Re=150: Cd≈1.33, St≈0.183, rms Cl≈0.3)
|
|
# без ручной поправки (1−β)². Запуск из этой папки:
|
|
# python run_blockage.py
|
|
# AMR_NY_LIST=90,128,180 python run_blockage.py # свой список Ny
|
|
# AMR_STEPS=20000 AMR_NY_LIST=128 python run_blockage.py # смоук
|
|
# Стоимость прогона ≈ как у run.py: патч L1 (основная работа) от Ny не зависит (высота 4D фикс.).
|
|
import os, time
|
|
import numpy as np
|
|
import config as C
|
|
import backend as B
|
|
import solver as S
|
|
import diagnostics as Diag
|
|
import visualization as Viz
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
OUT = os.path.join(HERE, "out"); os.makedirs(OUT, exist_ok=True)
|
|
|
|
LIT = dict(Cd=1.33, Clrms=0.3, St=0.183) # безграничный цилиндр, Re≈150
|
|
|
|
def run_one(Ny):
|
|
cfg = C.from_env(Ny=Ny, frame_every=0) # серия без кадров/гифок (только ряды)
|
|
print(f"\n[блокировка] Ny={cfg.Ny} (β={cfg.D/cfg.Ny:.3f}); домен {cfg.Nx}x{cfg.Ny}; "
|
|
f"патч y=[{cfg.ay1},{cfg.by1}]; steps={cfg.steps}")
|
|
t0 = time.perf_counter()
|
|
sim = S.Solver(cfg)
|
|
res = sim.run(Viz.make_progress(f"Ny={cfg.Ny}"))
|
|
print(" время:", round(time.perf_counter() - t0, 1), "s")
|
|
a = Diag.analyze(res, cfg)
|
|
Diag.print_table(a, cfg)
|
|
Diag.print_crosscheck(a)
|
|
Diag.convergence_report(res, cfg)
|
|
np.savez(os.path.join(OUT, f"probe_blockage_Ny{cfg.Ny}.npz"),
|
|
St=a["St"], Cd=a["Cd"], Clrms=a["Clrms"], uyrms=a["uyrms"],
|
|
Cm=a.get("Cm", float("nan")),
|
|
Cd_st=a.get("Cd_st", float("nan")), Clrms_st=a.get("Clrms_st", float("nan")),
|
|
Ny=cfg.Ny, beta=cfg.D/cfg.Ny, D=cfg.D, U=cfg.U, Re=cfg.Re, steps=cfg.steps,
|
|
Cl=a["Cl_s"], uy=a["uy"], rho=B.to_cpu(res["rho"]),
|
|
Fx=B.to_cpu(res["Fx"]), Fy=B.to_cpu(res["Fy"]), Tz=B.to_cpu(res["Tz"]))
|
|
row = dict(Ny=cfg.Ny, beta=cfg.D/cfg.Ny, St=a["St"], Cd=a["Cd"], Clrms=a["Clrms"],
|
|
Cm=a.get("Cm", float("nan")), Cd_st=a.get("Cd_st"))
|
|
# освободить VRAM перед следующим прогоном (буферы, BC, захваченный CUDA-граф)
|
|
del sim, res, a
|
|
B.cp.get_default_memory_pool().free_all_blocks()
|
|
return row
|
|
|
|
def save_extrapolation_figure(rows, fits, path):
|
|
import matplotlib; matplotlib.use("Agg")
|
|
import matplotlib.pyplot as plt
|
|
betas = np.array([r["beta"] for r in rows]); bb = np.linspace(0, betas.max()*1.05, 100)
|
|
panels = [("Cd", "⟨Cd⟩", LIT["Cd"], "лит. 1.33"),
|
|
("Clrms", "rms Cl", LIT["Clrms"], "лит. ~0.3"),
|
|
("St", "St", LIT["St"], "лит. 0.183")]
|
|
fig, axs = plt.subplots(1, 3, figsize=(13.5, 4.2), dpi=110)
|
|
for ax, (key, lab, lit, litlab) in zip(axs, panels):
|
|
vals = np.array([r[key] for r in rows]); ft = fits[key]
|
|
ax.plot(betas, vals, "o", ms=7, color="#d32f2f", label="серия (raw)")
|
|
al, sl = ft["lin"]; aq, sq = ft["quad"]
|
|
ax.plot(bb, al + sl*bb, "-", lw=1.2, color="#1976d2", label=f"a+b·β → {al:.3f}")
|
|
ax.plot(bb, aq + sq*bb**2, "--", lw=1.2, color="#388e3c", label=f"a+b·β² → {aq:.3f}")
|
|
ax.axhline(lit, color="gray", lw=1.0, ls=":", label=litlab)
|
|
if key == "St": # для St полезен и кинематически поправленный вид
|
|
ax.plot(betas, vals*(1-betas), "s", ms=6, mfc="none", color="#7b1fa2", label="St·(1−β)")
|
|
ax.set_xlabel("β = D/Ny"); ax.set_ylabel(lab); ax.set_xlim(0, bb[-1])
|
|
ax.legend(fontsize=8); ax.set_title(f"{lab}(β): экстраполяция β→0", fontsize=10)
|
|
fig.suptitle("Серия по блокировке: стеснение каналом — свойство ПОСТАНОВКИ, не считывания силы",
|
|
fontsize=11)
|
|
fig.tight_layout(rect=(0, 0, 1, 0.93))
|
|
fig.savefig(path, bbox_inches="tight"); plt.close(fig)
|
|
print("saved", os.path.basename(path))
|
|
|
|
def main():
|
|
ny_list = [int(s) for s in os.environ.get("AMR_NY_LIST", "90,128,180").split(",")]
|
|
print(f"[серия по блокировке] backend={B.BACKEND}; Ny={ny_list}")
|
|
rows = [run_one(Ny) for Ny in ny_list]
|
|
if len(rows) < 2:
|
|
print("\n(одна точка — фит/экстраполяция пропущены)")
|
|
return
|
|
betas = [r["beta"] for r in rows]
|
|
fits = {key: Diag.fit_blockage(betas, [r[key] for r in rows]) for key in ("Cd", "Clrms", "St")}
|
|
Diag.print_blockage_table(rows, fits)
|
|
np.savez(os.path.join(OUT, "blockage_summary.npz"),
|
|
Ny=np.array([r["Ny"] for r in rows]), beta=np.array(betas),
|
|
St=np.array([r["St"] for r in rows]), Cd=np.array([r["Cd"] for r in rows]),
|
|
Clrms=np.array([r["Clrms"] for r in rows]), Cm=np.array([r["Cm"] for r in rows]),
|
|
Cd_st=np.array([r["Cd_st"] if r["Cd_st"] is not None else np.nan for r in rows]),
|
|
Cd0_lin=fits["Cd"]["lin"][0], Cd0_quad=fits["Cd"]["quad"][0],
|
|
Cl0_lin=fits["Clrms"]["lin"][0], Cl0_quad=fits["Clrms"]["quad"][0],
|
|
St0_lin=fits["St"]["lin"][0], St0_quad=fits["St"]["quad"][0],
|
|
lit_Cd=LIT["Cd"], lit_Clrms=LIT["Clrms"], lit_St=LIT["St"])
|
|
save_extrapolation_figure(rows, fits, os.path.join(OUT, "blockage_extrapolation.png"))
|
|
print("DONE (серия по блокировке)")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|