以下流体模拟是paper by Stam的翻译。发生了真正可怕的事情。每次使用低DIFF=0.01
运行程序时,值都会从小开始,然后快速展开或“爆炸”。我仔细检查了数学例程。由于代码以一个0.5
开始,因此在数学上它是乘法并添加一串零,因此最终结果应接近零密度和其他向量。
代码很长,所以我将它分成块并删除了额外的代码。减去所有开头和SDL代码只有大约120行。我花了几个小时尝试改变无济于事,所以非常感谢帮助。
经过一些实验后,我相信当DIFF
设置得太低时可能会出现一些浮点错误。当值从0.01
增加到0.02
时,值不会爆炸。不过,我不认为这是整个问题。
要清楚,1201ProgramAlarm和vidstige的当前答案无法解决问题。
粗体中的部分是重要部分,其余部分是为了完整性。
开头的东西,跳过
#include <SDL2/SDL.h>
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define IX(i,j) ((i)+(N+2)*(j))
using namespace std;
// Constants
const int SCREEN_WIDTH = 600;
const int SCREEN_HEIGHT = 600; // Should match SCREEN_WIDTH
const int N = 20; // Grid size
const int SIM_LEN = 1000;
const int DELAY_LENGTH = 40; // ms
const float VISC = 0.01;
const float dt = 0.1;
const float DIFF = 0.01;
const bool DISPLAY_CONSOLE = false; // Console or graphics
const bool DRAW_GRID = false; // implement later
const int nsize = (N+2)*(N+2);
数学例程漫反射例程除以1+4*a
。这是否意味着密度必须<= 1?
void set_bnd(int N, int b, vector<float> &x)
{
// removed
}
inline void lin_solve(int N, int b, vector<float> &x, vector<float> &x0, float a, float c)
{
for (int k=0; k<20; k++)
{
for (int i=1; i<=N; i++)
{
for (int j=1; j<=N; j++)
{
x[IX(i,j)] = (x0[IX(i,j)] + a*(x[IX(i-1,j)]+x[IX(i+1,j)]+x[IX(i,j-1)]+x[IX(i,j+1)])) / c;
}
}
set_bnd ( N, b, x );
}
}
// Add forces
void add_source(vector<float> &x, vector<float> &s, float dt)
{
for (int i=0; i<nsize; i++) x[i] += dt*s[i];
}
// Diffusion with Gauss-Seidel relaxation
void diffuse(int N, int b, vector<float> &x, vector<float> &x0, float diff, float dt)
{
float a = dt*diff*N*N;
lin_solve(N, b, x, x0, a, 1+4*a);
}
// Backwards advection
void advect(int N, int b, vector<float> &d, vector<float> &d0, vector<float> &u, vector<float> &v, float dt)
{
float dt0 = dt*N;
for (int i=1; i<=N; i++)
{
for (int j=1; j<=N; j++)
{
float x = i - dt0*u[IX(i,j)];
float y = j - dt0*v[IX(i,j)];
if (x<0.5) x=0.5; if (x>N+0.5) x=N+0.5;
int i0=(int)x; int i1=i0+1;
if (y<0.5) y=0.5; if (y>N+0.5) y=N+0.5;
int j0=(int)y; int j1=j0+1;
float s1 = x-i0; float s0 = 1-s1; float t1 = y-j0; float t0 = 1-t1;
d[IX(i,j)] = s0*(t0*d0[IX(i0,j0)] + t1*d0[IX(i0,j1)]) +
s1*(t0*d0[IX(i1,j0)] + t1*d0[IX(i1,j1)]);
}
}
set_bnd(N, b, d);
}
}
void project(int N, vector<float> &u, vector<float> &v, vector<float> &p, vector<float> &div)
{
float h = 1.0/N;
for (int i=1; i<=N; i++)
{
for (int j=1; j<=N; j++)
{
div[IX(i,j)] = -0.5*h*(u[IX(i+1,j)] - u[IX(i-1,j)] +
v[IX(i,j+1)] - v[IX(i,j-1)]);
p[IX(i,j)] = 0;
}
}
set_bnd(N, 0, div); set_bnd(N, 0, p);
lin_solve(N, 0, p, div, 1, 4);
for (int i=1; i<=N; i++)
{
for (int j=1; j<=N; j++)
{
u[IX(i,j)] -= 0.5*(p[IX(i+1,j)] - p[IX(i-1,j)])/h;
v[IX(i,j)] -= 0.5*(p[IX(i,j+1)] - p[IX(i,j-1)])/h;
}
}
set_bnd(N, 1, u); set_bnd(N, 2, v);
}
密度和速度求解器
// Density solver
void dens_step(int N, vector<float> &x, vector<float> &x0, vector<float> &u, vector<float> &v, float diff, float dt)
{
add_source(x, x0, dt);
swap(x0, x); diffuse(N, 0, x, x0, diff, dt);
swap(x0, x); advect(N, 0, x, x0, u, v, dt);
}
// Velocity solver: addition of forces, viscous diffusion, self-advection
void vel_step(int N, vector<float> &u, vector<float> &v, vector<float> &u0, vector<float> &v0, float visc, float dt)
{
add_source(u, u0, dt); add_source(v, v0, dt);
swap(u0, u); diffuse(N, 1, u, u0, visc, dt);
swap(v0, v); diffuse(N, 2, v, v0, visc, dt);
project(N, u, v, u0, v0);
swap(u0, u); swap(v0, v);
advect(N, 1, u, u0, u0, v0, dt); advect(N, 2, v, v0, u0, v0, dt);
project(N, u, v, u0, v0);
}
我考虑了floating-point inconsistencies,但在使用-ffloat-store
进行编译后,问题仍然存在。
答案 0 :(得分:5)
问题与add_source()
中缺乏规范化有关。
当您的密度变得足够稳定时(x0
与x
的分布非常相似,直到比例因子),add_source()
有效地将x
乘以约1+dt
1}},导致你的指数爆炸。 DIFF
的高值会通过x
对x0
lin_solve()
的{{1}}进行加权来掩盖此效果,这意味着有效乘数更接近1
,但是仍高于1。
效果是,每次迭代都会增加质量。如果它不能&#34;展开&#34;在边缘足够快,它将开始堆积。一旦密度变得完全静止,它将以1+dt/(4a)
确定的指数速率增加质量。
使用您的指定设置(dt=0.1, a=0.1*0.01*20*20=0.4
),这是1+0.1/1.6 ~ 1.06
。
修复是在add_source中进行规范化:
x[i] = (x[i]+dt*s[i])/(1.0f+dt);
,或将c
参数计算为lin_solve()
1+4*a+dt
。要么会迫使质量下降。
答案 1 :(得分:3)
麻烦的一个来源是last_updated = db.Column(db.DateTime, default=db.func.current_timestamp())
。您的lin_solve
和i
循环从零开始,但您引用j
,它将访问越界数组元素IX(i-1,j)
。
答案 2 :(得分:2)
看到这一点,我立刻觉得我必须回答。我在阅读这篇文章的时候发表了这篇文章。我在Android上实现了他的东西并且喜欢它。我甚至在21世纪初在Umeå演讲时遇到了这个人,他是一个非常友善的人。高大的。 :)
所以问题。你没有进行速度传播步骤,我认为如果我没记错的话,这对于“炸毁”至关重要。