AlgebraMaster

Modern C++ 创造非凡 . 改变世界

导航

流体模拟基础1

1,散度 鼠标右键打开(高清图)

 

 1.1 探讨1D 情况下的advection

(来自Fluid simulation for computer graphic - Robert Bridson)

 

初始化一些变量:,

其中dt 按照文章写的:

 

import numpy as np
from matplotlib import pyplot
import time,sys

nx = 100
dx = 2/(nx-1)
nt = 100  #25 steps
u  = 1   #assume advection speed = 1
dt = dx/2


q = np.array([0.2 for x in range(nx)])

############ our init condition#######################################
# THIS CONDITION FROM PAGE-41 Robert Bridson
for x in range(2,11,1):
    q[x] = x/10.0
q[10:19] = (q[2:11])[::-1]
######################################################################

 

这个是初值条件:

pyplot.plot(np.linspace(0,nx,nx),q)
pyplot.show()
pyplot.close()

 

 

 接下来先用 这个方法做:

 

def eulerian_advection():
    rhq = q.copy()  # this function compute THIS, and return this!
    qn = np.zeros(nx)
    for step in range(nt):
        # every time step we need update our READY-Q: qn
        qn = rhq.copy()
        for i in range(1,nx):
            rhq[i] = qn[i] - u * dt / dx* (qn[i] - qn[i-1])
    return rhq

pyplot.plot(np.linspace(0,nx,nx),q)
pyplot.show()
pyplot.close()

 

然后把这个写成人类可读的 py易读:

def eulerian_advection_very_numpy():
    rhq = q.copy()  # this function compute THIS, and return this!
    qn = np.zeros(nx)
    for step in range(nt):
        # every time step we need update our READY-Q: qn
        qn = rhq.copy()
        rhq[1:] = qn[1:] - u * dt / dx *(qn[1:] - qn[0:-1])
    return rhq
pyplot.plot(np.linspace(0,nx,nx),q)
pyplot.show()
pyplot.close()

 这个公式经过100 time step size步带来的效果,

 

 使用semi-lagrangian:

 

# fluid simulation computer graphics [page 33]
def semi_lagrangian():
    rhq = q.copy()  # this function compute THIS, and return this!
    qn = np.zeros(nx)

    for step in range(nt):
        qn = rhq.copy()

        for i in range(1, nx-1):
            xj = (i-1) * dx
            xg = i*dx
            xp = xg - dt * u
            w = (xp-xj)/dx
            qj  = qn[i-1]
            qj1 = qn[i+1]

            rhq[i] = (1-w) * qj + w * qj1
        plot_data(rhq)
    return rhq

semi_lagrangian()

 1.2 signed distance

假设速度是vector(0,1,0) 会出现下面的可视化:

 

 

 

 

2,如何在Houdini里解 2-D Linear Convection

离散完之后:

 

 

 初始条件:

Boundry地方全为0

中间设置了一些 数值.不过是把u映射到y 的高度

 

 

int row = chi("ny");
int column = chi("nx");
float c = 1;
float dx = 2.0 / float(row - 1);
float dy = 2.0 / float(column - 1);
float sigma = .2;
float dt = sigma * dx;


for(int j=0; j< row ;j++)
{
    for(int i=0; i< column ; i++)
    {
        int pt =  j * row + i;
        float u = point(0,"u",pt);
        
        // partial u/x
        int pt_x_next = j * row + i-1;
        float x_next_u = point(0,"u",pt_x_next);
        float partial_u_x = u - x_next_u;
        
        // partial u/y
        int pt_y_next = (j-1)*row + i;
        float y_next_u = point(0,"u",pt_y_next);
        float partial_u_y = u - y_next_u;
        
        
        float newu = u - c * dt/dx * ( partial_u_x) - c * dt/dy *( partial_u_y);
        setpointattrib(0,"u",pt,newu);
    }
}
code detail 模式

 

 过了一些帧

 

 

 

 Diffusion 方程:做完感觉是个高斯模糊。。。。

 

int row = chi("ny");
int column = chi("nx");
float c = 16;
float dx = 2.0 / float(row - 1);
float dy = 2.0 / float(column - 1);
float sigma = .05;
float dt = sigma * dx;
float viscosity = 0.05;

for(int j=1; j< row ;j++)
{
    for(int i=1; i< column ; i++)
    {
        int pt =  j * row + i;
        float u = point(0,"u",pt);
        
        
        int uxnext  = j * row + i+1;
        int uxfront = j * row + i-1;
        
        int uynext = (j+1)*row + i;
        int uyfront = (j-1)*row + i;
        
        
        // u/x 二阶偏导数
        float uxnext_val = point(0,"u",uxnext);
        float uxfront_val = point(0,"u",uxfront);
        float partial_uu_xx = uxnext_val - 2 * u + uxfront_val;
        
        // u/y 二阶偏导数
        float uynext_val = point(0,"u",uynext);
        float uyfront_val = point(0,"u",uyfront);
        float partial_uu_yy = uynext_val - 2 * u + uyfront_val;
        
 
        
        float newu = u + viscosity * dt / (dx*dx) * partial_uu_xx + viscosity*dt / (dy*dy) * partial_uu_yy ;
        
        
    
        setpointattrib(geoself(),"u",pt,newu);
    }
}
View Code

 

 

 

 第一帧:

 

过了60帧:

 

 

 

 

 

欧拉方法,K阶泰勒方法, 龙格-库塔RK2(中点midpoint), RK4

欧拉向前公式: 

 

欧拉向前步进法 精度测试,事实上可以用托马斯微积分给的 改进欧拉法(梯形法) 比现在这个要精确的多。

