欢迎光临散文网 会员登陆 & 注册

手动实现用有限元方法求解一维方程

2023-07-15 10:45 作者:quantumtopology  | 我要投稿

上次专栏写了scikit-fem的两个例子。但随着后续使用发现,这个软件包用起来也相当不方便,最大的问题还是出在文档不完整。我尝试求解一个向量值的方程(这个专栏)就会讲,但尝试了好久也没搞清楚在scikit-fem中要怎么实现。最后在github上问作者,他写的代码用到的是这个包中还处于试验阶段的模块NonlinearForm,还要依赖外部软件包autograd,于是我瞬间就不想用了😂。后来想想,其实一维方程解起来并不麻烦。有限元方法麻烦的是在高维以及高阶基函数的情形,此时需要规划产生的线性方程组矩阵元的排列顺序。因为我用到的只有一维情形,那为什么不自己写呢。

  1. 简单的扩散方程

    下面我们求解方程

    %5Cfrac%7B%5Cpartial%20u%7D%7B%5Cpartial%20t%7D%20%3D%20%5Cfrac%7B%5Cpartial%5E2%20u%7D%7B%5Cpartial%20x%5E2%7D%2C%5Cquad%5Cfrac%7B%5Cpartial%20u%7D%7B%5Cpartial%20x%7D(x%20%3D%200)%20%3D%200.05%2C%5Cquad%20u(1%2C%20t)%20%3D%201.0

    初始条件为u=1。

使用向后欧拉公式:

u%5E%7Bk%2B1%7D%20-%20u%5Ek%20%3D%20%5CDelta%20t%20%5Cfrac%7B%5Cpartial%5E2%20u%5E%7Bk%2B1%7D%7D%7B%5Cpartial%20x%5E2%7D

两边同时乘以$v$,再积分得到变分形式

%5Cint%20u%5E%7Bk%2B1%7D%20v%20%2B%20%5CDelta%20t%20%5Cint%20%5Cfrac%7B%5Cpartial%20u%5E%7Bk%2B1%7D%7D%7B%5Cpartial%20x%7D%5Cfrac%7B%5Cpartial%20v%7D%7B%5Cpartial%20x%7D%20%3D%20%5Cint%20u%5Ek%20v%20-%200.05%5CDelta%20t%20v(0)

一般trial function和test function取成一样的基函数,注意只有当test function v%20%3D%20%5Cvarphi_0(x)时,v(0)%5Cneq%200,所以-0.05%5CDelta%20t%20v(0)这一项只出现在第一个方程里。

%5Csum%20C_i%5E%7Bk%2B1%7D%20%5Cleft(%5Cint%20%5Cvarphi_i%20%5Cvarphi_j%20%2B%20%5CDelta%20t%20%5Cint%20%5Cpartial_x%5Cvarphi_i%20%5Cpartial_x%5Cvarphi_j%5Cright)%20%3D%20-0.05%5CDelta%20t%20v(0)%2B%20%5Csum%20C_i%5Ek%20%5Cint%20%5Cvarphi_i%20%5Cvarphi_j

我们可以先计算出 %5Cint%20%5Cvarphi_i%20%5Cvarphi_j%5Cint%20%5Cpartial_x%5Cvarphi_i%20%5Cpartial_x%5Cvarphi_j的矩阵然后求解。注意到,因为右边界条件是不需要求解的,所以未知数的个数是n个,从C0到C(n-1)。

我们使用一维的线性元。假设把区间分解成0%20%3D%20x_0%20%3C%20x_1%20%3C%20%5Ccdots%20%3C%20x_%7Bn-1%7D%20%3C%20x_n%20%3D%20L。则相应的基函数为

%5Cvarphi_0%20%3D%20%0A%20%20%20%20%5Cbegin%7Bcases%7D%0A%20%20%20%20%20%20%20%201%20-%20(x%20-%20x_0)%2Fh_1%2C%20%5Cquad%20x%20%5Cin%20%5Bx_0%2C%20x_1%5D%20%5C%5C%0A%20%20%20%20%20%20%20%200%2C%20%5Cquad%20%5Ctext%7Belse%7D%0A%20%20%20%20%5Cend%7Bcases%7D

%5Cvarphi_i%20%3D%20%0A%20%20%20%20%5Cbegin%7Bcases%7D%0A%20%20%20%20%20%20%20%201%20%2B%20(x%20-%20x_i)%2Fh_i%2C%20%5Cquad%20x%20%5Cin%20%5Bx_%7Bi-1%7D%2C%20x_i%5D%20%5C%5C%20%0A%20%20%20%20%20%20%20%201%20-%20(x%20-%20x_i)%2Fh_%7Bi%2B1%7D%2C%5Cquad%20x%20%5Cin%20%5Bx_i%2C%20x_%7Bi%2B1%7D%5D%20%5C%5C%0A%20%20%20%20%20%20%20%200%2C%20%5Cquad%20%5Ctext%7Belse%7D%0A%20%20%20%20%5Cend%7Bcases%7D

