python流体传热模拟 python流体力学_帧差法matlab代码


在游戏中模拟流体并不是什么新鲜事,但是我几乎就没看到什么好的入门文章。有些文章用尖峰波或者FFT模拟,但那毕竟是统计学方法,和流体力学还是不搭边。其余的文章倒是用了纳韦斯托克方程,但那也仅仅是把纳韦斯托克方程写了一遍,好一点的还给仍你一些居复杂的代码,对我等入门者来说实在是看不懂。

最近我在github上找到三个不错的python jupyter notebook库

1.首先是大名鼎鼎的12步纳韦斯托克方程,oschina上有中文翻译,油管上python版本和matlab版本的对应编程视频

barbagroup/CFDPython

2.然后是格子波兹曼方法,也是除了纳韦斯托克方法外的一个经典方方法

pylbm/pylbm

3.然后这个空气动力学python库,虽然说是空气动力学,但和流体力学相同的地方很多

barbagroup/AeroPython

于是这个“游戏流体力学基础”系列将在这些库的帮助下,从最基本的方程开始推导,尽量保证猫都都能看懂,来完成游戏中的流体模拟吧!预计会有十几篇。

好了,我们开始学习一些基本的火系魔法知识吧!【误】

一维热传导

首先我们看一个这样的问题,对于一个一维物体,如果它的左端点温度是700度,右端点温度是300度,并且这个物体上的温度是连续均匀变化的,那么这个物体的其它位置的温度是多少?

当然,由于计算机只能求解离散的物体,我们需要将问题转化成如下形式,将一个物体分成八格,每格都有一个自己的温度值。如果最左边一格的温度是700度,最右边一格的温度是300度,并且每格之间的温度变化也是均匀连续的,那么中间六格的温度各是多少?


python流体传热模拟 python流体力学_python流体传热模拟_02


你会说,这还不简单嘛,不就是解一元一次方程嘛。但是这仅仅是一维的情况,并且我们确定的温度值只有两个而已。如果到了三维,并且确定的温度值有很多个的时候,要求解的温度值也很多的时候,要通过解析方法求解就很麻烦了。我们得换个法子。

既然我们不知道中间六格的温度,我们就瞎猜嘛,说不定猜对了呢?

我们猜中间六格的温度都是0度。嗯,结果显然看起来不太对,但我们可以改一下。除了我第一格和第八格的温度已经给定不能再改变外,其它六格的温度我们都可以继续改,让结果看起来更加合理一些。


python流体传热模拟 python流体力学_热传导_03


牛顿爷爷说过,当两个互相接近的物体温度不相等的时候,它们的温度肯定会被互相影响。也就说,第二格的温度会因第一格和第三格的温度的影响而改变,第三格的温度会因为第二格和第四格的温度的影响而改变....但具体会如何被影响呢?

某个方格的值会被附近方格的值影响,是不是很熟悉?对啦,高斯模糊就是用的这种方法!


python流体传热模拟 python流体力学_热传导_04


简单起见,我们就设第二格的温度等于(第一格的温度 加上 第三格的温度)除以二。于是经过一次计算后,每个方格的温度就变成了下面的样子。


python流体传热模拟 python流体力学_帧差法matlab代码_05


显然结果看起来还是不太对。但是,热量会持续传导,时间会见证一切。


python流体传热模拟 python流体力学_帧差法matlab代码_06


python流体传热模拟 python流体力学_帧差法matlab代码_07


python流体传热模拟 python流体力学_着色器_08


可以看到在第100秒,方格之间的温度差是57度左右,拿出除法一算,400 / 7 大约也是57度,很不错的。实际上第50秒的结果已经很接近第100秒的结果了,所以如果对精度要求不那么高,那么就算100次就结束吧。

既然我们的瞎猜法很不错,那么我们就要总结经验,把瞎猜法发扬光大。不过我们之前说的:“第二格的温度等于(第一格的温度 加上 第三格的温度)除以二” 这句话太长了,我们将其转换为数学语言:


python流体传热模拟 python流体力学_热传导_09


其中T就是温度,下标n代表第n个方格,t代表第t秒。然后我们就可以得到第n个方格第t+1秒的温度和第t秒温度之间的关系:


python流体传热模拟 python流体力学_迭代_10


不过上面这个公式还不够简洁,让我们将其写为人看不懂的形式吧:


python流体传热模拟 python流体力学_着色器_11


dt即时间的跨度,我们之前将其设置为1秒,也就是1。dx就是空间的跨度,也是不同网格之间的距离,我们之前将其设置为1格,也就是1。这里,时间t只有一阶导数是因为时刻t的温度只受到t-1的温度的影响。而x为二阶导数是因为,第n个格子会受到n-1和n+1两个格子的影响。

然后我们将这个公式写得更加简洁一些,然后取个名字,就叫一维热传导方程吧:


python流体传热模拟 python流体力学_帧差法matlab代码_12


