CFDpython - 12 steps to N-S equation
承接上文,这是step5-8,相当于课程的第二阶段,这一阶段主要是讲2维的,先老实把链接放在这:
06 Array Operations with NumPy
- 在开始前,作者介绍了numpy的数组操作,举了一个很简单的例子,切片计算:
import numpy
u = numpy.array((0, 1, 2, 3, 4, 5))
# 方法一:一个一个算相邻元素的差值
for i in range(1, len(u)):
print(u[i] - u[i-1])
# 方法二:利用切片直接计算
u[1:] - u[0:-1]
# (array([1, 2, 3, 4, 5])-array([0, 1, 2, 3, 4]))
- 其次,作者还重点强调了,使用numpy内置的数组和函数是可以加速运算的(原因在于numpy的底层是C内核,并且在数据结构和算法上做了优化)
- 这里需要介绍一下iPython的一个magic function–>
%%timeit
- 这个函数会返回jupyter一段函数的平均执行时间
- 注意嗷,这个函数是先运行,再计算,如果在这段section里画图了,会重复画一次图!
- 例子的代码如下:
#%% 网格划分:求解的是二维对流方程
nx = 81
ny = 81
nt = 100
c = 1
dx = 2 / (nx - 1)
dy = 2 / (ny - 1)
sigma = .2
dt = sigma * dx
x = numpy.linspace(0, 2, nx)
y = numpy.linspace(0, 2, ny)
u = numpy.ones((ny, nx)) ##create a 1xn vector of 1's
un = numpy.ones((ny, nx))
### 初始条件
u[int(.5 / dy): int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2
#%% 例子1:raw python
%%timeit
u = numpy.ones((ny, nx))
u[int(.5 / dy): int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2
for n in range(nt + 1): ##loop across number of time steps
un = u.copy()
row, col = u.shape
for j in range(1, row):
for i in range(1, col):
u[j, i] = (un[j, i] - (c * dt / dx *
(un[j, i] - un[j, i - 1])) -
(c * dt / dy *
(un[j, i] - un[j - 1, i])))
u[0, :] = 1
u[-1, :] = 1
u[:, 0] = 1
u[:, -1] = 1
#%% 例子2:python with numpy
%%timeit
u = numpy.ones((ny, nx))
u[int(.5 / dy): int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2
for n in range(nt + 1): ##loop across number of time steps
un = u.copy()
u[1:, 1:] = (un[1:, 1:] - (c * dt / dx * (un[1:, 1:] - un[1:, 0:-1])) -
(c * dt / dy * (un[1:, 1:] - un[0:-1, 1:])))
u[0, :] = 1
u[-1, :] = 1
u[:, 0] = 1
u[:, -1] = 1
- 以上两个例子的结果如下(我的设备是Macbook air M2,不同机子的运行时间不一样的)
- 例子1:
1.63 s ± 31.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
- 例子2:
3.32 ms ± 39.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
- 例子1:
07 Step 5: 2-D Linear Convection 2维线性扩散
071 简单的有限差分
- 这一步很简单,就是把原来的1D变成了现在的2D,方程如下:
∂ u ∂ t + c ∂ u ∂ x + c ∂ u ∂ y = 0 \frac{\partial u}{\partial t}+c\frac{\partial u}{\partial x} + c\frac{\partial u}{\partial y} = 0 ∂t∂u+c∂x∂u+c∂y∂u=0
u i , j n + 1 − u i , j n Δ t + c u i , j n − u i − 1 , j n Δ x + c u i , j n − u i , j − 1 n Δ y = 0 \frac{u_{i,j}^{n+1}-u_{i,j}^n}{\Delta t} + c\frac{u_{i, j}^n-u_{i-1,j}^n}{\Delta x} + c\frac{u_{i,j}^n-u_{i,j-1}^n}{\Delta y}=0 Δtui,jn+1−ui,jn+cΔxui,jn−ui−1,jn+cΔyui,jn−ui,j−1n=0 - 其中,i表示x方向的index,j表示y方向的index,n表示时间上的index
- 例子给的初始条件是: u ( x , y ) = { 2 for 0.5 ≤ x , y ≤ 1 1 for everywhere else u(x,y) = \begin{cases} \begin{matrix} 2\ \text{for} & 0.5 \leq x, y \leq 1 \cr 1\ \text{for} & \text{everywhere else}\end{matrix}\end{cases} u(x,y)={2 for1 for0.5≤x,y≤1everywhere else
- 边界条件: u = 1 for { x = 0 , 2 y = 0 , 2 u = 1\ \text{for } \begin{cases} \begin{matrix} x = 0,\ 2 \cr y = 0,\ 2 \end{matrix}\end{cases} u=1 for {x=0, 2y=0, 2
- 原文给了初始条件的3D图,还是很赞的3D图
- 代码如下:
from mpl_toolkits.mplot3d import Axes3D ##New Library required for projected 3d plots
import numpy
from matplotlib import pyplot, cm
%matplotlib inline
###variable declarations
nx = 81
ny = 81
nt = 100
c = 1
dx = 2 / (nx - 1)
dy = 2 / (ny - 1)
sigma = .2
dt = sigma * dx
x = numpy.linspace(0, 2, nx)
y = numpy.linspace(0, 2, ny)
u = numpy.ones((ny, nx)) ##create a 1xn vector of 1's
un = numpy.ones((ny, nx)) ##
###Assign initial conditions
##set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
u[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2
###Plot Initial Condition
##the figsize parameter can be used to produce different sized images
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, u[:], cmap=cm.viridis)
# array operation
u = numpy.ones((ny, nx))
u[int(.5 / dy):int(1 / dy + 1), int(.5 / dx):int(1 / dx + 1)] = 2
for n in range(nt + 1): ##loop across number of time steps
un = u.copy()
u[1:, 1:] = (un[1:, 1:] - (c * dt / dx * (un[1:, 1:] - un[1:, :-1])) -(c * dt / dy * (un[1:, 1:] - un[:-1, 1:])))
u[0, :] = 1
u[-1, :] = 1
u[:, 0] = 1
u[:, -1] = 1
# plot answer
fig = pyplot.figure(figsize=(11, 7), dpi=100)
ax = fig.gca(projection='3d')
surf2 = ax.plot_surface(X, Y, u[:], cmap=cm.viridis)
072-关于绘制3D图
- 3D图库代码:
from mpl_toolkits.mplot3d import Axes3D
- 创建画布,figsize用来限定尺寸,dpi表示图片精度:
fig = pyplot.figure(figsize=(11, 7), dpi=100)
- 创建轴,projection表示3d还是2d:
ax = fig.gca(projection='3d')
- 将散点展开成网格点,这是画3d图的基础,因为单看x和y都是一组列向量or行向量:
X, Y = numpy.meshgrid(x, y)
- 画图:
surf = ax.plot_surface(X, Y, u[:], cmap=cm.viridis)
- 画轴标签:
ax.set_xlabel('$x$') ax.set_ylabel('$y$');
08-Step 6: 2-D Convection 2维非线性扩散
- 这几章套路都是一样的,方程形式如下:
∂ u ∂ t + u ∂ u ∂ x + v ∂ u ∂ y = 0 \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} + v \frac{\partial u}{\partial y} = 0 ∂t∂u+u∂x∂u+v∂y∂u=0
∂
v
∂
t
+
u
∂
v
∂
x
+
v
∂
v
∂
y
=
0
\frac{\partial v}{\partial t} + u \frac{\partial v}{\partial x} + v \frac{\partial v}{\partial y} = 0
∂t∂v+u∂x∂v+v∂y∂v=0
2. 离散与上一个step一样:
u
i
,
j
n
+
1
−
u
i
,
j
n
Δ
t
+
u
i
,
j
n
u
i
,
j
n
−
u
i
−
1
,
j
n
Δ
x
+
v
i
,
j
n
u
i
,
j
n
−
u
i
,
j
−
1
n
Δ
y
=
0
\frac{u_{i,j}^{n+1}-u_{i,j}^n}{\Delta t} + u_{i,j}^n \frac{u_{i,j}^n-u_{i-1,j}^n}{\Delta x} + v_{i,j}^n \frac{u_{i,j}^n-u_{i,j-1}^n}{\Delta y} = 0
Δtui,jn+1−ui,jn+ui,jnΔxui,jn−ui−1,jn+vi,jnΔyui,jn−ui,j−1n=0
v
i
,
j
n
+
1
−
v
i
,
j
n
Δ
t
+
u
i
,
j
n
v
i
,
j
n
−
v
i
−
1
,
j
n
Δ
x
+
v
i
,
j
n
v
i
,
j
n
−
v
i
,
j
−
1
n
Δ
y
=
0
\frac{v_{i,j}^{n+1}-v_{i,j}^n}{\Delta t} + u_{i,j}^n \frac{v_{i,j}^n-v_{i-1,j}^n}{\Delta x} + v_{i,j}^n \frac{v_{i,j}^n-v_{i,j-1}^n}{\Delta y} = 0
Δtvi,jn+1−vi,jn+ui,jnΔxvi,jn−vi−1,jn+vi,jnΔyvi,jn−vi,j−1n=0
3. 最终代码形式:
u
i
,
j
n
+
1
=
u
i
,
j
n
−
u
i
,
j
Δ
t
Δ
x
(
u
i
,
j
n
−
u
i
−
1
,
j
n
)
−
v
i
,
j
n
Δ
t
Δ
y
(
u
i
,
j
n
−
u
i
,
j
−
1
n
)
u_{i,j}^{n+1} = u_{i,j}^n - u_{i,j} \frac{\Delta t}{\Delta x} (u_{i,j}^n-u_{i-1,j}^n) - v_{i,j}^n \frac{\Delta t}{\Delta y} (u_{i,j}^n-u_{i,j-1}^n)
ui,jn+1=ui,jn−ui,jΔxΔt(ui,jn−ui−1,jn)−vi,jnΔyΔt(ui,jn−ui,j−1n)
v
i
,
j
n
+
1
=
v
i
,
j
n
−
u
i
,
j
Δ
t
Δ
x
(
v
i
,
j
n
−
v
i
−
1
,
j
n
)
−
v
i
,
j
n
Δ
t
Δ
y
(
v
i
,
j
n
−
v
i
,
j
−
1
n
)
v_{i,j}^{n+1} = v_{i,j}^n - u_{i,j} \frac{\Delta t}{\Delta x} (v_{i,j}^n-v_{i-1,j}^n) - v_{i,j}^n \frac{\Delta t}{\Delta y} (v_{i,j}^n-v_{i,j-1}^n)
vi,jn+1=vi,jn−ui,jΔxΔt(vi,jn−vi−1,jn)−vi,jnΔyΔt(vi,jn−vi,j−1n)
4. 初始条件和上一个step一样:
u
,
v
=
{
2
for
x
,
y
∈
(
0.5
,
1
)
×
(
0.5
,
1
)
1
everywhere else
u,\ v\ = \begin{cases}\begin{matrix} 2 & \text{for } x,y \in (0.5, 1)\times(0.5,1) \cr 1 & \text{everywhere else} \end{matrix}\end{cases}
u, v ={21for x,y∈(0.5,1)×(0.5,1)everywhere else
5. 边界条件和上一个step一样:
u
=
1
,
v
=
1
for
{
x
=
0
,
2
y
=
0
,
2
u = 1,\ v = 1 \text{ for } \begin{cases} \begin{matrix}x=0,2\cr y=0,2 \end{matrix}\end{cases}
u=1, v=1 for {x=0,2y=0,2
6. 这里和上一届一样,就改了改这里
for n in range(nt + 1): ##loop across number of time steps
un = u.copy()
vn = v.copy()
u[1:, 1:] = (un[1:, 1:] - (un[1:, 1:] * c * dt / dx * (un[1:, 1:] - un[1:, :-1])) -vn[1:, 1:] * c * dt / dy * (un[1:, 1:] - un[:-1, 1:]))
v[1:, 1:] = (vn[1:, 1:] - (un[1:, 1:] * c * dt / dx * (vn[1:, 1:] - vn[1:, :-1])) -vn[1:, 1:] * c * dt / dy * (vn[1:, 1:] - vn[:-1, 1:]))
u[0, :] = 1
u[-1, :] = 1
u[:, 0] = 1
u[:, -1] = 1
v[0, :] = 1
v[-1, :] = 1
v[:, 0] = 1
v[:, -1] = 1
09-Step 7: 2D Diffusion
- 这个也和一维扩散差不多,就是加了一维罢了,方程形式如下:
∂ u ∂ t = ν ∂ 2 u ∂ x 2 + ν ∂ 2 u ∂ y 2 \frac{\partial u}{\partial t} = \nu \frac{\partial ^2 u}{\partial x^2} + \nu \frac{\partial ^2 u}{\partial y^2} ∂t∂u=ν∂x2∂2u+ν∂y2∂2u - 离散后:
u i , j n + 1 − u i , j n Δ t = ν u i + 1 , j n − 2 u i , j n + u i − 1 , j n Δ x 2 + ν u i , j + 1 n − 2 u i , j n + u i , j − 1 n Δ y 2 \frac{u_{i,j}^{n+1} - u_{i,j}^n}{\Delta t} = \nu \frac{u_{i+1,j}^n - 2 u_{i,j}^n + u_{i-1,j}^n}{\Delta x^2} + \nu \frac{u_{i,j+1}^n-2 u_{i,j}^n + u_{i,j-1}^n}{\Delta y^2} Δtui,jn+1−ui,jn=νΔx2ui+1,jn−2ui,jn+ui−1,jn+νΔy2ui,j+1n−2ui,jn+ui,j−1n
u i , j n + 1 = u i , j n + ν Δ t Δ x 2 ( u i + 1 , j n − 2 u i , j n + u i − 1 , j n ) + ν Δ t Δ y 2 ( u i , j + 1 n − 2 u i , j n + u i , j − 1 n ) \begin{split} u_{i,j}^{n+1} = u_{i,j}^n &+ \frac{\nu \Delta t}{\Delta x^2}(u_{i+1,j}^n - 2 u_{i,j}^n + u_{i-1,j}^n) + \frac{\nu \Delta t}{\Delta y^2}(u_{i,j+1}^n-2 u_{i,j}^n + u_{i,j-1}^n) \end{split} ui,jn+1=ui,jn+Δx2νΔt(ui+1,jn−2ui,jn+ui−1,jn)+Δy2νΔt(ui,j+1n−2ui,jn+ui,j−1n) - 代码如下:
###Run through nt timesteps
def diffuse(nt):
u[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2
for n in range(nt + 1):
un = u.copy()
u[1:-1, 1:-1] = (un[1:-1,1:-1] + nu * dt / dx**2 * (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) + nu * dt / dy**2 *(un[2:,1: -1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1]))
u[0, :] = 1
u[-1, :] = 1
u[:, 0] = 1
u[:, -1] = 1
fig = pyplot.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, u[:], rstride=1, cstride=1, cmap=cm.viridis,
linewidth=0, antialiased=True)
ax.set_zlim(1, 2.5)
ax.set_xlabel('$x$')
ax.set_ylabel('$y$');
10-Step 8: Burgers’ Equation in 2D
- (如果你的想法和我一样,我觉得应该算入门了)感觉step5-8都差不多,就是先对方程进行离散,然后绘制网格+初始条件边界条件,最后用
numpy array operation
递推 - 作者说,第八步的Burgers方程是一个里程碑,但是对于Burgers的解并不是光滑的,而是离散的,比如“激波”
- 同理,原方程形式:
∂ u ∂ t + u ∂ u ∂ x + v ∂ u ∂ y = ν ( ∂ 2 u ∂ x 2 + ∂ 2 u ∂ y 2 ) \frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} + v \frac{\partial u}{\partial y} = \nu \; \left(\frac{\partial ^2 u}{\partial x^2} + \frac{\partial ^2 u}{\partial y^2}\right) ∂t∂u+u∂x∂u+v∂y∂u=ν(∂x2∂2u+∂y2∂2u)
∂
v
∂
t
+
u
∂
v
∂
x
+
v
∂
v
∂
y
=
ν
(
∂
2
v
∂
x
2
+
∂
2
v
∂
y
2
)
\frac{\partial v}{\partial t} + u \frac{\partial v}{\partial x} + v \frac{\partial v}{\partial y} = \nu \; \left(\frac{\partial ^2 v}{\partial x^2} + \frac{\partial ^2 v}{\partial y^2}\right)
∂t∂v+u∂x∂v+v∂y∂v=ν(∂x2∂2v+∂y2∂2v)
4. 一样地离散:
u
i
,
j
n
+
1
−
u
i
,
j
n
Δ
t
+
u
i
,
j
n
u
i
,
j
n
−
u
i
−
1
,
j
n
Δ
x
+
v
i
,
j
n
u
i
,
j
n
−
u
i
,
j
−
1
n
Δ
y
=
ν
(
u
i
+
1
,
j
n
−
2
u
i
,
j
n
+
u
i
−
1
,
j
n
Δ
x
2
+
u
i
,
j
+
1
n
−
2
u
i
,
j
n
+
u
i
,
j
−
1
n
Δ
y
2
)
\begin{split} & \frac{u_{i,j}^{n+1} - u_{i,j}^n}{\Delta t} + u_{i,j}^n \frac{u_{i,j}^n-u_{i-1,j}^n}{\Delta x} + v_{i,j}^n \frac{u_{i,j}^n - u_{i,j-1}^n}{\Delta y} = \nu \left( \frac{u_{i+1,j}^n - 2u_{i,j}^n+u_{i-1,j}^n}{\Delta x^2} + \frac{u_{i,j+1}^n - 2u_{i,j}^n + u_{i,j-1}^n}{\Delta y^2} \right) \end{split}
Δtui,jn+1−ui,jn+ui,jnΔxui,jn−ui−1,jn+vi,jnΔyui,jn−ui,j−1n=ν(Δx2ui+1,jn−2ui,jn+ui−1,jn+Δy2ui,j+1n−2ui,jn+ui,j−1n)
v
i
,
j
n
+
1
−
v
i
,
j
n
Δ
t
+
u
i
,
j
n
v
i
,
j
n
−
v
i
−
1
,
j
n
Δ
x
+
v
i
,
j
n
v
i
,
j
n
−
v
i
,
j
−
1
n
Δ
y
=
ν
(
v
i
+
1
,
j
n
−
2
v
i
,
j
n
+
v
i
−
1
,
j
n
Δ
x
2
+
v
i
,
j
+
1
n
−
2
v
i
,
j
n
+
v
i
,
j
−
1
n
Δ
y
2
)
\begin{split} & \frac{v_{i,j}^{n+1} - v_{i,j}^n}{\Delta t} + u_{i,j}^n \frac{v_{i,j}^n-v_{i-1,j}^n}{\Delta x} + v_{i,j}^n \frac{v_{i,j}^n - v_{i,j-1}^n}{\Delta y} = \nu \left( \frac{v_{i+1,j}^n - 2v_{i,j}^n+v_{i-1,j}^n}{\Delta x^2} + \frac{v_{i,j+1}^n - 2v_{i,j}^n + v_{i,j-1}^n}{\Delta y^2} \right) \end{split}
Δtvi,jn+1−vi,jn+ui,jnΔxvi,jn−vi−1,jn+vi,jnΔyvi,jn−vi,j−1n=ν(Δx2vi+1,jn−2vi,jn+vi−1,jn+Δy2vi,j+1n−2vi,jn+vi,j−1n)
5. 代码如下:
import numpy
from matplotlib import pyplot, cm
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline
###variable declarations
nx = 41
ny = 41
nt = 120
c = 1
dx = 2 / (nx - 1)
dy = 2 / (ny - 1)
sigma = .0009
nu = 0.01
dt = sigma * dx * dy / nu
x = numpy.linspace(0, 2, nx)
y = numpy.linspace(0, 2, ny)
u = numpy.ones((ny, nx)) # create a 1xn vector of 1's
v = numpy.ones((ny, nx))
un = numpy.ones((ny, nx))
vn = numpy.ones((ny, nx))
comb = numpy.ones((ny, nx))
###Assign initial conditions
##set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
u[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2
##set hat function I.C. : u(.5<=x<=1 && .5<=y<=1 ) is 2
v[int(.5 / dy):int(1 / dy + 1),int(.5 / dx):int(1 / dx + 1)] = 2
###(plot ICs)
fig = pyplot.figure(figsize=(11, 7), dpi=100)
ax = fig.gca(projection='3d')
X, Y = numpy.meshgrid(x, y)
ax.plot_surface(X, Y, u[:], cmap=cm.viridis, rstride=1, cstride=1)
ax.plot_surface(X, Y, v[:], cmap=cm.viridis, rstride=1, cstride=1)
ax.set_xlabel('$x$')
ax.set_ylabel('$y$');
for n in range(nt + 1): ##loop across number of time steps
un = u.copy()
vn = v.copy()
u[1:-1, 1:-1] = (un[1:-1, 1:-1] -
dt / dx * un[1:-1, 1:-1] *
(un[1:-1, 1:-1] - un[1:-1, 0:-2]) -
dt / dy * vn[1:-1, 1:-1] *
(un[1:-1, 1:-1] - un[0:-2, 1:-1]) +
nu * dt / dx**2 *
(un[1:-1,2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +
nu * dt / dy**2 *
(un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1]))
v[1:-1, 1:-1] = (vn[1:-1, 1:-1] -
dt / dx * un[1:-1, 1:-1] *
(vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -
dt / dy * vn[1:-1, 1:-1] *
(vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) +
nu * dt / dx**2 *
(vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +
nu * dt / dy**2 *
(vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1]))
u[0, :] = 1
u[-1, :] = 1
u[:, 0] = 1
u[:, -1] = 1
v[0, :] = 1
v[-1, :] = 1
v[:, 0] = 1
v[:, -1] = 1
fig = pyplot.figure(figsize=(11, 7), dpi=100)
ax = fig.gca(projection='3d')
X, Y = numpy.meshgrid(x, y)
ax.plot_surface(X, Y, u, cmap=cm.viridis, rstride=1, cstride=1)
ax.plot_surface(X, Y, v, cmap=cm.viridis, rstride=1, cstride=1)
ax.set_xlabel('$x$')
ax.set_ylabel('$y$');