i=1,2,...,n-1

%5Cvarphi_n%20%3D%20%0A%20%20%20%20%5Cbegin%7Bcases%7D%0A%20%20%20%20%20%20%20%201%20%2B%20(x%20-%20x_n)%2Fh_n%2C%20%5Cquad%20x%20%5Cin%20%5Bx_%7Bn-1%7D%2C%20x_n%5D%20%5C%5C%0A%20%20%20%20%20%20%20%200%2C%20%5Cquad%20%5Ctext%7Belse%7D%0A%20%20%20%20%5Cend%7Bcases%7D

在使用这个基组时,每个小区间$[x_{i-1}, x_i]$上有两个基函数:1%20-%20(x-x_%7Bi-1%7D)%2Fh_i1%20%2B%20(x-x_i)%2Fh_i。把它们进行如下的线性组合

%5Cbegin%7Balign*%7D%0A%20%20%20%20%26f(x_%7Bi-1%7D)(1%20-%20%5Cfrac%7Bx-x_%7Bi-1%7D%7D%7Bh_i%7D)%20%2B%20f(x_i)(1%20%2B%20%5Cfrac%7Bx-x_i%7D%7Bh_i%7D)%20%5C%5C%0A%20%20%20%20%26%3D%20%5Cfrac%7Bf(x_i)%20-%20f(x_%7Bi-1%7D)%7D%7Bh_i%7D%20x%20%2B%20f(x_i)%20%2B%20f(x_%7Bi-1%7D)%20%2B%20f(x_%7Bi-1%7D)%20%5Cfrac%7Bx_%7Bi-1%7D%7D%7Bh_i%7D%20-%20f(x_i)%5Cfrac%7Bx_i%7D%7Bh_i%7D%20%5C%5C%0A%20%20%20%20%26%3D%20%5Cfrac%7Bf(x_i)%20-%20f(x_%7Bi-1%7D)%7D%7Bh_i%7D%20x%20%2B%20f(x_i)%20%2B%20f(x_%7Bi-1%7D)%20%2B%20f(x_%7Bi-1%7D)%20%5Cfrac%7Bx_%7Bi-1%7D%7D%7Bh_i%7D%20-%20f(x_i)%5Cfrac%7Bx_%7Bi-1%7D%20%2B%20h_i%7D%7Bh_i%7D%20%5C%5C%0A%20%20%20%20%26%3D%20%20%5Cfrac%7Bf(x_i)%20-%20f(x_%7Bi-1%7D)%7D%7Bh_i%7D%20x%20%2B%20f(x_%7Bi-1%7D)%20%2B%20f(x_%7Bi-1%7D)%20%5Cfrac%7Bx_%7Bi-1%7D%7D%7Bh_i%7D%20-%20f(x_i)%5Cfrac%7Bx_%7Bi-1%7D%7D%7Bh_i%7D%20%5C%5C%0A%20%20%20%20%26%3D%20%5Cfrac%7Bf(x_i)%20-%20f(x_%7Bi-1%7D)%7D%7Bh_i%7D%20(x%20-%20x_%7Bi-1%7D)%20%2B%20f(x_%7Bi-1%7D)%0A%5Cend%7Balign*%7D

所以把基函数按照函数值线性组合得到的就是线性插值的结果。

下面是完整的代码:

2. 非线性的扩散方程组

%5Cbegin%7Bcases%7D%0A%20%20%20%20%5Cfrac%7B%5Cpartial%20u_1%7D%7B%5Cpartial%20t%7D%20%26%3D%20%5Cfrac%7B%5Cpartial%5E2%20u_1%7D%7B%5Cpartial%20x%5E2%7D%20-%20%5Cexp(u_1%20-%20u_2)%20%2B%20%5Cexp(-(u_1%20-%20u_2))%20%5C%5C%0A%20%20%20%20%5Cfrac%7B%5Cpartial%20u_2%7D%7B%5Cpartial%20t%7D%20%26%3D%20%5Cfrac%7B%5Cpartial%5E2%20u_2%7D%7B%5Cpartial%20x%5E2%7D%20%2B%20%5Cexp(u_1%20-%20u_2)%20-%20%5Cexp(-(u_1%20-%20u_2))%20%5C%5C%0A%20%20%20%20%5Cend%7Bcases%7D

