Начальный коммит: 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,54 @@
|
||||
# AMR-связка L0 (грубый) ↔ L1 (тонкий, измельчение r).
|
||||
#
|
||||
# Идея: на тонком патче считаем r подшагов по времени на каждый шаг L0. Границы патча («ghost»)
|
||||
# заполняем интерполяцией с грубого уровня (по пространству + по времени между «до» и «после» шага L0),
|
||||
# а внутренность патча после подшагов проецируем обратно на грубый уровень (рестрикция).
|
||||
# Неравновесная часть масштабируется множителями Rcf (грубый→тонкий) и Rfc (тонкий→грубый),
|
||||
# т.к. при смене τ масштаб неравновесия меняется.
|
||||
import backend as B
|
||||
import lattice as L
|
||||
from equilibrium import feq, macros
|
||||
|
||||
cp = B.cp; xp = B.xp; DTYPE = B.DTYPE
|
||||
|
||||
def patch(pNx, pNy, ax, bx, ay, by, r):
|
||||
"""Описание патча: размеры (Nfy,Nfx), индексы/веса билинейной интерполяции с грубого уровня,
|
||||
срезы для рестрикции каждого r-го узла."""
|
||||
Wx, Wy = bx - ax, by - ay
|
||||
Nfx, Nfy = r*Wx + 1, r*Wy + 1
|
||||
FX, FY = cp.meshgrid(ax + cp.arange(Nfx)/r, ay + cp.arange(Nfy)/r) # координаты тонких узлов в L0
|
||||
x0 = cp.floor(FX).astype(cp.int64); y0 = cp.floor(FY).astype(cp.int64)
|
||||
x1 = cp.minimum(x0 + 1, pNx - 1); y1 = cp.minimum(y0 + 1, pNy - 1)
|
||||
return dict(Nfx=int(Nfx), Nfy=int(Nfy), ax=ax, bx=bx, ay=ay, by=by, r=r,
|
||||
x0=x0, y0=y0, x1=x1, y1=y1,
|
||||
tx=(FX - x0).astype(DTYPE), ty=(FY - y0).astype(DTYPE),
|
||||
slx=slice(r, r*Wx, r), sly=slice(r, r*Wy, r))
|
||||
|
||||
def pint(fld, P):
|
||||
"""Билинейная интерполяция поля fld (грубого) в узлы тонкого патча."""
|
||||
return (fld[..., P["y0"], P["x0"]]*(1 - P["tx"])*(1 - P["ty"])
|
||||
+ fld[..., P["y0"], P["x1"]]*P["tx"]*(1 - P["ty"])
|
||||
+ fld[..., P["y1"], P["x0"]]*(1 - P["tx"])*P["ty"]
|
||||
+ fld[..., P["y1"], P["x1"]]*P["tx"]*P["ty"])
|
||||
|
||||
def ghost(pf, P, Rcf):
|
||||
"""Граничные значения тонкого уровня из грубого поля pf: равновесие по интерполированным ρ,u
|
||||
плюс масштабированная неравновесная часть Rcf·neq.
|
||||
u в feq передаём кортежем (без cupy.stack) — H2D ломает CUDA-graph capture."""
|
||||
r, u = macros(pf); neq = pf - feq(r, u)
|
||||
return feq(pint(r, P), (pint(u[0], P), pint(u[1], P))) + Rcf*pint(neq, P)
|
||||
|
||||
def fill(cf, gh):
|
||||
"""Записать ghost-значения gh в рамку (1 ряд) тонкого поля cf."""
|
||||
cf[:, 0, :] = gh[:, 0, :]; cf[:, -1, :] = gh[:, -1, :]
|
||||
cf[:, :, 0] = gh[:, :, 0]; cf[:, :, -1] = gh[:, :, -1]
|
||||
return cf
|
||||
|
||||
def restrict(cf, pf, P, Rfc, fluid):
|
||||
"""Спроецировать тонкое поле cf обратно на грубое pf (каждый r-й узел), масштабируя neq на Rfc.
|
||||
Меняется только внутренняя жидкая часть перекрытия (маска fluid)."""
|
||||
r, u = macros(cf); neq = cf - feq(r, u); slx, sly = P["slx"], P["sly"]
|
||||
nv = feq(r[sly, slx], u[:, sly, slx]) + Rfc*neq[:, sly, slx]
|
||||
cur = pf[:, P["ay"]+1:P["by"], P["ax"]+1:P["bx"]]
|
||||
pf[:, P["ay"]+1:P["by"], P["ax"]+1:P["bx"]] = xp.where(fluid[None], nv, cur)
|
||||
return pf
|
||||
Reference in New Issue
Block a user