复现iisph遇到问题

运行环境

taichi 0.8.9

问题描述

我在课程提供的sph_base和particle_system基础上,复现iisph原论文时,发现无法收敛压强,具体表现为a_ii的值很小,迭代过程中压强p_i,l+1 = (density_0 - desity_i,adv - (…))/a_ii非常大,超过正常的压强应有的数值
我读公式没发现问题,求大佬们教教

代码链接

链接
论文pdf

1 个赞

Hi @emt, 你好,非常欢迎你来到太极论坛。

你提供的代码,只有一个class,能够提供一个能跑的代码么?还有就是SPH base 类也可以放在你的repo里面。

不好意思,我之前疏忽忘记放了 ……
已经更新在repo里了,运行ii_demo.py会调用iisph.py和其他文件,麻烦大佬看一下谢谢

1 个赞

最后问题解决了吗?我最近写iisph时也出现了不收敛的问题。

import taichi as ti
import math

poly6_factor = 315.0/(64.0*math.pi)
spiky_grad_factor = -45.0/math.pi

@ti.func
def Cubic(r_norm,h):
    res = ti.cast(0.0, ti.f32)
    k = 40 / 7 / math.pi/h**2
    q = r_norm / h
    if q <= 1.0:
        if q <= 0.5:res = k * (6.0 * q**3 - 6.0 *q**2 + 1)
        else:       res = k * 2 * ti.pow(1 - q, 3.0)
    return res

@ti.func
def CubicGradient(r,h):
    res = ti.Vector([0.0,0.0])
    k = 6. * 40 / 7 / math.pi / h ** 2
    r_norm = r.norm()
    q = r_norm / h
    if r_norm > 1e-5 and q <= 1.0:
        grad_q = r / (r_norm * h)
        if q <= 0.5:     res = k * q * (3.0 * q - 2.0) * grad_q
        else:            res = k * -(1.0 - q) ** 2 * grad_q
    return res


dt  = 1e-4
r   = 0.05
h   = 4*r
V   = math.pi*r**2
rho0 = 1000
se_exp,se_coe = 7.0,50.0
fluid_rect = (20,120,1,1) #(width,hight,lowerleftX,lowerleftY)
bound      = (5,10)            # lowerleft bound is assumed (0,0) 
n          = fluid_rect[0]*fluid_rect[1]
mass       = rho0*V
world2screen = 1.0/max(bound)

ti.init(arch = ti.cpu)

# particle properties
x   = ti.Vector.field(2,dtype = ti.f32 , shape = n)
xs  = ti.Vector.field(2,dtype = ti.f32 , shape = n) # screen position
v   = ti.Vector.field(2,dtype = ti.f32 , shape = n)
a   = ti.Vector.field(2,dtype = ti.f32 , shape = n)
rho = ti.field(dtype = ti.f32 , shape = n)
p   = ti.field(dtype = ti.f32 , shape = n)

# grid properties
cellNum,maxParticleInCell,maxNeighbor = 10007,100,100
gridData        = ti.field(dtype = ti.i32,shape = (cellNum,maxParticleInCell))
gridParticleNum = ti.field(dtype = ti.i32,shape = cellNum)
neighbor        = ti.field(dtype = ti.i32,shape = (n,maxNeighbor))
neighborNum     = ti.field(dtype = ti.i32,shape = n)

@ti.kernel
def Initialize():
    for j in range(fluid_rect[1]):
        for i in range(fluid_rect[0]):
            v[j*fluid_rect[0]+i] = ti.Vector([0.0,0.0])
            x[j*fluid_rect[0]+i] = ti.Vector([i*r,j*r])+ti.Vector([fluid_rect[2],fluid_rect[3]])

@ti.kernel
def StepBeforePressureSolve():
    for i in gridParticleNum:gridParticleNum[i]=0
    for i in x:
        hashedIndex = (int(x[i][0]/2/h)*73856093+int(x[i][1]/2/h)*19349663)%cellNum
        if gridParticleNum[hashedIndex]>=maxParticleInCell:continue
        idx = ti.atomic_add(gridParticleNum[hashedIndex],1)
        gridData[hashedIndex,idx] = i
    for i in x:
        neighborNum_i = 0
        coord = int(x[i]/2/h)
        for offset in ti.static([(-1,-1),(-1,0),(-1,1),(0,-1),(0,0),(0,1),(1,-1),(1,0),(1,1)]):
            nei_idx = ((coord[0]+offset[0])*73856093+(coord[1]+offset[1])*19349663)%cellNum
            for celli in range(gridParticleNum[nei_idx]):
                j = gridData[nei_idx,celli]
                if i==j or (x[i]-x[j]).norm()>=h or neighborNum_i>=maxNeighbor:continue
                neighbor[i,neighborNum_i]=j
                neighborNum_i+=1
        neighborNum[i] = neighborNum_i
    for i in x:
        rho_i = 0.0
        for j in range(neighborNum[i]):
            rho_i += mass*Cubic((x[i]-x[neighbor[i,j]]).norm(),h)
        rho[i] = ti.max(rho_i,rho0)
    for i in x:
        ai    = ti.Vector([0.0,-9.81])
        for nei_j in range(neighborNum[i]):
            j = neighbor[i,nei_j]
            ai += V*(v[i]-v[j]).dot(x[i]-x[j])/((x[i]-x[j]).norm_sqr()+0.01*h*h)*CubicGradient(x[i]-x[j],h)
        a[i] = ai