边界条件为

%5Cbegin%7Bcases%7D%0A%20%20%20%20%20%20%20%20%5Cfrac%7B%5Cpartial%20u_1%7D%7B%5Cpartial%20x%7D(0%2C%20t)%20%26%3D%20%5Csin(u_1(x%20%3D%200))%20%5C%5C%0A%20%20%20%20%20%20%20%20%5Cfrac%7B%5Cpartial%20u_2%7D%7B%5Cpartial%20x%7D(0%2C%20t)%20%26%3D%20-0.1u_1(x%3D0)u_2(x%3D0)%0A%20%20%20%20%5Cend%7Bcases%7D%0A%20%20%20%20%5Cquad%0A%20%20%20%20%5Cbegin%7Bcases%7D%0A%20%20%20%20%20%20%20%20u_1(1%2C%20t)%20%3D%201%20%5C%5C%0A%20%20%20%20%20%20%20%20u_2(1%2C%20t)%20%3D%200%0A%20%20%20%20%5Cend%7Bcases%7D

同样使用向后欧拉公式,得到方程的变分形式。然后把解用基函数展开。这里只给出展开后的结果。

%5Cbegin%7Bmultline%7D%0A%20%20%20%20%5Csum_i%20C_%7Bi%2C1%7D%5E%7Bk%2B1%7D%20%5Cleft(%5Cint%20%5Cvarphi_i%20%5Cvarphi_j%20%2B%20%5CDelta%20t%20%5Cint%20%5Cpartial_x%5Cvarphi_i%20%5Cpartial_x%5Cvarphi_j%5Cright)%20%2B%20%5CDelta%20t%20%5Csin(C_%7B0%2C1%7D%5E%7Bk%2B1%7D)v(0)%20%5C%5C%0A%20%20%20%20%2B%20%5CDelta%20t%20%5Cint%20%5Cleft(%5Cexp(%5Csum_i%20(C_%7Bi%2C1%7D%5E%7Bk%2B1%7D%20-%20C_%7Bi%2C2%7D%5E%7Bk%2B1%7D)%5Cvarphi_i)%20-%20%5Cexp(%5Csum_i%20(C_%7Bi%2C2%7D%5E%7Bk%2B1%7D-C_%7Bi%2C1%7D%5E%7Bk%2B1%7D%20)%5Cvarphi_i)%5Cright)%20%5Cvarphi_j%20%5C%5C%0A%20%20%20%20%3D%20%5Csum_i%20C_%7Bi%2C1%7D%5Ek%20%5Cint%20%5Cvarphi_i%5Cvarphi_j%0A%5Cend%7Bmultline%7D

%5Cbegin%7Bmultline%7D%0A%20%20%20%20%5Csum_i%20C_%7Bi%2C2%7D%5E%7Bk%2B1%7D%20%5Cleft(%5Cint%20%5Cvarphi_i%20%5Cvarphi_j%20%2B%20%5CDelta%20t%20%5Cint%20%5Cpartial_x%5Cvarphi_i%20%5Cpartial_x%5Cvarphi_j%5Cright)%20-%200.01%5CDelta%20t%20C_%7B0%2C1%7D%5E%7Bk%2B1%7DC_%7B0%2C2%7D%5E%7Bk%2B1%7Dv(0)%20%5C%5C%0A%20%20%20%20%2B%20%5CDelta%20t%20%5Cint%20%5Cleft(-%5Cexp(%5Csum_i%20(C_%7Bi%2C1%7D%5E%7Bk%2B1%7D%20-%20C_%7Bi%2C2%7D%5E%7Bk%2B1%7D)%5Cvarphi_i)%20%2B%20%5Cexp(%5Csum_i%20(C_%7Bi%2C2%7D%5E%7Bk%2B1%7D-C_%7Bi%2C1%7D%5E%7Bk%2B1%7D%20)%5Cvarphi_i)%5Cright)%20%5Cvarphi_j%20%5C%5C%0A%20%20%20%20%3D%20%5Csum_i%20C_%7Bi%2C2%7D%5Ek%20%5Cint%20%5Cvarphi_i%5Cvarphi_j%0A%5Cend%7Bmultline%7D

这是关于C_%7Bi%2C1%7D%5E%7Bk%2B1%7DC_%7Bi%2C2%7D%5E%7Bk%2B1%7D的非线性方程,要使用牛顿法求解。但这里我们不写出它的Jacobian矩阵表达式,因为太!麻!烦!了!。直接把它当作一个非线性方程交给scipy的newton_krylov函数求解即可。完整代码如下:


手动实现用有限元方法求解一维方程的评论 (共 条)

分享到微博请遵守国家法律