本文最后更新于 2023-10-22,文章内容可能已经过时。

现代浏览器可以高效地运行js程序,可以方便的绘图,所以这次我用html+js,向大家示范一个简单的CFD程序。

首先,我们要建立这池水的简化模型。

假设这池水,液面的垂直速度足够小(水面足够平缓),可以作如下简化:

  1. 可以用一维数组,描述从左到右液面高度。

  2. 液面只传递纵波。

  3. 有黏度参数,“液柱”之间有损耗

首先要初始化液面高度数组

<code class="lang-js">var buffer = []
for(var i=0;i<128;i++){   buffer.push(.5*i); buffer init }< code></128;i++){></code>

仿真的每一步,都要计算多个参数,下面列出。

1.液体在重力作用下,如果两个相邻的液柱有高度差,较高的液柱会受到向下的压力,较矮的液柱会受到向上的压力。简单的讲,设两个相邻液柱的液面高度差为delta_h, F 正比于 delta_h。

<code class="lang-js">
    //calculate force
    for(var i=0;i<buffer.length;i++)     {       if(i>0){
        //not start of array.
        forcebuffer[i] += buffer[i-1]-buffer[i];
      }
      if(i<buffer.length-1){          not end of array.         forcebuffer[i] +="buffer[i+1]-buffer[i];"       }     } < code></buffer.length-1){></buffer.length;i++)></code>

2.液体具有黏度,可以近似认为两条液柱之间存在剪切力。

看不懂也无所谓,结论就是:这个力正比于两条相邻液柱的相对速度。
因此,在上一段代码中,加入通过速度计算剪切力的项:

<code class="lang-js">
//calculate vertical velocity
    for(var i=0;i<buffer.length;i++){       var f="0;"       if(i>0&&i<buffer.length-1)       {         f="velocitybuffer[i+1]-velocitybuffer[i]"         + velocitybuffer[i-1]-velocitybuffer[i]         f*="0.2;" viscosity factor       }       forcebuffer[i] +="f;"       velocitybuffer[i] * .2;     } < code></buffer.length-1)></buffer.length;i++){></code>

其中的0.2,可用于控制液体黏度。黏度较低的液体,可以让高频振荡随液面传递得更远(衰减得更慢)。

  1. 我们都知道1/2mv^2的公式:能量正比于速度的平方。在我们的简化模型中,没有模拟液体内部各种紊流,自然也就无法模拟这部分能量的损耗,但我们知道这种损耗在真实液体中是存在的。因此,我用了一个衰减因子(0.99)逐步衰减速度。相比不衰减的情况,水面在受到扰动后会逐渐趋于平静。

<code class="lang-js">
for(var i=0;i<buffer.length;i++){       velocitybuffer[i] *="  0.99;" damping     } < code></buffer.length;i++){></code>

5.液面高度是对液面垂直速度的积分。这个很简单。

<code class="lang-js">    //calculate vertical height
    for(var i=0;i<buffer.length;i++){       buffer[i] +="velocitybuffer[i]*0.5;"     } < code></buffer.length;i++){></code>

到此,一步仿真就结束了。

我通过定时器设定每秒仿真50步。

<code class="lang-js">function drop(){ //random drop on surface
  var m = Math.floor(Math.random()*10)+3;
  var i = Math.floor(Math.random()*(buffer.length-m))
  var k = Math.random()*90;
  for(var n=i;n<i+m;n++){     buffer[n]="k+Math.random()*10;"   } }< code></i+m;n++){></code>