# Геометрия: маски тела/стенок и знаковое поле расстояний (SDF) для L0 и патча L1. # Чистый CuPy. Соглашение по SDF: phi>0 в жидкости, phi<0 в теле; |phi| ≈ расстоянию до границы. import backend as B import lattice as L import amr cp = B.cp; xp = B.xp; DTYPE = B.DTYPE def _cmask(NX, NY, ccx, ccy, R): """Булева маска круга радиуса R (центр ccx,ccy) на сетке (NY,NX).""" Y, X = cp.meshgrid(cp.arange(NY), cp.arange(NX), indexing="ij") return (X - ccx)**2 + (Y - ccy)**2 <= R*R def _csdf(NX, NY, ccx, ccy, R): """SDF круга: sqrt((x−ccx)²+(y−ccy)²) − R (отрицательно внутри).""" Y, X = cp.meshgrid(cp.arange(NY), cp.arange(NX), indexing="ij") return cp.sqrt((X - ccx).astype(DTYPE)**2 + (Y - ccy).astype(DTYPE)**2) - R class Geometry: """Контейнер масок/SDF/патча. Поля *_np — host-копии для визуализации.""" pass def build(cfg): Nx, Ny, D, cx, cy = cfg.Nx, cfg.Ny, cfg.D, cfg.cx, cfg.cy r = cfg.refine ax1, bx1, ay1, by1 = cfg.ax1, cfg.bx1, cfg.ay1, cfg.by1 g = Geometry() # --- L0: только цилиндр как твёрдое тело (стенки канала верх/низ — free-slip, не solid) --- g.cyl0 = _cmask(Nx, Ny, cx, cy, D/2.0) g.solid0 = g.cyl0 # стенки обрабатываются specular-отражением, см. boundary.free_slip_walls g.phi0 = _csdf(Nx, Ny, cx, cy, D/2.0) # SDF только цилиндра (для Bouzidi тела) # --- патч L1 (измельчение r) над областью [ax1,bx1]×[ay1,by1] --- g.P1 = amr.patch(Nx, Ny, ax1, bx1, ay1, by1, r) Nfx, Nfy = g.P1["Nfx"], g.P1["Nfy"] # цилиндр на L1: центр и радиус масштабируются на r (диаметр r·D → радиус r·D/2) R1 = r * D / 2.0 g.solid1 = _cmask(Nfx, Nfy, (cx - ax1)*r, (cy - ay1)*r, R1) g.phi1 = _csdf(Nfx, Nfy, (cx - ax1)*r, (cy - ay1)*r, R1) # маска жидких узлов для рестрикции L1→L0 (внутренняя часть патча на L0) g.fl1 = ~g.solid0[ay1+1:by1, ax1+1:bx1] # host-копии для визуализации g.solid0_np = B.to_cpu(g.solid0); g.solid1_np = B.to_cpu(g.solid1) return g