#---------------------WCSPH-------------------------------------------------------------------------
@ti.kernel
def PressureSolve_WCSPH():
     for i in x: p[i]  =  ti.max( se_coe*(ti.pow(rho[i]/rho0,se_exp)-1) ,0.0)

#----------------------IISPH-------------------------------------------------------------------------------
# A @ p = s
s     = ti.field(dtype = ti.f32,shape = n)
Adiag = ti.field(dtype = ti.f32,shape = n) 
Ddiag  = ti.Vector.field(2,dtype = ti.f32,shape = n) 
dijxpj = ti.Vector.field(2,dtype = ti.f32,shape = n) 
p_new  = ti.field(dtype = ti.f32,shape = n)
rho_adv= ti.field(dtype = ti.f32,shape = n)

@ti.kernel
def FillADiagonalAnds_IISPH():  
    for i in v: 
        v[i] +=dt*a[i]
        a[i]  = ti.Vector([0.0,0.0])  
        dii = ti.Vector([0.0,0.0])
        for nei_j in range(neighborNum[i]):dii +=  CubicGradient(x[i]-x[neighbor[i,nei_j]],h)
        Ddiag[i] = -dt*dt*mass/rho[i]**2*dii
    for i in x:
        aii = 0.0
        # s_i = 0.0
        rho_adv[i] = rho[i]
        for nei_j in range(neighborNum[i]):
            j    = neighbor[i,nei_j]
            dwij =  CubicGradient(x[i]-x[j],h)
            dji = -dt**2*mass*CubicGradient(x[j]-x[i],h)/rho[i]**2
            aii+=(Ddiag[i] - dji).dot(dwij)
            rho_adv[i] += dt*mass*(v[i]-v[j]).dot(dwij) 
        Adiag[i] = aii*mass
        p[i] *= 0.5

@ti.kernel
def UpdatePressure_IISPH()->ti.f32: #return average error
    for i in x:
        dijpj = ti.Vector([0.0,0.0])
        for nei_j in range(neighborNum[i]):
            j    = neighbor[i,nei_j]
            dijpj += (-dt*dt*mass/rho[j]**2)*CubicGradient(x[i]-x[j],h)*p[j]
        dijxpj[i] = dijpj
    err = 0.0
    # rho_avg = 0.0
    for i in x:
        sigma = 0.0
        for nei_j in range(neighborNum[i]):
            j = neighbor[i,nei_j]
            dwij =  CubicGradient(x[i]-x[j],h)
            dji = -dt**2*mass*CubicGradient(x[j]-x[i],h)/rho[i]**2
            sigma += mass*( dijxpj[i]-Ddiag[j]*p[j]-(dijxpj[j]- dji*p[i]) ).dot(dwij)
        p_new[i] = ti.max( 0.5*p[i]+0.5*(rho0-rho_adv[i]-sigma)/Adiag[i],0.0)
        err += (sigma + Adiag[i]*p_new[i] -(rho0-rho_adv[i]))
    for i in p:p[i] = p_new[i]
    ret = err/rho0/n
    print(ret)
    return ret

def PressureSolve_IISPH():
    l = 0
    FillADiagonalAnds_IISPH()
    while UpdatePressure_IISPH()>1e-3 and l<50: l+=1
#-----------------------------------------------------------------------------------------------------

@ti.kernel
def StepAfterPressureSolve():
    for i in x: #compute pressure accelration
        for nei_j in range(neighborNum[i]):
            j = neighbor[i,nei_j]
            a[i] += -mass*(p[j]/rho[j]**2 + p[i]/rho[i]**2)*CubicGradient(x[i]-x[j],h)
    for i in x:
        v[i] += dt*a[i]
        x[i] += dt*v[i]
    for i in x:
        if x[i][0]<r or x[i][0]>bound[0]-r:
            x[i][0] = ti.max(ti.min(x[i][0],bound[0]-r),r)
            v[i][0] = -0.9*v[i][0]
        if x[i][1]<r or x[i][1]>bound[1]-r:
            x[i][1] = ti.max(ti.min(x[i][1],bound[1]-r),r)
            v[i][1] = -0.9*v[i][1]

@ti.kernel
def UpdateScreenPosition():
    for i in xs:xs[i] = x[i]*world2screen

def Main():
    Initialize()
    window = ti.ui.Window("SPH FLuid",(1000,1000))
    canvas = window.get_canvas()
    while not window.is_pressed(ti.ui.ESCAPE):
    # for _ in range(1):
        for _ in range(1): 
            StepBeforePressureSolve()
            # PressureSolve_WCSPH()
            PressureSolve_IISPH()
            StepAfterPressureSolve()
        UpdateScreenPosition()
        canvas.set_background_color((245/255,245/255,245/255))
        canvas.circles(xs,radius = r*world2screen/2,color=(183/255,134/255,11/255))
        window.show()

Main()

cc @mzhang