在[0,1]区间,看到n的步伐增加,对y的预测更加准确。

import numpy
from matplotlib import pyplot

def euler_step(t, y, h, yPrimFunction):
    """
    :param t: 当前时间t
    :param y: 当前值y
    :param h: 当前步长h
    :return: 时间上t+h的近似解
    """
    val =  y + h * yPrimFunction(t, y)
    return val

def euler(space, y0, n, yPrime):
    """
    :param space: [x 轴向上的区间]
    :param y0:  初值y0
    :param n:   步伐总值
    :return: [( t array) (y array)]
    """

    h = (space[1] - space[0]) / float(n)
    tArray = numpy.linspace(space[0], space[1], n)
    yArray = numpy.zeros(n)
    yArray[0] = y0
    
    for i in range(n-1):
        yArray[i+1] = euler_step(tArray[i], yArray[i], h, yPrime)
    return numpy.array((tArray,yArray))


func = lambda t, y : t * y + t*t*t
mapped_10 = euler([0, 1], 1, 10, func )
mapped_20 = euler([0, 1], 1, 20, func )
mapped_30 = euler([0, 1], 1, 30, func )
mapped_40 = euler([0, 1], 1, 40, func )

def plot_simple():
    pyplot.plot(mapped_10[0],mapped_10[1])  ##Plot the results
    pyplot.plot(mapped_20[0],mapped_20[1])  ##Plot the results
    pyplot.show()

def plot_complex():
    fig, ax = pyplot.subplots()
    ax.plot(mapped_10[0], mapped_10[1], label="n=10")
    ax.plot(mapped_20[0], mapped_20[1], label="n=20")
    ax.plot(mapped_30[0], mapped_30[1], label="n=30")
    ax.plot(mapped_40[0], mapped_40[1], label="n=40")
    ax.legend(loc=2)  # upper left corner
    ax.set_xlabel('t')
    ax.set_ylabel('y')
    ax.set_title("Euler Step method for: y'= t*y + t^3 ")
    pyplot.show()

plot_complex()
View Code

 

根据斜率场预测下一个值的除了欧拉法 还有几个出名的方法。

1, K阶泰勒方法 ,一阶的泰勒方法正是 欧拉方法。

    K阶的泰勒方法有个缺点,就是必须把K阶导数求出来

    围绕泰勒方法,离散方法: 求导数,ODE, PDE

    而ODE的离散方法 和 导数的离散方法,仔细观察就是从泰勒定理里 完全剥离出来的

 

2,但是RK2 RK3 RK4  完全是不用把不用知道 f'' f''' ... 的情况下把K阶的方法求出来,

     RK4 全局误差和h^4 成正比. 流体力学用到RK4 已经算出很高的精度了

 

3,还有更高级的 可变步长方法。

 

泊松方程

 

import numpy
from matplotlib import pyplot, cm
from mpl_toolkits.mplot3d import Axes3D

nx = 100
ny = 100

xmin = 0
xmax = 2

ymin = 0
ymax = 1

dx = (xmax - xmin) / (nx - 1)
dy = (ymax - ymin) / (ny - 1)

print(dx, dy)

p = numpy.zeros((ny, nx))
b = numpy.zeros((ny, nx))

x = numpy.linspace(xmin, xmax, nx)
y = numpy.linspace(ymin, ymax, ny)

# boundary condition for p
p[0, :] = 0  # p = 0 @ y = 1      最顶一行
p[ny - 1, :] = 0  # p = 0 @ y = 0     最底一行

p[:, 0] = 0  # p = 0 @ x = 0     左边一列
p[:, nx - 1] = 0  # p = 0 @ x = 2     右边一列

# print(p)
# init condition for b

b[:, int(ny / 2 - 10):int(ny / 2 + 10)] = 100
b[int(ny / 2 - 10):int(ny / 2 + 10):] = 100

# print(b)


square_dx = dx * dx
square_dy = dy * dy

nt = 200


def plot2D(x, y, p, frame):
    fig = pyplot.figure(figsize=(11, 7), dpi=100)
    ax = fig.gca(projection='3d')
    X, Y = numpy.meshgrid(x, y)
    surf = ax.plot_surface(X, Y, p[:], rstride=1, cstride=1, cmap=cm.viridis,
                           linewidth=0, antialiased=False)
    ax.view_init(30, 225)
    ax.set_xlabel('$x$')
    ax.set_ylabel('$y$')
    fig.savefig('./imgs/Laplace2d.' + ('00000' + str(frame))[-4:] + '.png')
    # pyplot.close()
    pyplot.show()


for it in range(nt):
    pre_p = p.copy()

    p[1:-1, 1:-1] = ((pre_p[1:-1, 2:] + pre_p[1:-1, 0:-2]) * dy * dy +
                     (pre_p[2:, 1:-1] + pre_p[:-2, 1:-1]) * dx * dx -
                     b[1:-1, 1:-1] * dx * dx * dy * dy) / (2 * (dx * dx + dy * dy))

    p[0, :] = 0  # p = 0 @ y = 1      最顶一行
    p[ny - 1, :] = 0  # p = 0 @ y = 0     最底一行
    p[:, 0] = 0  # p = 0 @ x = 0     左边一列
    p[:, nx - 1] = 0  # p = 0 @ x = 2     右边一列
    plot2D(x, y, p, it)
View Code

 

迭代100次的过程 

REF:

http://nbviewer.jupyter.org/github/barbagroup/CFDPython/blob/master/lessons/07_Step_5.ipynb

https://en.wikipedia.org/wiki/Euler_method

https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods

 

posted on 2018-12-01 16:11  gearslogy  阅读(1167)  评论(1编辑  收藏  举报