在2D网格上建模膨胀气体的算法

4
我有一个简单的程序,核心是一个二维浮点数数组,表示气体浓度,我一直在试图想出一个简单的算法,模拟气体向外扩散,像云一样,最终导致整个网格上气体浓度相等。
例如,给定状态的进展可能是: (为了简单起见使用整数)
开始状态
00000 00000 00900 00000 00000
一次算法后的状态
00000 01110 01110 01110 00000
另一次算法应该会给出一个5x5的网格,其中每个格子都包含值0.36(9/25)。 我已经在纸上尝试过,但无论如何我都无法理解这个算法。 那么我的问题是,我应该如何编写这个算法?我尝试过一些方法,应用卷积,尝试依次处理每个网格单元并将其分配给其邻居,但它们最终都会产生不良影响,例如最终比我最初开始的气体少,或者所有气体运动都朝着一个方向而不是从中心向外扩散。我真的完全搞不懂它,并且非常感谢任何帮助。

快速问题:你是否使用单独的数组来保存下一次遍历的结果?如果没有,你可能会遇到你所描述的奇怪效果。 - Zach Scrivena
我认为Zach Scrivena是正确的,你的代码中一定有bug。你所描述的算法会导致质量守恒和在所有方向上的均匀分布。这个bug可能也与你如何处理边缘有关。 - David Norman
实际上在所有方向上并不完全均匀,因为你将(+-1,+-1)方向的权重与(+-1,0)和(0,+-1)方向的权重相同。这将导致云朵呈正方形而不是圆形。 - David Norman
我所呈现的算法图片只是我希望实现的一个例子,而不是我目前拥有的。我仍处于纸笔阶段,试图找出如何使它按照我的意愿执行的阶段。我确实为输出使用了单独的数组,所以至少在这一点上我做得对。 - RichardBennet
1
RichardBennet,你可以确定一个能生成结果的方程。每当你进行物理建模时,都需要某种控制运动的方程...你需要某种潜力,否则就没有任何运动。 - Nope
7个回答

6
如果你忽略对流,那么它就是一个扩散问题;如果你不忽略对流,那么它就是一个流体动力学/质量传递问题。如果你从头开始解决问题,你需要从欧拉(固定控制体)视角出发,列出质量和动量守恒方程。
这是一个瞬态问题,所以你需要进行积分,将状态从时间t(n)推进到时间t(n+1)。你展示了一个网格,但没有说明你如何在时间上求解。你尝试过哪种积分方案?显式?隐式?Crank-Nicholson?如果你不知道,那么你的方法是错误的。
关于这个主题,我真的很喜欢S.W. Patankar的《数值传热与流体流动》一书。现在有点过时了,但我喜欢它的处理方式。虽然我读这个主题的时候已经29年了,但它仍然很好。我认为它适合第一次研究这个主题的人。

5
在您提供的示例中,您的第二阶段有一个1的核心。通常扩散需要浓度梯度,因此大多数与扩散相关的技术不会在下一次迭代中改变中间的1(它们也不会在第一次迭代后到达该状态,但是当您拥有一块相等值的区块时更容易看出)。但正如您帖子上的评论者所说,这不太可能是净运动的原因。减少气体可能是边缘效应,但也可能是四舍五入误差的问题-将CPU设置为四舍五入,然后总计气体并不时进行修正。

Pete,你也从Java论坛转过来了。欢迎另一个聪明的贡献者加入。 - duffymo
在一次迭代后,9个核心位置之间达到值为1的平衡是相当可行的。中间和其他对称位置的值将在每两次迭代后改变。 - jeffD

2

看起来您正在尝试使用有限差分法求解具有Neumann边界条件(边缘绝缘)的热方程。关于这种方法有很多文献资料。维基百科上关于有限差分法的页面描述了一种简单但稳定的方法,但是适用于Dirichlet边界条件(边缘处密度不变)。修改边界条件的处理应该不太困难。


1

1

这里有一个简单的一维解决方案:

初始设置是在原点处浓度为9,所有其他正负坐标都为0。

初始状态: 0 0 0 0 (9) 0 0 0 0

找到下一个迭代值的算法是从原点开始,将当前浓度与相邻的邻居平均。原点值是边界情况,平均值考虑了原点值及其两个邻居,即3个值之间的平均值。所有其他值实际上是在2个值之间平均。

迭代1后: 0 0 0 3 (3) 3 0 0 0

迭代2后: 0 0 1.5 1.5 (3) 1.5 1.5 0 0

迭代3后: 0 .75 .75 2 (2) 2 .75 .75 0

迭代4后: .375 .375 1.375 1.375 (2) 1.375 1.375 .375 .375

你可以在循环中执行这些迭代。每n个迭代输出一次状态。你可以引入一个时间常数来控制多少次迭代代表墙上时钟的一秒钟。这也取决于整数坐标表示的长度单位。对于给定的H/W系统,你可以通过经验调整这个值。你还可以引入一个稳态容差值来控制程序何时说“所有邻居值都在这个容差范围内”或“迭代之间没有任何值变化超过这个容差”,因此算法已达到稳态解。


1

看起来你想要的是类似于平滑算法的东西,通常在像Photoshop这样的程序中使用,或者像这个简单的火焰效果这样的老派演示效果。

无论你使用什么算法,它都可能会帮助你双缓冲你的数组。

一个典型的平滑效果将会是:

begin loop forever
    For every x and y
    {
        b2[x,y]  = (b1[x,y] + (b1[x+1,y]+b1[x-1,y]+b1[x,y+1]+b1[x,y-1])/8) / 2
    }
    swap b1 and b2
end loop forever

0

给定起始浓度,每次迭代的浓度可以通过以下方程式获得:

concentration = startingConcentration/(2*iter + 1)**2

iter是时间迭代。所以对于你的例子。

startingConcentration = 9
iter = 0
concentration = 9/(2*0 + 1)**2 = 9

iter = 1
concentration = 9/(2*1 + 1)**2 = 1

iter = 2
concentration = 9/(2*2 + 1)**2 = 9/25 = .35

在每个“时间步”之后,您可以设置数组的值


9/((2*0 + 1)2) = 9/(22) = 9/4 = 2.25 != 9 - Die in Sente
死在 Sente…再检查一下你的数学。 (2*0 + 1) = 1,1 ** 2 = 1 - Nope

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接