以下内容均可参考本人知乎文章添加链接描述和添加链接描述
有限差分法
有限差分法finite difference(FD)是求解微分方程的最为容易理解的方法,下面将针对几类常见的PDE来做一些具体的介绍。由于本人知识有限,关于误差分析和收敛性证明都不会介绍.
一维例子
我们以一个一维PDE的求解来介绍有限差分算法,这里给出精确解
u
^
=
x
(
1
−
x
)
exp
(
x
)
\hat{u} = x(1-x)\exp(x)
u^=x(1−x)exp(x).
{ − u x x = f , x ∈ ( 0 , 1 ) , f = exp ( x ) ( − x 2 − x + 1 ) , u ( 0 ) = 0 , u ( 1 ) = 0. \left\{\begin{array}{l} -u_{xx} = f, x \in (0,1),\\ f = \exp(x)(-x^2 - x + 1),\\ u(0) = 0,u(1) = 0.\\ \end{array}\right. ⎩ ⎨ ⎧−uxx=f,x∈(0,1),f=exp(x)(−x2−x+1),u(0)=0,u(1)=0.
对区域 [ 0 , 1 ] [0,1] [0,1]剖分,均匀分成 N N N份,即 x 0 = 0 < x i < x N = 1 x_0 = 0 < x_i < x_N = 1 x0=0<xi<xN=1,其中 h x = 1 N h_x = \frac{1}{N} hx=N1,现在要求 u i = u ( x i ) , 1 ≤ i ≤ N − 1 u_i=u(x_i),1 \leq i \leq N - 1 ui=u(xi),1≤i≤N−1的取值.利用 u i + 1 − 2 u i + u i − 1 h x 2 , 1 ≤ i ≤ N − 1 \frac{u_{i+1} - 2u_i + u_{i-1}}{h_x^2},1 \leq i \leq N - 1 hx2ui+1−2ui+ui−1,1≤i≤N−1可以近似代替 u x x ( x i ) u_{xx}(x_i) uxx(xi),那么得到一个线性系统:
{ − ( u i + 1 − 2 u i + u i − 1 ) = f i h x 2 , 1 ≤ i ≤ N − 1 , u 0 = 0 , u N = 0 , A u = b . \left\{\begin{array}{l} -(u_{i+1} - 2u_i + u_{i-1}) = f_i h_x^2,1 \leq i \leq N - 1,\\ u_0 = 0,u_N = 0,\\ Au = b. \end{array}\right. ⎩ ⎨ ⎧−(ui+1−2ui+ui−1)=fihx2,1≤i≤N−1,u0=0,uN=0,Au=b.
这里的 b , u b,u b,u是一个 N + 1 N + 1 N+1维向量,其中 b 0 = 0 , b N = 0 b_0 = 0,b_N=0 b0=0,bN=0,其他部分 b i = f i h x 2 , 1 ≤ i ≤ N − 1 b_i = f_i h_x^2,1 \leq i \leq N - 1 bi=fihx2,1≤i≤N−1,这里的A是一个 ( N + 1 ) × ( N + 1 ) (N + 1)\times (N + 1) (N+1)×(N+1)的带状矩阵,满足 A 0 , 0 = A N , N = 1 A_{0,0}=A_{N,N} = 1 A0,0=AN,N=1,其余对角线元素 A i , i = 2 A_{i,i}=2 Ai,i=2,就是下面这个样子.
A = ( 1 0 0 ⋯ 0 − 1 2 − 1 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 ⋯ − 1 2 − 1 0 0 ⋯ 0 1 ) A=\left(\begin{array}{ccccc} 1 & 0 & 0 & \cdots & 0 \\ -1 & 2 & -1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & \cdots & -1 & 2 & -1 \\ 0 & 0 & \cdots & 0 & 1 \end{array}\right) A= 1−1⋮0002⋮⋯00−1⋱−1⋯⋯⋯⋮2000−11
FD求解泊松方程
Dirichlet边界条件
给定泊松方程如下,其中
Ω
=
[
0
,
1
]
2
,
x
=
(
x
,
y
)
\Omega = [0,1]^2,\mathbf{x} = (x,y)
Ω=[0,1]2,x=(x,y):
{ − Δ u = f , x ∈ Ω , u = u 0 , x ∈ ∂ Ω . \left\{\begin{array}{l} -\Delta u = f,\mathbf{x} \in \Omega,\\ u = u_0,\mathbf{x} \in \partial \Omega .\\ \end{array}\right. {−Δu=f,x∈Ω,u=u0,x∈∂Ω.
引入剖分
M
,
N
,
h
x
=
1
M
,
h
y
=
1
N
M,N,h_x = \frac{1}{M},h_y = \frac{1}{N}
M,N,hx=M1,hy=N1,将区域离散化以后得到网格点
(
x
i
,
y
j
)
,
0
≤
i
≤
M
,
0
≤
j
≤
N
(x_i,y_j),0 \leq i \leq M,0 \leq j \leq N
(xi,yj),0≤i≤M,0≤j≤N,记
u
i
,
j
=
u
(
x
i
,
y
j
)
u_{i,j}=u(x_i,y_j)
ui,j=u(xi,yj),利用中心差分公式\ref{center}得到离散以后的系统,网格剖分如下图所示
{ u x x ( x i , y j ) = u i + 1 , j − 2 u i , j + u i − 1 , j h x 2 + O ( h x 2 ) , 1 ≤ i ≤ M − 1 , u y y ( x i , y j ) = u i , j + 1 − 2 u i , j + u i , j − 1 h y 2 + O ( h y 2 ) , 1 ≤ j ≤ N − 1. \left\{\begin{array}{l} u_{xx}(x_i,y_j) = \frac{u_{i + 1,j} - 2u_{i,j} + u_{i - 1,j}}{h_x^2} + O(h_x^2),1 \leq i \leq M - 1,\\ u_{yy}(x_i,y_j) = \frac{u_{i,j + 1} - 2u_{i,j} + u_{i,j-1}}{h_y^2} + O(h_y^2) , 1 \leq j \leq N - 1.\\ \end{array}\right. {uxx(xi,yj)=hx2ui+1,j−2ui,j+ui−1,j+O(hx2),1≤i≤M−1,uyy(xi,yj)=hy2ui,j+1−2ui,j+ui,j−1+O(hy2),1≤j≤N−1.
{ − u i + 1 , j − 2 u i , j + u i − 1 , j h x 2 − u i , j + 1 − 2 u i , j + u i , j − 1 h y 2 = f i , j , 1 ≤ i ≤ M − 1 , 1 ≤ j ≤ N − 1 , u i , j = u 0 ( x i , y j ) , ( x i , y j ) ∈ ∂ Ω . \left\{\begin{array}{l} -\frac{u_{i + 1,j} - 2u_{i,j} + u_{i - 1,j}}{h_x^2} - \frac{u_{i,j + 1} - 2u_{i,j} + u_{i,j-1}}{h_y^2} = f_{i,j} ,1 \leq i \leq M - 1,1 \leq j \leq N - 1,\\ u_{i,j} = u_0(x_i,y_j),(x_i,y_j) \in \partial \Omega.\\ \end{array}\right. {−hx2ui+1,j−2ui,j+ui−1,j−hy2ui,j+1−2ui,j+ui,j−1=fi,j,1≤i≤M−1,1≤j≤N−1,ui,j=u0(xi,yj),(xi,yj)∈∂Ω.
通过上面的公式可以形成一个线性方程组
A
x
=
b
,
A
∈
R
(
M
+
1
)
(
N
+
1
)
×
(
M
+
1
)
(
N
+
1
)
Ax = b,A \in R^{(M + 1)(N + 1)\times (M + 1)(N + 1)}
Ax=b,A∈R(M+1)(N+1)×(M+1)(N+1),不过由于在实际操作过程中
M
,
N
M,N
M,N很大,会导致
h
x
2
,
h
y
2
h_x^2,h_y^2
hx2,hy2太大,为了避免矩阵
A
A
A病态,我们可以在\ref{FDmat1}第一个公式两边同时乘一个
h
x
h
y
h_xh_y
hxhy,经过整理以后得到下面这个公式
{
−
h
y
h
x
u
i
−
1
,
j
+
(
2
h
x
h
y
+
2
h
x
h
y
)
u
i
,
j
−
h
y
h
x
u
i
+
1
,
j
−
h
x
h
y
u
i
,
j
−
1
−
h
x
h
y
u
i
,
j
+
1
=
f
i
,
j
h
x
h
y
,
u
i
,
j
=
u
0
(
x
i
,
y
j
)
,
(
x
i
,
y
j
)
∈
∂
Ω
.
\left\{\begin{array}{l} -\frac{h_y}{h_x}u_{i - 1,j} + (2\frac{h_x}{h_y} + 2\frac{h_x}{h_y})u_{i,j} - \frac{h_y}{h_x}u_{i + 1,j} - \frac{h_x}{h_y}u_{i,j - 1} - \frac{h_x}{h_y}u_{i,j + 1} = f_{i,j}h_x h_y,\\ u_{i,j} = u_0(x_i,y_j),(x_i,y_j) \in \partial \Omega.\\ \end{array}\right.
{−hxhyui−1,j+(2hyhx+2hyhx)ui,j−hxhyui+1,j−hyhxui,j−1−hyhxui,j+1=fi,jhxhy,ui,j=u0(xi,yj),(xi,yj)∈∂Ω.
有了上述公式以后,下面我们开始在python中实现
A
,
b
A,b
A,b的构造,这里说明
x
=
(
u
0
,
0
,
u
0
,
1
,
…
,
u
0
,
N
,
…
,
u
M
,
0
,
u
M
,
1
,
…
,
u
M
,
N
)
x = (u_{0,0},u_{0,1},\ldots,u_{0,N},\ldots,u_{M,0},u_{M,1},\ldots,u_{M,N})
x=(u0,0,u0,1,…,u0,N,…,uM,0,uM,1,…,uM,N).
图片展示了五点差分法构造矩阵的全过程,
u
u
u在边界点的取值已知,所以当循环遇到边界点的索引,此时矩阵的对角元素设定为1.事实上,如果想缩小矩阵的规模,可以把已知量挪到右边,只针对位于网格点内部的函数值
u
i
,
j
u_{i,j}
ui,j求解,形成一个
(
M
−
1
)
(
N
−
1
)
×
(
M
−
1
)
(
N
−
1
)
(M - 1)(N-1)\times(M - 1)(N - 1)
(M−1)(N−1)×(M−1)(N−1)的矩阵,读者可以自己尝试.引入精确解
u
=
log
(
10
(
x
+
y
)
2
+
(
x
−
y
)
2
+
0.5
)
u = \log(10(x + y)^2 + (x - y)^2 + 0.5)
u=log(10(x+y)2+(x−y)2+0.5),然后形成对应的右端项和边界条件测试的结果如下所示:
代码参考添加链接描述
Neumann边界条件
给定泊松方程如下,其中
Ω
=
[
0
,
1
]
2
,
x
=
(
x
,
y
)
\Omega = [0,1]^2,\mathbf{x} = (x,y)
Ω=[0,1]2,x=(x,y):
{ − Δ u + u = f , x ∈ Ω , ∂ u ∂ n = u 0 , x ∈ ∂ Ω . \left\{\begin{array}{l} -\Delta u + u= f,\mathbf{x} \in \Omega,\\ \frac{\partial u}{\partial n} = u_0,\mathbf{x} \in \partial \Omega .\\ \end{array}\right. {−Δu+u=f,x∈Ω,∂n∂u=u0,x∈∂Ω.
针对Neumann边界条件,泊松方程的设定和上面的Dirichlet\ref{poisson}不太一样,在控制方程中多了一项关于 u u u的线性项,这个设定可以保证该方程有唯一解.如果没有这一项,假设 u ∗ u^* u∗是精确解,那么会发现 u ∗ + c u^* + c u∗+c也是精确解,其中 c c c是任意常数.在控制方程的处理和上面Dirichlet几乎一样,离散化以后会得到下面这个公式:
{ − h y h x u i − 1 , j + ( 2 h x h y + 2 h x h y + h x h y ) u i , j − h y h x u i + 1 , j − h x h y u i , j − 1 − h x h y u i , j + 1 = f i , j h x h y , ∂ u ∂ n ( x i , y j ) = u 0 ( x i , y j ) , ( x i , y j ) ∈ ∂ Ω . \left\{\begin{array}{l} -\frac{h_y}{h_x}u_{i - 1,j} + (2\frac{h_x}{h_y} + 2\frac{h_x}{h_y} + h_x h_y)u_{i,j} - \frac{h_y}{h_x}u_{i + 1,j} - \frac{h_x}{h_y}u_{i,j - 1} - \frac{h_x}{h_y}u_{i,j + 1} = f_{i,j}h_x h_y,\\ \frac{\partial u}{\partial n}(x_i,y_j) = u_0(x_i,y_j),(x_i,y_j) \in \partial \Omega.\\ \end{array}\right. {−hxhyui−1,j+(2hyhx+2hyhx+hxhy)ui,j−hxhyui+1,j−hyhxui,j−1−hyhxui,j+1=fi,jhxhy,∂n∂u(xi,yj)=u0(xi,yj),(xi,yj)∈∂Ω.
重点是在边界上的处理,方向导数放在四个边界上是有两个 u x , u y u_x,u_y ux,uy,因此这里需要逼近 u x , u y u_x,u_y ux,uy,可以使用一阶差分或者二阶差分和虚拟点来逼近,下面重点介绍虚拟点方法.
这里我们选取边界点 ( x i , y 0 ) , 1 ≤ i ≤ M − 1 (x_i,y_0),1 \leq i \leq M - 1 (xi,y0),1≤i≤M−1举例子,注意在4个角需要做特殊处理.则会有
∂ u ∂ n ( x i , y 0 ) = u i , 1 − u i , − 1 2 h y = u 0 ( x i , y 0 ) , 1 ≤ i ≤ M − 1. \frac{\partial u}{\partial n}(x_i,y_0) = \frac{u_{i,1} - u_{i,-1}}{2h_y} = u_0(x_i,y_0),1 \leq i \leq M - 1. ∂n∂u(xi,y0)=2hyui,1−ui,−1=u0(xi,y0),1≤i≤M−1.
然后代入方程中替换
u
i
,
−
1
=
u
i
,
1
−
2
u
0
(
x
i
,
y
0
)
h
y
u_{i,-1} = u_{i,1} - 2u_0(x_i,y_0)h_y
ui,−1=ui,1−2u0(xi,y0)hy即可,对应需要修改右端项
b
b
b的定义.代码参考添加链接描述.
FD求解含时间项的泊松方程
引入方程定义:
{ u ( x , t ) − Δ u ( x , t ) = f ( x , t ) , ( x , t ) ∈ Ω × [ 0 , 1 ] , u ( x , t ) = g ( x , t ) , ( x , t ) ∈ ∂ Ω × [ 0 , 1 ] , u ( x , 0 ) = h ( x ) , x ∈ Ω . \left\{\begin{array}{l} u(\mathbf{x},t)-\Delta u(\mathbf{x},t) = f(\mathbf{x},t),(\mathbf{x},t) \in \Omega \times [0,1],\\ u(\mathbf{x},t) = g(\mathbf{x},t),(\mathbf{x},t) \in \partial \Omega \times [0,1],\\ u(\mathbf{x},0) = h(\mathbf{x}),\mathbf{x} \in \Omega. \end{array}\right. ⎩ ⎨ ⎧u(x,t)−Δu(x,t)=f(x,t),(x,t)∈Ω×[0,1],u(x,t)=g(x,t),(x,t)∈∂Ω×[0,1],u(x,0)=h(x),x∈Ω.
针对方程\ref{tpoisson}引入4种差分格式如下,为了方便记忆,定义:\
δ
x
u
i
,
j
n
=
u
i
+
1
,
j
n
−
2
u
i
,
j
n
+
u
i
−
1
,
j
n
h
x
2
\delta_x u_{i,j}^n = \frac{u_{i + 1,j}^n - 2u_{i,j}^n + u_{i - 1,j}^n}{h_x^2}
δxui,jn=hx2ui+1,jn−2ui,jn+ui−1,jn,
δ
y
u
i
,
j
n
=
u
i
,
j
+
1
n
−
2
u
i
,
j
n
+
u
i
,
j
−
1
n
h
y
2
\delta_y u_{i,j}^n = \frac{u_{i,j + 1}^n - 2u_{i,j}^n + u_{i,j - 1}^n}{h_y^2}
δyui,jn=hy2ui,j+1n−2ui,jn+ui,j−1n \
Forward-Euler:
u i , j n + 1 − u i , j n τ − δ x u i , j n − δ y u i , j n = f i , j n + 1 . \frac{u_{i,j}^{n + 1} - u_{i,j}^{n}}{\tau} - \delta_x u_{i,j}^n - \delta_y u_{i,j}^n = f_{i,j}^{n + 1}. τui,jn+1−ui,jn−δxui,jn−δyui,jn=fi,jn+1.
Backward-Euler:
u i , j n + 1 − u i , j n τ − δ x u i , j n + 1 − δ y u i , j n + 1 = f i , j n + 1 . \frac{u_{i,j}^{n + 1} - u_{i,j}^{n}}{\tau} - \delta_x u_{i,j}^{n + 1} - \delta_y u_{i,j}^{n + 1} = f_{i,j}^{n + 1}. τui,jn+1−ui,jn−δxui,jn+1−δyui,jn+1=fi,jn+1.
BDF:
3 u i , j n + 1 − 4 u i , j n + u i , j n − 1 2 τ − δ x u i , j n + 1 − δ y u i , j n + 1 = f i , j n + 1 . \frac{3u_{i,j}^{n + 1} - 4u_{i,j}^{n} + u_{i,j}^{n - 1}}{2\tau} - \delta_x u_{i,j}^{n + 1} - \delta_y u_{i,j}^{n + 1} = f_{i,j}^{n + 1}. 2τ3ui,jn+1−4ui,jn+ui,jn−1−δxui,jn+1−δyui,jn+1=fi,jn+1.
CN:
u i , j n + 1 − u i , j n τ − 1 2 ( δ x u i , j n + 1 + δ y u i , j n + 1 + δ x u i , j n + δ y u i , j n ) = f i , j n + 1 . \frac{u_{i,j}^{n + 1} - u_{i,j}^{n}}{\tau} - \frac{1}{2}(\delta_x u_{i,j}^{n + 1} + \delta_y u_{i,j}^{n + 1} + \delta_x u_{i,j}^{n} + \delta_y u_{i,j}^{n}) = f_{i,j}^{n + 1}. τui,jn+1−ui,jn−21(δxui,jn+1+δyui,jn+1+δxui,jn+δyui,jn)=fi,jn+1.
有了这些格式以后,我们引入
U
n
=
(
u
0
,
0
n
,
…
,
u
0
,
N
n
,
…
,
u
M
,
0
n
,
…
,
u
M
,
N
n
)
U^n = (u_{0,0}^{n},\ldots,u_{0,N}^{n},\ldots,u_{M,0}^{n},\ldots,u_{M,N}^{n})
Un=(u0,0n,…,u0,Nn,…,uM,0n,…,uM,Nn),则上述4个格式可以理解为4个迭代的线性系统.
Forward-Euler:
U n + 1 = A U n + b . U^{n + 1} = A U^{n} + b. Un+1=AUn+b.
Backward-Euler:
A U n + 1 = B U n + b . AU^{n + 1} = B U^{n} + b. AUn+1=BUn+b.
BDF:
A U n + 1 = B U n + C U n − 1 + b . AU^{n + 1} = B U^{n} + C U^{n - 1} + b. AUn+1=BUn+CUn−1+b.
CN:
A U n + 1 = B U n + b . AU^{n + 1} = B U^{n} + b. AUn+1=BUn+b.
在代码的实现过程中,Forward-Euler不需要求解线性系统,但是这里需要对时间步长
τ
\tau
τ和空间步长
h
x
,
h
y
h_x,h_y
hx,hy做一个限制来满足稳定性条件,另外三个格式对于任意的时间空间步长都是稳定的,而上面提到的4个格式里面的矩阵需要自己根据离散格式来确定.这里仅仅针对BDF格式做一个具体的说明,其他的类似.
\subsubsection{BDF格式}
对于时间层,我们每隔
τ
\tau
τ时间做了一次计算,
U
n
U^n
Un表示当时间层到了第n层的时候那一层的解.将BDF格式整理如下:
( 3 + 4 τ ( 1 h x 2 + 1 h y 2 ) ) u i , j n + 1 − 2 τ h y 2 ( u i , j + 1 n + u i , j − 1 n ) − 2 τ h x 2 ( u i + 1 , j n + u i − 1 , j n ) = 4 u i , j n − u i , j n − 1 + 2 f i , j n + 1 τ . \begin{aligned} &(3 + 4\tau(\frac{1}{h_x^2} + \frac{1}{h_y^2}))u_{i,j}^{n+1} - 2\frac{\tau}{h_y^2}(u_{i,j + 1}^n + u_{i,j - 1}^n) - 2\frac{\tau}{h_x^2}(u_{i + 1,j}^n + u_{i - 1,j}^n) \\ &= 4u_{i,j}^n - u_{i,j}^{n-1} + 2f_{i,j}^{n+1}\tau. \end{aligned} (3+4τ(hx21+hy21))ui,jn+1−2hy2τ(ui,j+1n+ui,j−1n)−2hx2τ(ui+1,jn+ui−1,jn)=4ui,jn−ui,jn−1+2fi,jn+1τ.
具体代码参考下面图片
在BDF代码实现中,计算
U
2
U^2
U2的时候需要
U
1
,
U
0
U^1,U^0
U1,U0,其中
U
0
U^0
U0可以直接通过初始条件获得,
U
1
U^1
U1可以通过CN格式或者Backward-Euler格式获得.
这里我们取
u
=
(
x
2
+
y
2
)
exp
(
t
−
t
2
)
u=(x^2 + y^2)\exp(t-t^2)
u=(x2+y2)exp(t−t2)来做算例测试BDF格式,这里只计算最后一层
u
(
x
,
y
,
1
)
u(x,y,1)
u(x,y,1)的误差.
区域分解算法
上面的FD把整个区域进行剖分,当剖分很细的时候,得到的线性系统维度很大,为了降低存储成本和计算成本,可以将区域划分为若干区域,在每个区域上求解,具体过程将以数学公式来说明.这种算法也称之为cauchy-schwarz算法.
首先需要把区域进行划分,先假设划分为两个区域 Ω = Ω 0 ⋃ Ω 1 \Omega=\Omega_0 \bigcup \Omega_1 Ω=Ω0⋃Ω1,其中 Ω 0 = [ 0 , 0.5 ] × [ 0 , 1 ] , ω 1 = [ 0.4 , 1 ] × [ 0 , 1 ] \Omega_0=[0,0.5]\times[0,1],\omega_1=[0.4,1]\times[0,1] Ω0=[0,0.5]×[0,1],ω1=[0.4,1]×[0,1],注意两个区域必须要有重叠overlap.做剖分的时候注意,为了避免麻烦,应该让边界线 { 0.4 } × [ 0 , 1 ] , { 0.5 } × [ 0 , 1 ] \{0.4\}\times [0,1],\{0.5\}\times [0,1] {0.4}×[0,1],{0.5}×[0,1]都位于网格线上,针对泊松方程引入两个子问题.
{ − Δ u 1 n + 1 = f , x ∈ Ω 0 , u 1 n + 1 = u 0 , x ∈ ∂ Ω 0 ∩ ∂ Ω , u 1 n + 1 = u 2 n , x ∈ ∂ Ω 0 ∖ ∂ Ω . \left\{\begin{array}{l} -\Delta u_{1}^{n+1} = f,\mathbf{x} \in \Omega_0,\\ u_{1}^{n+1} = u_0,\mathbf{x} \in \partial \Omega_0 \cap \partial \Omega,\\ u_{1}^{n+1} = u_{2}^n,\mathbf{x} \in \partial \Omega_0 \setminus \partial \Omega. \end{array}\right. ⎩ ⎨ ⎧−Δu1n+1=f,x∈Ω0,u1n+1=u0,x∈∂Ω0∩∂Ω,u1n+1=u2n,x∈∂Ω0∖∂Ω.
{ − Δ u 2 n = f , x ∈ Ω 1 , u 2 n = u 0 , x ∈ ∂ Ω 1 ∩ ∂ Ω , u 2 n = u 1 n + 1 , x ∈ ∂ Ω 1 ∖ ∂ Ω . \left\{\begin{array}{l} -\Delta u_{2}^n = f,\mathbf{x} \in \Omega_1,\\ u_{2}^n = u_0,\mathbf{x} \in \partial \Omega_1 \cap \partial \Omega,\\ u_{2}^n = u_{1}^{n+1},\mathbf{x} \in \partial \Omega_1 \setminus \partial \Omega. \end{array}\right. ⎩ ⎨ ⎧−Δu2n=f,x∈Ω1,u2n=u0,x∈∂Ω1∩∂Ω,u2n=u1n+1,x∈∂Ω1∖∂Ω.
则cauchy-schwarz算法的定义是:
上面有两个子问题,两个线性系统的区别在于右端项,因此可以提前构建好矩阵
A
A
A,然后根据不同的右端项定义两个求解函数solve,具体过程可以参考下列图片:
如果将区域分成3个区域,比如说 Ω 0 = [ 0 , 0.5 ] × [ 0 , 1 ] , ω 1 = [ 0.4 , 0.8 ] × [ 0 , 1 ] , ω 1 = [ 0.7 , 1 ] × [ 0 , 1 ] \Omega_0=[0,0.5]\times[0,1],\omega_1=[0.4,0.8]\times[0,1],\omega_1=[0.7,1]\times[0,1] Ω0=[0,0.5]×[0,1],ω1=[0.4,0.8]×[0,1],ω1=[0.7,1]×[0,1],仍然要保证两两之间有overlap,过程与上面算法一样,代码可以参考添加链接描述
1:在上述方法中,最终的问题都被转化成求解一个线性系统
A
x
=
b
Ax=b
Ax=b,这里本人调用的是python库numpy自带的线性求解器
l
i
n
a
l
g
.
s
o
l
v
e
(
A
,
b
)
linalg.solve(A,b)
linalg.solve(A,b),如何设计G-S迭代方法求解,如何证明G-S迭代法对这类线性系统的收敛性.\par
2:线性系统\ref{FDmat2}是稀疏的,是否可以设计一种针对这类问题的带状问题进行求解,并且减少矩阵存储量.
3:在介绍FD求解含时泊松方程时,向前欧拉格式不稳定,如何确定合适的
τ
,
h
x
,
h
y
\tau,h_x,h_y
τ,hx,hy使得该格式稳定.
4:在区域分解算法中,本人的划分都是针对一个维度,如果对区域做棋盘划分,这个算法该如何写,参考提示添加链接描述
有限元法
FD方法的特点是只能求解离散点的取值,如果想要知道不在网格点的函数值,就需要重新做剖分或者做插值得到,基于此有限元方法finite element(FE)发展起来.FE可以被理解为一种插值方法,还是以二维区域为例,将区域 Ω \Omega Ω进行剖分得到 M × N M\times N M×N个小区域,在每个小区域上定义一个分片函数,然后根据网格点的取值进行插值,下面直接以数学公式来进行说明.
首先引入单元函数
ψ
(
x
,
y
)
{\psi}(x,y)
ψ(x,y),对区域进行剖分以后得到
(
x
i
,
y
j
)
(x_i,y_j)
(xi,yj),则会有
φ
i
,
j
=
ψ
(
x
−
x
i
h
x
,
y
−
y
j
h
y
)
\varphi_{i,j} = \psi(\frac{x - x_i}{h_x},\frac{y - y_j}{h_y})
φi,j=ψ(hxx−xi,hyy−yj),那么这样定义的基函数
φ
i
,
j
\varphi_{i,j}
φi,j就满足在第
(
i
,
j
)
(i,j)
(i,j)个网格点上取值为1,在其他网格点上取值为0.
ψ
(
x
,
y
)
=
{
(
1
−
x
)
∗
(
1
−
y
)
,
0
≤
x
≤
1
,
0
≤
y
≤
1
,
(
1
+
x
)
∗
(
1
−
y
)
,
−
1
≤
x
≤
0
,
0
≤
y
≤
1
,
(
1
+
x
)
∗
(
1
+
y
)
,
−
1
≤
x
≤
0
,
−
1
≤
y
≤
0
,
(
1
−
x
)
∗
(
1
+
y
)
,
0
≤
x
≤
1
,
−
1
≤
y
≤
0.
\operatorname{\psi}(x,y)=\left\{\begin{array}{ll} (1 - x)*(1 - y),0 \leq x \leq 1,0 \leq y \leq 1, \\ (1 + x)*(1 - y),-1 \leq x \leq 0,0 \leq y \leq 1, \\ (1 + x)*(1 + y),-1 \leq x \leq 0,-1 \leq y \leq 0, \\ (1 - x)*(1 + y),0 \leq x \leq 1,-1 \leq y \leq 0.\\ \end{array}\right.
ψ(x,y)=⎩
⎨
⎧(1−x)∗(1−y),0≤x≤1,0≤y≤1,(1+x)∗(1−y),−1≤x≤0,0≤y≤1,(1+x)∗(1+y),−1≤x≤0,−1≤y≤0,(1−x)∗(1+y),0≤x≤1,−1≤y≤0.
于是我们的有限元近似解定义为,接下来的重点就是确定
u
i
,
j
u_{i,j}
ui,j的取值.有了上面FD的介绍,其实可以先通过FD求出对应的
u
i
,
j
u_{i,j}
ui,j,然后根据\ref{FE}求出数值解
u
h
u_h
uh,这个方法很容易实现.不过FE并非如此.
u
h
=
∑
i
=
0
i
=
M
∑
j
=
0
j
=
N
u
i
,
j
φ
i
,
j
.
u_h = \sum_{i=0}^{i = M}\sum_{j=0}^{j = N}u_{i,j}\varphi_{i,j}.
uh=∑i=0i=M∑j=0j=Nui,jφi,j.
FE首先需要引入弱形式\ref{FEGalerkin},其中
v
∈
V
h
=
{
v
∣
v
∣
∂
Ω
=
0
}
v \in V_h=\{v| v|_{\partial \Omega} = 0\}
v∈Vh={v∣v∣∂Ω=0}.
{ a ( u , v ) = ∮ Ω ∇ u ⋅ ∇ v d x d y , f ( v ) = ∮ Ω f v d x d y . \left\{\begin{array}{l} a(u,v) = \oint_{\Omega} \nabla u \cdot \nabla v dxdy,\\ f(v) = \oint_{\Omega} fv dxdy. \end{array}\right. {a(u,v)=∮Ω∇u⋅∇vdxdy,f(v)=∮Ωfvdxdy.
根据方程\ref{FE},我们知道这个公式需要求解
(
M
+
1
)
(
N
+
1
)
(M + 1)(N + 1)
(M+1)(N+1)个未知数,在边界上的取值已知,还需要求出在区域内部的取值,因此取
v
i
,
j
=
φ
i
,
j
,
1
≤
i
≤
M
−
1
,
1
≤
j
≤
N
−
1
v_{i,j} = \varphi_{i,j},1 \leq i \leq M - 1,1 \leq j \leq N - 1
vi,j=φi,j,1≤i≤M−1,1≤j≤N−1带入方程\ref{FEGalerkin}就可以得到
(
M
+
1
)
(
N
+
1
)
(M + 1)(N + 1)
(M+1)(N+1)个方程,这里面有
(
M
−
1
)
(
N
−
1
)
(M - 1)(N - 1)
(M−1)(N−1)个积分需要计算.但是可以发现由于
φ
i
,
j
\varphi_{i,j}
φi,j在大部分区域都是0,因此可以采取扫描单元的做法来简化计算.\
重点:代入
u
h
,
v
=
φ
i
,
j
u_h,v = \varphi_{i, j}
uh,v=φi,j得到线性系统得到公式\eqref{intergral}.\
{
a
(
u
h
,
φ
i
,
j
)
=
f
(
φ
i
,
j
)
,
1
≤
i
≤
M
−
1
,
1
≤
j
≤
N
−
1
,
u
h
(
x
i
,
y
j
)
=
u
0
(
x
i
,
y
j
)
,
(
x
i
,
y
j
)
∈
∂
Ω
.
\left\{\begin{array}{l} a(u_h,\varphi_{i, j}) = f(\varphi_{i, j}),1 \leq i \leq M - 1, 1 \leq j \leq N -1,\\ u_h(x_i,y_j) = u_0(x_i,y_j),(x_i,y_j) \in \partial \Omega. \end{array}\right.
{a(uh,φi,j)=f(φi,j),1≤i≤M−1,1≤j≤N−1,uh(xi,yj)=u0(xi,yj),(xi,yj)∈∂Ω.
由于
u
h
u_h
uh和
φ
i
,
j
\varphi_{i, j}
φi,j的特殊性,区域
Ω
\Omega
Ω上的积分只有一小部分非0.
{
∫
Ω
(
∑
j
=
0
j
=
N
u
i
,
j
∇
φ
i
,
j
)
∇
φ
i
^
,
j
^
d
Ω
=
∫
Ω
f
φ
i
^
,
j
^
d
Ω
,
∑
m
=
0
M
N
−
1
∫
Ω
m
(
∑
i
=
0
i
=
M
∑
j
=
0
j
=
N
u
i
,
j
∇
φ
i
,
j
)
∇
φ
i
^
,
j
^
d
Ω
=
∑
m
=
0
M
N
−
1
∫
Ω
m
f
φ
i
^
,
j
^
d
Ω
.
\left\{\begin{array}{l} \int_{\Omega}(\sum_{j=0}^{j = N}u_{i,j}\nabla \varphi_{i,j})\nabla \varphi_{\hat{i},\hat{j}} d\Omega = \int_{\Omega} f \varphi_{\hat{i},\hat{j}}d\Omega,\\ \sum_{m=0}^{MN-1}\int_{\Omega_m}(\sum_{i=0}^{i = M}\sum_{j=0}^{j = N}u_{i,j}\nabla \varphi_{i,j})\nabla \varphi_{\hat{i},\hat{j}} d\Omega = \sum_{m=0}^{MN-1} \int_{\Omega_m} f \varphi_{\hat{i},\hat{j}}d\Omega. \end{array}\right.
{∫Ω(∑j=0j=Nui,j∇φi,j)∇φi^,j^dΩ=∫Ωfφi^,j^dΩ,∑m=0MN−1∫Ωm(∑i=0i=M∑j=0j=Nui,j∇φi,j)∇φi^,j^dΩ=∑m=0MN−1∫Ωmfφi^,j^dΩ.
当
(
x
i
,
y
j
)
,
(
x
i
^
,
y
j
^
)
(x_i,y_j),(x_{\hat{i}},y_{\hat{j}})
(xi,yj),(xi^,yj^)同时位于
Ω
m
\Omega_m
Ωm的顶点,才有
∫
Ω
m
∇
φ
i
,
j
∇
φ
i
^
,
j
^
≠
0
\int_{\Omega_m}\nabla \varphi_{i,j} \nabla \varphi_{\hat{i},\hat{j}} \neq 0
∫Ωm∇φi,j∇φi^,j^=0.
∫
Ω
m
(
∑
i
=
0
i
=
M
∑
j
=
0
j
=
N
u
i
,
j
∇
φ
i
,
j
)
∇
φ
i
^
,
j
^
d
Ω
=
∫
Ω
m
∑
∣
i
−
i
^
∣
≤
1
,
∣
j
−
j
^
∣
≤
1
∇
φ
i
,
j
∇
φ
i
^
,
j
^
d
Ω
.
\int_{\Omega_m}(\sum_{i=0}^{i = M}\sum_{j=0}^{j = N}u_{i,j}\nabla \varphi_{i,j})\nabla \varphi_{\hat{i},\hat{j}} d\Omega = \int_{\Omega_m} \sum_{|i-\hat{i}|\leq 1,|j - \hat{j}|\leq 1} \nabla \varphi_{i,j} \nabla \varphi_{\hat{i},\hat{j}} d\Omega.
∫Ωm(∑i=0i=M∑j=0j=Nui,j∇φi,j)∇φi^,j^dΩ=∫Ωm∑∣i−i^∣≤1,∣j−j^∣≤1∇φi,j∇φi^,j^dΩ.
因此才有下面的公式成立.
a
i
,
j
=
∑
N
o
d
e
s
[
i
]
,
N
o
d
e
s
[
j
]
∈
T
m
∮
T
m
∇
ϕ
i
∇
ϕ
j
d
x
d
y
+
∑
N
o
d
e
s
[
i
]
,
N
o
d
e
s
[
j
]
∈
∂
Ω
m
∮
∂
Ω
m
β
ϕ
i
ϕ
j
d
s
a_{i,j}= \sum_{Nodes[i],Nodes[j] \in T_{m}} \oint_{T_{m}} \nabla \phi_{i}\nabla \phi_{j}dxdy + \sum_{Nodes[i],Nodes[j] \in \partial \Omega_{m}} \oint_{\partial \Omega_{m}} \beta \phi_{i}\phi_{j}ds
ai,j=∑Nodes[i],Nodes[j]∈Tm∮Tm∇ϕi∇ϕjdxdy+∑Nodes[i],Nodes[j]∈∂Ωm∮∂Ωmβϕiϕjds
其中Nodes$[i]
表示在整体排序中的第
表示在整体排序中的第
表示在整体排序中的第i
个网格点
,
个网格点,
个网格点,T_m
表示第
m
个单元,区域一共有
表示第m个单元,区域一共有
表示第m个单元,区域一共有M\times N$个单元.
类似地,我们可以给出右端项,也就是所谓的载荷向量的b的定义
b
i
=
∑
N
o
d
e
s
[
i
]
∈
T
m
∮
T
m
f
ϕ
i
d
x
d
y
+
∑
s
e
l
f
.
N
o
d
e
s
[
i
]
∈
∂
Ω
m
∮
∂
Ω
m
β
g
ϕ
i
d
s
b_{i}= \sum_{Nodes[i] \in T_{m}} \oint_{T_{m}} f\phi_{i}dxdy + \sum_{self.Nodes[i] \in \partial \Omega_{m}} \oint_{\partial \Omega_{m}} \beta g\phi_{i}ds
bi=∑Nodes[i]∈Tm∮Tmfϕidxdy+∑self.Nodes[i]∈∂Ωm∮∂Ωmβgϕids
在给出这些矩阵元素和右端项元素的定义计算以后,我们就必须要解决积分问题,我们需要对区域进行离散化处理,选取有限个点作为积分节点,根据数值逼近的原理,我们知道在区域
[
0
,
1
]
2
[0,1]^2
[0,1]2中,我们只要选取以下4个点
A
1
=
(
1
−
3
/
3
2
,
1
−
3
/
3
2
)
,
A
2
=
(
1
+
3
/
3
2
,
1
−
3
/
3
2
)
,
A
3
=
(
1
+
3
/
3
2
,
1
+
3
/
3
2
)
,
A
4
=
(
1
−
3
/
3
2
,
1
+
3
/
3
2
)
A_1 = (\frac{1 - \sqrt{3}/3}{2},\frac{1 - \sqrt{3}/3}{2}),A_2 = (\frac{1 + \sqrt{3}/3}{2},\frac{1 - \sqrt{3}/3}{2}),A_3 = (\frac{1 + \sqrt{3}/3}{2},\frac{1 + \sqrt{3}/3}{2}),A_4 = (\frac{1 - \sqrt{3}/3}{2},\frac{1 + \sqrt{3}/3}{2})
A1=(21−3/3,21−3/3),A2=(21+3/3,21−3/3),A3=(21+3/3,21+3/3),A4=(21−3/3,21+3/3)作为积分点,
∮
[
0
,
1
]
2
f
d
x
d
y
=
0.25
∗
∑
i
=
1
4
(
f
(
A
i
)
)
\oint_{[0,1]^2} f dxdy = 0.25*\sum_{i = 1}^{4}(f(A_i))
∮[0,1]2fdxdy=0.25∗∑i=14(f(Ai))
那么上述积分近似就有2阶代数精度,即对于
1
,
x
,
y
,
x
2
,
y
2
,
x
y
1,x,y,x^2,y^2,xy
1,x,y,x2,y2,xy这些函数的线性组合,上述积分等式恒成立。本人代码里面FENET里面定义了
s
e
l
f
.
g
p
_
p
o
s
=
[
1
−
3
/
3
2
,
1
−
3
/
3
2
]
self.gp\_{}pos = [\frac{1 - \sqrt{3}/3}{2},\frac{1 - \sqrt{3}/3}{2}]
self.gp_pos=[21−3/3,21−3/3]以便于在每个积分单元采取这样4个高斯积分节点。在这些定义完以后,本人定义了
I
n
t
_
b
a
s
i
c
_
b
a
s
i
c
Int\_{}basic\_{}basic
Int_basic_basic函数表示在某积分单元上两个基函数之间梯度的积分,定义了
I
n
t
_
F
_
b
a
s
i
c
Int\_{}F\_{}basic
Int_F_basic表示在某积分单元上f和某基函数的积分,定义
B
o
u
_
b
a
s
i
c
_
b
a
s
i
c
Bou\_{}basic\_{}basic
Bou_basic_basic表示在某Neumann边界线段单元上两个基函数的线积分,定义
B
o
u
_
N
e
u
_
b
a
s
i
c
Bou\_{}Neu\_{}basic
Bou_Neu_basic表示在Neumann边界上函数g和基函数在该线段单元上的线积分,代码参考添加链接描述
深度学习求解PDE
传统方法FD,FE最大的问题在于当坐标是高维向量的时候,无法对高维空间进行剖分,这个会造成维度灾难curse of dimensionality.而这些年深度学习的兴起给我们提供了一个新的方法来求解PDE,下图\ref{FNN}就是一个典型的全连接网络,该网络接受4个维度,输出一个标量,可以这么理解
F
N
N
(
x
)
FNN(\mathbf{x})
FNN(x)定义了一个从
R
4
R^4
R4到
R
1
R^1
R1的函数,这个函数是通过激活函数和线性组合得到的,使用深度学习求解PDE的核心就是选择适当的参数
W
,
b
W,b
W,b使得
F
N
N
(
x
)
FNN(\mathbf{x})
FNN(x)尽可能逼近精确解.
一个通用的全连接网络NN表达式如下\ref{FNNform}所示,在图片\ref{FNN}中有两个隐藏层,因此
n
=
2
n=2
n=2,网络搭建的细节可以参考文献\cite{2nd}.
{
f
i
(
s
)
=
ϕ
(
W
i
,
2
⋅
ϕ
(
W
i
,
1
s
+
b
i
,
1
)
+
b
i
,
2
)
,
z
=
f
n
∘
f
n
−
1
∘
…
∘
f
0
(
x
)
,
y
(
x
)
=
W
z
+
b
.
\left\{\begin{array}{l} f_{i}(s)=\phi\left(W_{i, 2} \cdot \phi\left(W_{i, 1} s+b_{i, 1}\right)+b_{i, 2}\right),\\ z=f_n \circ f_{n-1}\circ \ldots \circ f_0(\mathbf{x}),\\ y(\mathbf{x}) = W z + b. \end{array}\right.
⎩
⎨
⎧fi(s)=ϕ(Wi,2⋅ϕ(Wi,1s+bi,1)+bi,2),z=fn∘fn−1∘…∘f0(x),y(x)=Wz+b.
正如上面提到的,利用深度学习求解PDE的核心在于选择合适的权重和偏置,我们以
θ
\theta
θ来表示神经网络NN中的权重和偏置,这些也被称为NN的参数,我们需要根据PDE来选择等价的优化问题,然后利用优化中的迭代算法来迭代更新参数
θ
\theta
θ,下面我们引入几个基本的算法来做介绍,测试函数都是
u
=
log
(
10
(
x
+
y
)
2
+
(
x
−
y
)
2
+
0.5
)
u=\log(10(x+y)^2 + (x - y)^2 + 0.5)
u=log(10(x+y)2+(x−y)2+0.5).
Deep Ritz
对于泊松方程,利用极小位能原理可以把PDE转化为等价的变分问题
{
min
u
∫
Ω
∣
∇
u
∣
2
−
2
f
u
d
Ω
,
subject to
u
=
u
0
,
x
∈
∂
Ω
.
\left\{\begin{aligned} &\min_{u} \int_{\Omega}|\nabla u|^2 - 2fu d\Omega, \\ &\text { subject to } u = u_0,\mathbf{x} \in \partial \Omega. \end{aligned}\right.
⎩
⎨
⎧umin∫Ω∣∇u∣2−2fudΩ, subject to u=u0,x∈∂Ω.
我们需要根据变分问题\ref{Ritz}来更新参数,首先就需要把积分离散化,我们将在区域
Ω
\Omega
Ω采样
n
I
n_I
nI个点
{
x
i
}
i
=
0
n
I
−
1
\{\mathbf{x}_i\}_{i=0}^{n_I - 1}
{xi}i=0nI−1作为积分点.下面我们将详细介绍利用python如何实现deep ritz方法:
第一:我们需要对区域
Ω
\Omega
Ω进行离散化,需要在内部采集样本点
X
=
{
x
i
}
i
=
0
n
I
−
1
X = \{\mathbf{x}_i\}_{i=0}^{n_I - 1}
X={xi}i=0nI−1,从上面公式中可以发现还需要准备好函数
f
(
X
)
f(X)
f(X)数据集,采样参考以下图片.
第二:我们需要在区域边界 ∂ Ω \partial \Omega ∂Ω进行采样,得到边界上的样本点 X = { x i } i = 0 n B − 1 X=\{\mathbf{x}_i\}_{i=0}^{n_B - 1} X={xi}i=0nB−1,以及 u 0 ( X ) u_0(X) u0(X).
第三:我们需要搭建全连接网络,pytorch自带的torch.nn.Linear来作为每一层的初始参数,搭建好一个输入节点为2,输出节点为1的Net.
第四:我们需要根据网络和区域内部边界样本点和\ref{Ritz}定义一个损失函数,对于这种约束优化问题,我们可以采取惩罚函数方法来定义一个损失函数\ref{DRM},其中
u
(
θ
)
u(\theta)
u(θ)表示FNN的预测解,
∇
u
(
θ
)
\nabla u(\theta)
∇u(θ)通过调用pytorch中的自动求导函数torch.autograd.grad函数实现,损失函数以及一部分的训练过程参考以下图片.
{
L
=
L
I
+
β
L
B
,
L
I
=
1
n
I
∑
i
=
0
n
I
−
1
∣
∇
u
(
θ
)
i
∣
2
−
2
f
i
u
(
θ
)
i
,
L
B
=
1
n
B
∑
i
=
0
n
B
−
1
(
u
(
θ
)
−
u
0
)
i
2
.
\left\{\begin{aligned} &L = L_I + \beta L_B, \\ &L_I =\frac{1}{n_I}\sum_{i=0}^{n_I - 1}|\nabla u(\theta)_i|^2 - 2f_i u(\theta)_i,\\ &L_B = \frac{1}{n_B}\sum_{i=0}^{n_B - 1} (u(\theta) - u_0)_i^2. \end{aligned}\right.
⎩
⎨
⎧L=LI+βLB,LI=nI1i=0∑nI−1∣∇u(θ)i∣2−2fiu(θ)i,LB=nB1i=0∑nB−1(u(θ)−u0)i2.
第五:有了损失函数以后,我们需要对网络Net的参数做迭代优化,比如最简单的梯度下降法:
θ
k
+
1
=
θ
k
−
c
∇
L
(
θ
k
)
\theta^{k+1}=\theta^k - c \nabla L(\theta^k)
θk+1=θk−c∇L(θk)
对于大部分人来说,手动求出
∇
L
(
θ
k
)
\nabla L(\theta^k)
∇L(θk)极为困难,幸好pytorch自带这个功能,只需要调用L.backwad()函数就可以得到,并且调用pytorch自带的优化器比如说torch.optim.Adam(Net.parameters(),lr)就可以自动实现参数的更新,lr是步长,也称learning rate,参考论文,代码参考添加链接描述.
考虑该变分问题,可以发现这种方法有几个特点,首先这个问题只需要利用一阶梯度信息,其次这个问题不需要额外增加测试函数,正是这两点,使得Ritz方法非常适合求解高维PDE。但是另一方面,如果使用惩罚函数方法求解该优化问题,损失函数的最优值不见得是0,此时优化过程难以控制。因此如果可以寻找到一种方法,使得近似解自动满足边界条件,就可以避免惩罚项的出现。
Deep Nitsche
为了进一步提高精度,有的论文修改了针对泊松方程\ref{poisson}的变分问题,得到了一个Deep Nitsche Method,和Deep Ritz唯一的区别在于变分问题不一样,其他的区域采样或者训练如出一辙,细节参考论文,代码参考添加链接描述
Galerkin
Galerkin的特点,只需要利用pytorch对模型求一阶梯度(可以大幅度降低计算时间和存储成本,相比于PINN而言,PINN需要额外存储几个梯度数组),但是必须要提前准备好测试函数数据,这个对数学功底要求很高。以及,Galerkin仍然不能用来求解高维PDE,原因在于高维空间中的测试函数
v
v
v 是定义在高维超立方体的函数,这个定义一定需要利用for循环定义,而且高维超立方体的采样不容易。
Galerkin原理是从另一个角度来对泊松方程进行降阶处理,具体参考公式
{
∫
Ω
∇
u
⋅
∇
v
−
f
v
d
Ω
=
0
,
subject to
u
=
u
0
,
x
∈
∂
Ω
,
v
∈
V
h
=
{
v
∣
v
∣
∂
Ω
=
0
}
.
\left\{\begin{aligned} &\int_{\Omega} \nabla u \cdot \nabla v - f v d \Omega = 0, \\ &\text { subject to } u = u_0,\mathbf{x} \in \partial \Omega,\\ &v \in V_h = \{v | v |_{\partial \Omega} = 0\}. \end{aligned}\right.
⎩
⎨
⎧∫Ω∇u⋅∇v−fvdΩ=0, subject to u=u0,x∈∂Ω,v∈Vh={v∣v∣∂Ω=0}.
在代码的实现过程中,除了采集样本点
X
X
X和准备右端项
f
(
X
)
f(X)
f(X)以外,这里还需要额外准备好
v
(
X
)
,
∇
v
(
X
)
v(X),\nabla v(X)
v(X),∇v(X),在本人的代码实现中,
v
v
v取得是前面有限元提到的单元函数
φ
i
,
j
\varphi_{i,j}
φi,j,值得注意的是,此时我们需要选取尽可能多个测试函数
v
v
v带入方程\ref{DGM}才能得到好的效果,针对测试函数的选取,又可以有多种策略.
特别注意,Galerkin对数学功底要求很大的另一个原因在于推导弱形式,上面这个二维泊松方程的弱形式只需要利用二维积分的格林公式即可,但是我特意没用含时间的热方程举例子,原来就在于即使是那种简单例子,弱形式的推导就已经有难度了。下面罗列一个NS方程的例子.
上面这个NS方程的弱形式就连很多数学系本科的同学也很难推导(此时反而利用PINN会更加方便)。PINN和Galerkin都有一个特点,就是损失函数直接描述了PDE的求解情况,根据损失函数的定义我们知道,神经网络的训练就是尽可能让损失函数接近于0,和0越接近,说明模型训练的效果越好。
PFNN
在论文中,以区域内点为中心构造了
φ
i
,
j
,
1
≤
M
−
1
,
1
≤
j
≤
N
−
1
\varphi_{i,j},1 \leq M - 1,1 \leq j \leq N - 1
φi,j,1≤M−1,1≤j≤N−1的
(
M
−
1
)
(
N
−
1
)
(M - 1)(N - 1)
(M−1)(N−1)个测试函数,然后代入方程\ref{DGM}来构造损失函数,同时使用length factor和另一个网络将区域内部和边界分开处理取得了不错的效果,损失函数参考\ref{lossDGM},其中由于
φ
i
,
j
\varphi_{i,j}
φi,j的特殊性质,
L
i
,
j
L_{i,j}
Li,j的积分计算可以限制在以
(
x
i
,
y
j
)
(x_i,y_j)
(xi,yj)为中心的周围4个单元上,这个可以简化积分的计算.
{
L
=
∑
i
=
1
M
−
1
∑
j
=
1
N
−
1
L
i
,
j
+
β
L
B
,
L
i
=
(
∫
Ω
∇
u
⋅
∇
φ
i
,
j
−
f
φ
i
,
j
d
Ω
)
2
,
L
B
=
1
n
B
∑
i
=
0
n
B
−
1
(
u
(
θ
)
−
u
0
)
i
2
.
\left\{\begin{aligned} &L = \sum_{i=1}^{M-1} \sum_{j=1}^{N-1} L_{i,j} + \beta L_B, \\ &L_i = (\int_{\Omega} \nabla u \cdot \nabla \varphi_{i,j} - f \varphi_{i,j} d \Omega)^2,\\ &L_B = \frac{1}{n_B}\sum_{i=0}^{n_B - 1} (u(\theta) - u_0)_i^2. \end{aligned}\right.
⎩
⎨
⎧L=i=1∑M−1j=1∑N−1Li,j+βLB,Li=(∫Ω∇u⋅∇φi,j−fφi,jdΩ)2,LB=nB1i=0∑nB−1(u(θ)−u0)i2.
代码参考添加链接描述
\subsubsection{WAN}
对抗神经网络求解泊松方程的核心是利用两个网络,
N
u
N_u
Nu模拟精确解
u
u
u,
N
v
N_v
Nv模拟测试函数
v
v
v,在训练过程中需要不断调整网络
N
v
N_v
Nv的参数使得
v
v
v尽量填充整个
V
h
V_h
Vh空间.假设
θ
\theta
θ表示网络
N
u
N_u
Nu的参数,
η
\eta
η表示网络
N
v
N_v
Nv的参数,那么WAN的核心思想可以理解为求解下列优化问题\ref{WAN},其中
L
L
L的定义类似于:
max
η
min
θ
L
.
\max_{\eta}\min_{\theta} L.
maxηminθL.
这里需要注意,为了使得
v
∈
V
h
v \in V_h
v∈Vh,我们需要在网络
N
v
N_v
Nv的基础上乘一个length factor,与此同时这两个网络需要交替训练,具体细节参考论文和代码添加链接描述
PINN
physics informed neural network最早出现于论文,核心在于下面这个二阶损失函数\ref{lossPINN},里面的梯度也是调用pytorch的自动求导功能完成的,这个损失函数非常直观,事实上,根据本人的数值实验,在求解二维泊松方程这类问题上,PINN的效果远比上面提到的方法要好,PINN的代码实现和上面的如出一辙,这里不赘述,代码参考添加链接描述.
{
L
=
L
I
+
β
L
B
,
L
I
=
1
n
I
∑
i
=
0
n
I
−
1
(
−
Δ
u
−
f
)
i
2
,
L
B
=
1
n
B
∑
i
=
0
n
B
−
1
(
u
(
θ
)
−
u
0
)
i
2
.
\left\{\begin{aligned} &L = L_I + \beta L_B, \\ &L_I = \frac{1}{n_I}\sum_{i=0}^{n_I - 1}(-\Delta u - f)_i^2,\\ &L_B = \frac{1}{n_B}\sum_{i=0}^{n_B - 1} (u(\theta) - u_0)_i^2. \end{aligned}\right.
⎩
⎨
⎧L=LI+βLB,LI=nI1i=0∑nI−1(−Δu−f)i2,LB=nB1i=0∑nB−1(u(θ)−u0)i2.
PINN的优势在于损失函数的形成非常直观,如果使用高阶优化器可以得到非常好的效果。PINN的缺点在于不能求解高维PDE,原来在于自动求导的时候需要求二阶导数,想象一下,如果问题是一个100维的,求解一阶导数的时候只需要利用下面这一行代码就可以解决。
u_x, = torch.autograd.grad(u, inset.X, create_graph=True, retain_graph=True,
grad_outputs=torch.ones(inset.size,1))
但是,如果求解二阶导数,就必须要使用for循环,在形成损失函数的时候出现这个for循环将会导致优化过程特别缓慢,没有特别意义。
u_xx = torch.zeros(100,N)
for i in range(100):
u_xx[i:i + 1,:], = torch.autograd.grad(u_x[i:i + 1,:], inset.X, create_graph=True, retain_graph=True,
grad_outputs=torch.ones(inset.size,1))
parametric PDE
仍然考虑泊松方程,不过这里的右端项
f
=
f
(
x
,
μ
)
,
μ
∈
P
f = f(\mathbf{x},\mathbf{\mu}),\mathbf{\mu} \in \mathcal{P}
f=f(x,μ),μ∈P,现在需要求解对于不同参数
μ
\mathbf{\mu}
μ的精确解,我们以
u
(
x
,
y
,
μ
)
=
exp
(
−
(
x
−
μ
1
)
2
+
(
y
−
μ
2
)
2
2
μ
3
2
)
u(x,y,\mathbf{\mu})=\exp(-\frac{(x - \mu_1)^2 + (y - \mu_2)^2}{2\mu_3^2})
u(x,y,μ)=exp(−2μ32(x−μ1)2+(y−μ2)2)
为例子,其中
μ
=
(
μ
1
,
μ
2
,
μ
3
)
∈
[
0
,
1
]
×
[
0
,
1
]
×
[
0.1
,
1
]
\mathbf{\mu}=(\mu_1,\mu_2,\mu_3) \in [0,1]\times[0,1]\times[0.1,1]
μ=(μ1,μ2,μ3)∈[0,1]×[0,1]×[0.1,1].对于这个问题,其实做法和上面的泊松方程处理类似,可以把
u
u
u理解为在一个5维空间的函数,也就是说在求解过程中,只需要在采样过程将原来的二维空间采样修改成现在的5维空间采样,把空间坐标和参数都当成输入传进网络就可以得到一个含参问题的解.引入网络Net接收
(
x
,
y
,
μ
1
,
μ
2
,
μ
3
)
(x,y,\mu_1,\mu_2,\mu_3)
(x,y,μ1,μ2,μ3)为输入,输出对应的解即可,网络搭建参考\ref{paNN},这里为了简单放了一个二维参数空间的FNN示意图,代码参考添加链接描述,求解结果如下,参考论文
代码说明
为了更好理解,这里我们将针对一个典型的PINN求解含参PDE问题代码做一个细致的说明,代码参考添加链接描述
第一步:导入相关的库,torch是用于神经网络搭建和训练的库,安装参考官方说明添加链接描述,matplotlib用于可视化,用法比较复杂,本人也需要百度才能知道如何针对需求使用命令,time用于计时,numpy是python最主要的数学库,注意最后那个bfgs是本人课题组自己编写的一个优化器,读者可以注释掉,最后优化的时候用torch自带的LBFGS优化器代替.
第二步:定义代码的运行设备device,这里本人选择GPU也就是cuda,读者需要自己确定自己电脑或者服务器是否有GPU,其次定义精确解UU和右端项FF,UU里面的order表示精确解的阶数,比如说
[
0
,
1
]
[0,1]
[0,1]表示
u
y
u_y
uy,bounds表示求解区域和参数空间.
第三步:定义INSET,BDSET,TESET这三个类,分别表示内点集合,边界点集合,测试集集合,其中还会封装一些跟样本点有关的数据进去,比如说右端项.
第四步:搭建网络,这个Net类接受两个参数,layer表示网络的层数和每层的神经元个数,dtype表示网络的数据类型,我后面取的是double,根据我的经验,数据精度高得到的效果也会更好一些,当然了,这个也更消耗内存.
第五步:修改数据类型,上面的INSET那三个类里面的数据默认都是float,为了和Net数据类型匹配,这里需要把INSET那几个类里面的数据类型进行修改,比如说 x = x . t y p e ( d t y p e ) x = x.type(dtype) x=x.type(dtype)就可以把 x x x从float变成dtype一样的类型,然后为了后面训练的时候加速,我们一开始就提出要把代码送到GPU上训练,但是数据一开始默认在CPU上,因此我们还需要写一个函数把所有的数据搬运到GPU,为了训练完以后数据可以回到CPU,这里相应地也要定义一个函数把数据从GPU搬运回CPU.
第六步:定义损失函数和训练函数,损失函数之前讲过,这里不赘述,主要介绍一下训练函数的定义.训练需要优化器optim,我一般都取LBFGS或者我们课题组自己写的BFGS,如果使用Adam优化器,训练过程的核心就是里面注释掉的代码,但是BFGS或者LBFGS的核心就是closure这个函数.这里做一些解释,optim.zero
_
\_{}
_grad表示清除优化器里面存储的梯度,防止每一次的梯度进行叠加,loss.backward表示反向传播,这里会自动计算损失函数关于网络参数
W
,
b
W,b
W,b的梯度,optim.step是负责根据优化器来对参数
W
,
b
W,b
W,b做更新,三者缺一不可,等到这三个结束以后还需要重新计算一下loss,这里本人不是特别理解,但是本人的实验指出这一步很重要,如果注释掉,结果不对.
最后就是对上面定义的函数,网络,类进行实例化和调用,然后一切就结束了.
总结
上述提到的几种方法各有特点,这里对这些方法做一个总结.
神经网络方法求解PDE的困难和弊端
1:神经网络方法求解PDE缺乏收敛性保证。通过上面的介绍,我们可以这么理解,神经网络求解PDE是把原始问题转化为一个优化问题,然后不断通过更新神经网络的参数来达到min loss function的做法,比如说PINN和Galerkin思想,神经网络形成的损失函数只要不断接近0,那么说明神经网络学习的越好。但是现在有一个问题,会不会出现一种情况,形成了一个损失函数,不管怎么更新网络的参数,损失函数都维持在一个较高的水平呢?或者另一种情况,神经网络需要花大量时间训练才能把损失函数降到一个较低的水平。下面我们看一个障碍流问题的例子。
上面这个就是计算区域,这个问题描述了流体从入口进入管道,管道内部有一个障碍,然后现在想预测流体在出口的流速。这个问题可以使用PINN的思想,先采集样本点,然后搭建网络,优化损失函数,下面先给出这个问题的一个参考解,这个解是通过OpenFoam求出来的。
本人训练过程就发现,这个问题的损失函数特别难下降,具体可以参考下面这个图片,虽然原理在那摆着,但是这个问题却难以求解。
本人总结了一句话:PINN或者是Galerkin这类神经网络方法,都只能求解简单的PDE,对于简单的PDE,往往可以得到优于传统数值方法的效果,但是训练时间绝对更长。对于复杂的PDE,比如说NS方程,尤其是带有复杂边界的问题,这类简单的NN方法无法得到多好的效果,但是另一方面,传统数值方法求解复杂的PDE也同样需要消耗大量的计算精力。
个人以为NN求解PDE的优势应该在于求解高维的简单的PDE,比如说高维的Dirichlet边界泊松方程,这种方程由于计算区域维度更高,所以传统数值方法很容易发生维度灾难,但是神经网络方法却可以通过蒙特卡洛等方法对计算区域进行采样,轻而易举求解。文章来源:https://www.toymoban.com/news/detail-778792.html
NN求解PDE目前更多还是应用于科研领域,对于实际应用目前难以推广,一个非常大的原因就是NN求解PDE没有收敛性的理论保证,对于PDE问题,NN就像一个炼丹炉,谁也不知道这个问题到底会求解成什么样子。反而是传统数值方法(FD,FE),虽然求解PDE很消耗时间,计算复杂度也高,但是这类方法的理论收敛经过几十年的研究论证,已经可以做到在求解问题之前就推断出计算结果大概可以到什么程度,这一点对于应用非常重要。文章来源地址https://www.toymoban.com/news/detail-778792.html
到了这里,关于PDE的数值解法(有限元,有限差分法)综合介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!