这里的a就是一个迭代参数,我们之前将其设置为1/2。实际上可以设置得更小,也就是热传导速度更慢,防止出现一些莫名奇妙的结果。当然这样求解起来也就更慢了。如果大于1/2,那么就会出现不稳定的状态,比如将其设置为0.7,仅仅第三次迭代后第二个网格的温度就高于730度了,产生不稳定状态的原因也很好理解,第三格实际上不能把0.7的热量同时分配给第二格和第四格,第三格没那么高的温度。

好了,现在你可以拿这个公式去欺负那些没见过世面的麻瓜了!记住把式子改成下面的样子让他们更加看不懂,其中三角形是拉普拉斯算子,u是待求解的量,我们这里就是温度。


python流体传热模拟 python流体力学_着色器_13


不过这个的一维热传导方程是否还能写成别的形式呢?

比如我们要测量b处的温度变化速度,而a处的温度会影响b处,它们之间的关系应该是怎样的呢?首先它们之间会有一个介质,介质面积越大,热传导速度越快,热量变化速度也越快,这就是面积A。介质厚度越大,热量变化越慢,这就是厚度dx。a处和b处的温度差越大,温度变化也越快,这就是温度差Ta - Tb。除了这个三个因素外,介质的材质参数k也会影响温度变化速率,如下图所示。


python流体传热模拟 python流体力学_着色器_14


根据傅里叶法则,我们得到热传导方程为如下形式


python流体传热模拟 python流体力学_热传导_15


其中等式左边是热量的变化速度。所以对于之前的迭代参数a来说,它的实际物理意义是材质参数乘上介质面积。

一维热传导的着色器代码

先废话两句。其实这种模拟流体的代码通常写在Python和Matlab和Foratan上,上github上一搜一大把,当然看不看得懂就是另一回事了。但是由于看这篇文章的读者对unity更加熟悉一些,而且很方便移植到opengl/directx上,所以就用unity写了。顺便吐槽一句unreal写着色器太麻烦了。

看懂下面的内容需要一点unity着色器的知识,如果你没有,现在立马去把那本《UnityShader入门精要》吃掉。

我们在着色器上实现的方法现在很简单,就是在摄像机上挂一个全屏处理脚本,直接把着色器生成的材质放到整块屏幕上。

我们需要三个RenderTexture,第一个_rtNull是全黑的纹理,也就是初始状态温度全为0,此时还没有设置边界条件。

第二个_rt0代表上一帧的结果,经过计算后得到本帧的结果_rt1,再将后者输出的屏幕上。最后再将rt1复制给rt0,用于下一帧的计算。

下面是要挂到摄像机上的代码,之后我们会将_TexelNumber设置为8,也就是方格的数量。同时将每秒的帧数设置为2,方便我们动态观察。


public


然后在资源管理器中创建这三个RenderTexture,然后拖到脚本上即可。注意RenderTexture大小要改成和输出屏幕一样大小。


python流体传热模拟 python流体力学_迭代_16


我们现在不用修改顶点着色器,所以下面是像素着色器的代码,原理和高斯模糊很类似,采集上一帧图像中左边网格的值和右边网格的值再平均,就得到新的温度值。


float


最后Return语句的原理如下。我们假设我们模拟的世界中,温度的颜色如下变化


python流体传热模拟 python流体力学_帧差法matlab代码_17


于是,我们就可以将RGB中的R设置为1,B设置为0,仅将G作为变量。当G从0到1变化时,我们就假设温度从0度到1000度变化,于是颜色就也如上图变化。点击,播放,现在场景看起来如下,大约过了十秒左右颜色变化就不太明显了。如果把_TexelNumber改得更大一些,那么迭代需要的时间也更久。


python流体传热模拟 python流体力学_热传导_18


是不是很简单?现在你已经初步掌握了火系魔法的要点,快用爆裂魔法去和魔王军首领对决吧!相信你一定可以的!【误】

二维及三维热传导方程

之前说过我们的瞎猜法,在一维的情况下,就是左边格子和右边格子的平均值。那么在二维情况下怎么办?你肯定已经想到了,就是上下左右四个格子的平均值!三维就是上下左右前后六个格子的平均值,就这么简单。

但不仅仅能取上下左右四个格子,对角线上四个其实也可以取,就连要求解的格子本身也可以取上一帧的值,不过要乘上一些不同的权重。这就是格子波兹曼方法,lattice boltzmann method。这个我们之后的篇章再讨论。


python流体传热模拟 python流体力学_迭代_19


写成离散化形式就是如下:


python流体传热模拟 python流体力学_着色器_20


二维代码和三维方程我就不贴了。就把它当作一个挑战留下来吧。值得注意的是我们这里的dt,dx和dy都是1,而a也仅仅是简单的权重,所以算是大大简化了这个方程。三维方程代码编写方式更加复杂一些,我们之后的篇章再讨论。

我将左下角温度设置为700度,右上角300度,右下角0度,最后得到这样的图。


python流体传热模拟 python流体力学_帧差法matlab代码_21


现在我们已经会搓小火球了,接下来我们正式开始学习如何搓水球,下一篇文章《用平流方程模拟染料流动》:

光影帽子:【游戏流体力学基础及Unity代码(二)】用平流方程模拟染料流动zhuanlan.zhihu.com

python流体传热模拟 python流体力学_帧差法matlab代码_22