求助:WCSPH模拟中Accessing field ... 报错

代码

请查看GitHub。直接运行文件 draft1.py 即可开始模拟,由ggui绘制,着色为密度的数值大小。

问题

在SPH的模拟中,用CPU、debug模式,进行到某一步的时候会在“分配粒子到网格”时出现错误中断:

(kernel=allocate_particles_to_grid_c56_0) Accessing field (S40place) of size (100, 136) with indices (-2147483648, -2147483648)

这个报错应该和论坛上之前的一篇MPM相关的帖子不同?我注意到:

  1. 同一离散粒子半径r=0.001m下,每次模拟中断的时间都不同,但报错的size都是(100, 136);而在半径r=0.002m时报错的size为(51, 69)。
  2. 最后的indices (-2147483648, -2147483648) 其实就是 -2^32 。

猜测这个报错的意思是有变量的数值超过了精度范围吗?不知道这两组数具体是指什么,似乎size那一组数和模拟的边界有关?因为发现在 [0.584m*0.8m] 的模拟中,报错为 (100, 136),两数比值相似;在 [0.8m*0.8m] 的模拟中,报错为 (69, 69) 。但是为什么是这个数?后面的两个 -2^32 是什么意思?

怀疑的原因

  1. 核函数梯度的标准化?
    经过标准化后的核函数梯度可以很好地在非对称模式下模拟一些行为。直接使用无标准化的核函数梯度可以正常进行模拟,但引入标准化矩阵后就会有这个报错。然而经过一些基本的检查,并未发现在计算这个标准化矩阵的过程中出现了什么错误。(标准化和未标准化的代码均包含在仓库里。)
  2. 网格中或者邻域表中的粒子数量超出了100个的限制?但是当把限制改为200个时,模拟运行地会更久一些,但最终会出现同样的报错信息。
  3. 把WCSPH的密度计算方案改为了根据密度梯度更新。
    由于直接使用核函数会导致边界尤其是自由界面附近精度不够,因此使用不会降低精度的标准化核函数梯度来更新密度。但是可能自己想的密度计算方法、每一个step中的计算顺序存在错误?

代码背景

基于Mingrui Zhang的太极图形课SPH代码,增加了ggui绘制、按colormap绘图、边界虚粒子、虚粒子赋人工速度(@Bui2008 section 4.4)、Wendland C2核函数(@Bui2021 section 2.3)、核函数梯度的标准化(@Bui2021 equation 21)、根据密度梯度更新密度(自己想的,为了避免在模拟中直接使用核函数)。

核函数梯度的标准化公式另见:

目标

使用标准化的核函数梯度模拟水的行为。

附图

这个报错很普遍,没法直接判断出BUG的来源

我只是在此列举出几种可能性:

可能性1

也许和你取的核半径有关。假如你取0.001,用没有归一化的spiky核的话:

梯度会急剧增长,都已经增长到了1e9,显然会造成很多麻烦。

可能性2

这个错误有可能是下标越界,那就去看你是如何计算下标。当然表面上看是这样,但错误不一定源于此。

有一种可能是粒子没有及时地被力弹开,导致过密集的堆积。这可能是计算力(尤其是压力梯度力)的错误。

还有一种可能就是在边界反弹的时候,把粒子反弹到了同一位置,导致此处受力无穷大。

还有一种可能是初始化的时候,采用了MPM的随机初始化。这显然是不可取的。MPM是混合法,实际的受力是计算在网格上的,因此没有什么问题。但对于SPH来说,假如粒子过于密集,必然造成奇异点。

我在别人的代码中很少见到核函数归一化的,这点很奇怪。这明显违背了核函数的定义。但是既然大家都没归一化,那么证明归一化或许不是问题的根源。

非常感谢大佬解答!

1.

请问可以讲一下这个报错里两组数字的具体含义吗?很是好奇,看taichi的源码也看不懂Accessing field报错信息那一块儿在讲什么,只感觉是数组越界之类的东西。

2.

这个的意思应该也是要避免过密的堆积?我观察到的现象是当流体粒子右下角的部分开始有乱窜的趋势时,会报错。那个区域应该是不存在过密的堆积的,邻域粒子基本上在13~26个之间(包含虚粒子)。不过我再想办法检测一下最小的粒子间距。

3.

所以这个矩阵L是翻译成 “归一化矩阵” 吗? :joy: 我也是没有在其他地方见过,不知道中文应该叫什么。但是感觉这个处理挺好的,相当于将SPH空间求导公式的非对称形式从一阶精度扩展到了二阶精度,并且能够保持边界附近粒子的数值不会因为邻域粒子的减少而降低。应该说它没有改变核函数的归一化条件,而是对SPH计算公式的更高精度的一种推导。

不过这个normalisation的过程涉及到了矩阵求逆,是不是也有可能是因为存在不可逆矩阵的情况导致的计算中断?不过我想如果是矩阵求逆出错,应该会有另外的报错信息吧!

从这一点来看,就很有可能是过密集堆积造成的。所有粒子都堆在某个网格内,就会造成超出邻域链表数量的报错。
但是过密集堆积有很多种可能性。有可能是你的压力梯度力没计算对(因为压力梯度力是用来推开粒子的,没能推开导致过密)。也有可能是我上面说的,边界反弹的时候没处理好导致全部反弹到同一位置了。或者是初始堆积就过密。