PCISPH的通俗解释与简单实现

PCISPH是SPH的一个变种,针对压力求解部分进行了改进。

本文参照

  1. Koschier, D., Bender, J., Solenthaler, B., & Teschner, M.
    (2020). Smoothed particle hydrodynamics techniques for the
    physics based simulation of fluids and solids. arXiv preprint
    arXiv:2009.06944.(主要参考)主要是P14-15
    https://interactivecomputergraphics.github.io/SPH-Tutorial/

伪代码

取自参考[1]
在这里插入图片描述

算法流程通俗解释

我们只关注PCISPH相较于WCSPH的改进部分,就是计算压力和压力梯度力的部分。其余部分完全一致。

WCSPH中直接通过状态方程解算出压力,而这里通过循环迭代求解。该循环就是上述伪代码中的算法。

  1. 我们将非压力梯度力的部分,即粘性力和重力先求出来,加入到加速度中,并通过时间积分预测出一个位置速度(此时,未施加压力梯度力,相当于迭代初始时压力梯度力为0)。

  2. 根据预测出的位置,使用核函数公式得到预测密度density_star。再减去静止密度得到密度差。

  3. 通过密度差乘以一个系数k_PCI得到预测压力。

  4. 根据预测压力,由核函数离散公式计算出预测压力梯度力

  5. 根据预测的压力梯度力,加入到加速度中,进行时间积分后得到新的预测位置和速度,进而得到新的预测密度(此时称为校正密度)。这一步虽然是多步,但是可以写为一个公式。

  6. 同样是密度差乘以k_PCI,得到校正压力

  7. 假如密度差小于规定值,就跳出循环,否则,重复第4、5、6步,不断迭代压力梯度力以最小化密度差。

这里我们看出来,循环的自变量是压力梯度力,循环的跳出条件则是密度误差。通过不断更正压力梯度力,从而最小化密度误差。

公式和公式翻译成代码

主要参考[1]的公式(53)-(60)

所有的代码都是针对粒子i的,最外层还需要嵌套对粒子i的循环。

  1. 计算k_PCI
    k P C I = − 0.5 ρ 0 2 Δ t 2 m 2 ⋅ 1 Σ j ∇ W i j ⋅ Σ j ∇ W i j + Σ j ( ∇ W i j ⋅ ∇ W i j ) k_{PCI} = - \frac{0.5 \rho_0^2}{\Delta t^2 m^2} \cdot \frac{1}{\Sigma_j\nabla W_{ij} \cdot \Sigma_j\nabla W_{ij} + \Sigma_j(\nabla W_{ij}\cdot \nabla W_{ij})} kPCI=Δt2m20.5ρ02ΣjWijΣjWij+Σj(WijWij)1
def formula_kPCI(i):
    sum1=ti.Vector([0.0, 0.0])
    sum2=ti.Vector([0.0, 0.0])
    for k in range(numNeighbor[i]):
        j = neighbor[i, k]      
        dir = (position[i]-position[j]).normalized()
        r = (position[i]-position[j]).norm()
        gradW= firstDW(r) * dir
        sum1 += gradW
        sum2 += gradW.dot(gradW)

    kPCI= - 0.5 * density0 **2 / timeStepSize **2 / mass**2 \
            * 1.0/ ( sum1.dot( sum1) + sum2 )
  1. 计算density_star
    在这里插入图片描述
def formula_densityStar(i):
    sum1 = ti.Vector([0.0, 0.0])
    sum2 = ti.Vector([0.0, 0.0])
    sum3 = ti.Vector([0.0, 0.0])
    for k in range(numNeighbor[i]):
        j = neighbor[i, k] 
        dir = (position[i]-position[j]).normalized()
        r = (position[i]-position[j]).norm()
        gradW = firstDW(r) * dir
        dV = velocity[i] - velocity[j]
        dA = acceleration[i] - acceleration[j] #a_nonp
        sum1 += kernelFunc(r)
        sum2 += dV.dot(gradW)
        sum3 += dA.dot(gradW)

    #densityStar
    densityStar=  sum1 * mass \
                + sum2 * mass * timeStepSize  \
                + sum3 * mass * timeStepSize * timeStepSize
  1. 计算预测压力
    在这里插入图片描述
    我们这里称之为pressureStar
def formula_pressureStar():
    pressureStar = kPCI* (densityStar-density0)
  1. 计算压力梯度加速度
    在这里插入图片描述
def formula_accP(i):
    sum1 = ti.Vector([0.0, 0.0])

    for k in range(numNeighbor[i]):
        j = neighbor[i, k] 
        dir = (position[i]-position[j]).normalized()
        r = (position[i]-position[j]).norm()
        gradW = firstDW(r) * dir
        sum1 += gradW

    accP= -1.0 * mass * 2 * pressureStar / (density0 *density0) \
                    * sum1
  1. 计算校正密度

在这里插入图片描述
与densityStar的区别仅仅在于把accNonP换为了accP(accNonP用acceleration表示了)

def formula_densityP(i):
    sum1 = ti.Vector([0.0, 0.0])
    for k in range(numNeighbor[i]):
        j = neighbor[i, k] 
        dir = (position[i]-position[j]).normalized()
        r = (position[i]-position[j]).norm()
        gradW = firstDW(r) * dir
        dA = accP[i] - accP[j] 
        sum1 += dA.dot(gradW)
        
    densityP = sum1 * mass * timeStepSize * timeStepSize
  1. 计算矫正压力
    在这里插入图片描述
    由于要迭代,所以
    我们把该压力(等式左边的)称之为pressureNew
    右边的称之为pressureOld
    最开始,pressureOld=pressureStar
    后来不断迭代pressureOld与pressureNew
def formula_pressureNew(i):
    sum1 = ti.Vector([0.0, 0.0])
    for k in range(numNeighbor[i]):
        j = neighbor[i, k] 
        dir = (position[i]-position[j]).normalized()
        r = (position[i]-position[j]).norm()
        gradW = firstDW(r) * dir
        dA = accP[i] - accP[j] 
        sum1 += dA.dot(gradW)
        
    pressureNew =   pressureOld \
                    + kPCI * \
                    (
                        density0 - densityStar 
                    -   timeStepSize * timeStepSize * mass * sum1
                    )
    pressureOld = pressureNew

7 计算收敛条件
在这里插入图片描述

def formula_isConverge(i):
    judge = ( density0 - densityStar + densityP ) / density0
    if judge <1e-3:
        isConverge = True

代码的重整与合并

我们发现许多代码部分是重合的,所以可以进行合并,合并后的代码为:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值