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


上面这个水池是最终效果。先别急,往下看。

=====这是分隔线=====

首先,我们要建立这池水的简化模型。
假设这池水,液面的垂直速度足够小(水面足够平缓),可以作如下简化:
1. 可以用一维数组,描述从左到右液面高度。
2. 液面只传递纵波。
3. 有黏度参数,“液柱”之间有损耗

首先要初始化液面高度数组,随便填充点啥吧
var buffer = []
for(var i=0;i<128;i++){
  buffer.push(.5*i); //buffer init
}

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

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

    //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];
      }
    }


2. 液体具有质量,根据 F=ma, 加速度正比于受力,故速度正比于受力的积分。下面的代码将受力乘以一个因子,积分为速度。

//calculate vertical velocity
    for(var i=0;i<buffer.length;i++){
      velocitybuffer[i] += forcebuffer[i] * .2;
    }


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


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

//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] += forcebuffer[i] * .2;
    }

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


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

for(var i=0;i<buffer.length;i++){
      velocitybuffer[i] *=  0.99; //damping
    }


5. 液面高度是对液面垂直速度的积分。这个很简单。
    //calculate vertical height
    for(var i=0;i<buffer.length;i++){
      buffer[i] += velocitybuffer[i]*0.5;
    }

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


我使用了Chart.js控件来展示水池,因此我需要不断地将buffer[]的内容发给图表,然后刷新图表,以产生动画效果。
    //update chart
    for(var i=0;i<buffer.length;i++){
      mynewchart.datasets[0].points[i].value = buffer[i];
    }
    mynewchart.update();


我通过定时器设定每秒仿真50步。
var timer = setInterval('step_on()',20)


水面一定要添加扰动才好玩,怎么添加扰动呢?我做了一个随机扰动函数,按一次按钮扰动一次。
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;
  }
}


最终效果:
262673



html和js源文件下载:在浏览器中可以直接运行
HomemadeCFD.zip14.0k76次

[修改于 4 年前 - 2016-04-11 17:30:34]

来自 机器学习
 
2016-4-11 17:31:20
novakon(作者)
1楼
下载zip文件,解压得到html文件,用编辑器打开即可看到全部代码。
折叠评论
加载评论中,请稍候...
折叠评论
2楼
真厉害 现在浏览器的脚本都可以做这么牛逼了
折叠评论
加载评论中,请稍候...
折叠评论
3楼
或许还可以试试用WebGL呈现下三维的情况。

js目前还是适合做一些轻量的应用,毕竟这玩意压缩后很适合网络传输。
比较繁重的运算任务扔给服务器。
折叠评论
加载评论中,请稍候...
折叠评论
2016-4-12 03:30:15
novakon(作者)
4楼
引用 drzzm32:
或许还可以试试用WebGL呈现下三维的情况。

js目前还是适合做一些轻量的应用,毕竟这玩意压缩后很适合网络传输。
比较繁重的运算任务扔给服务器。
WebGL需要比较繁琐的初始化,以及图形编程,用现成控件省事一点。但我有时间一定会做一个WebGL的版本。

话说,有谁愿意做一个命令行版本?
折叠评论
加载评论中,请稍候...
折叠评论
2016-5-8 09:19:28
2016-5-8 09:19:28
5楼
波速跟液体深度有关吗?
折叠评论
加载评论中,请稍候...
折叠评论
6楼
下载压缩包 下不了 点击链接就出乱码
折叠评论
加载评论中,请稍候...
折叠评论
novakon(作者)
7楼
引用 迪纳米斯:
波速跟液体深度有关吗?
在这个程序中是无关的(假定水无限深)。
折叠评论
加载评论中,请稍候...
折叠评论
8楼
程序很棒,如果水深曲线可以调整、还有位置可以定义、可以定义扰动波形频率幅度阻尼、这样就更有波浪槽的效果了。甚至比波浪槽还好用 。
模拟个海啸木有问题。
折叠评论
加载评论中,请稍候...
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

插入资源
全部
图片
视频
音频
附件
全部
未使用
已使用
正在上传
空空如也~
上传中..{{f.progress}}%
处理中..
上传失败,点击重试
{{f.name}}
空空如也~
(视频){{r.oname}}
{{selectedResourcesId.indexOf(r.rid) + 1}}
ID:{{user.uid}}
{{user.username}}
{{user.info.certsName}}
{{user.description}}
{{format("YYYY/MM/DD", user.toc)}}注册,{{fromNow(user.tlv)}}活动
{{submitted?"":"投诉"}}
请选择违规类型:
{{reason.description}}
支持的图片格式:jpg, jpeg, png