Начальный коммит: 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:
2026-08-14 16:37:38 +03:00
co-authored by Claude Opus 5
commit 11ff7b79b4
139 changed files with 88747 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
# Точка входа модели 2×+SDF. Запуск из этой папки:
# python run.py
# AMR_STEPS=2000 python run.py # короткий прогон
# AMR_CUDAGRAPH=0 python run.py # без CUDA-графов
# AMR_FP64=1 python run.py # двойная точность
import os, time
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)
def main():
cfg = C.from_env()
print(f"[2×+SDF] backend={B.BACKEND}; домен {cfg.Nx}x{cfg.Ny}; D={cfg.D}; steps={cfg.steps}; "
f"collision=kbc; force=gmem; "
f"graphs={'on' if cfg.use_cuda_graph else 'off'}; "
f"L1 {cfg.refine}× = {(cfg.bx1-cfg.ax1)*cfg.refine+1}x{(cfg.by1-cfg.ay1)*cfg.refine+1}; "
f"вход {cfg.cx/cfg.D:.1f}D / выход {(cfg.Nx-1-cfg.cx)/cfg.D:.1f}D; outlet_uy={cfg.outlet_uy}")
t0 = time.perf_counter()
sim = S.Solver(cfg)
res = sim.run(Viz.make_progress("2x+sdf"))
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)
Viz.save_gif(res, sim.geo, cfg, os.path.join(OUT, "cyl_2x_sdf.gif"))
Viz.save_series_panel(res, a, cfg, os.path.join(OUT, "series_2x_sdf.png"))
import numpy as np
np.savez(os.path.join(OUT, "probe_2x_sdf.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")),
Cm_st=a.get("Cm_st", float("nan")),
Ny=cfg.Ny, beta=cfg.D/cfg.Ny, D=cfg.D, U=cfg.U, Re=cfg.Re, steps=cfg.steps,
Nx=cfg.Nx, cx=cfg.cx, outlet_uy=cfg.outlet_uy,
stress_every=res["stress_every"],
# ряды
Cl=a["Cl_s"], uy=a["uy"], Fx=B.to_cpu(res["Fx"]), Fy=B.to_cpu(res["Fy"]),
Tz=B.to_cpu(res["Tz"]), rho=B.to_cpu(res["rho"]),
Fx_st=B.to_cpu(res["Fx_st"]), Fy_st=B.to_cpu(res["Fy_st"]), Tz_st=B.to_cpu(res["Tz_st"]))
print("DONE (2×+SDF)")
if __name__ == "__main__":
main()