有限元方法求解一维扩散方程(FEALPy)

有限元方法求解一维扩散方程


之前完成了 FEALPy 有限元求解 Poisson 方程 的数值算例, 通过 湘潭大学王唯师兄的协助, 今天基于 FEALPy 运用有限元方法求解一个抛物型方程,为说明使用方法而又简单起见,于是理论部分专注于讨论下面这个一维的抛物型方程:
∂ u ∂ t − a ∂ 2 u ∂ x 2 = f ( x , t ) , ( x , t ) ∈ ( 0 , 1 ) × ( 0 , T ] u ( x , 0 ) = ψ ( x ) , x ∈ [ 0 , 1 ] u ( 0 , t ) = u ( 1 , t ) = 0 , t ∈ [ 0 , T ] \begin{aligned} &\frac{\partial u}{\partial t}-a \frac{\partial^{2} u}{\partial x^{2}}=f(x, t), \quad(x, t) \in(0,1) \times(0, T] \\ &u(x, 0)=\psi(x), x \in[0,1] \\ &u(0, t)=u(1, t)=0, t \in[0, T] \end{aligned} tuax22u=f(x,t),(x,t)(0,1)×(0,T]u(x,0)=ψ(x),x[0,1]u(0,t)=u(1,t)=0,t[0,T]

有限元方法推导

有限元方法相对于有限差分方法的一个巨大优势在于:对于复杂的几何区域, 网格可以画得非常 随意, 而有限差分法的格式需要建立在规则的网格上.

幸运的是, 复杂区域只存在于空间上, (通常) 并没有“复杂的时间区域” 这一说法, 毕竟时间是一维的, 一维空间上的连通区域都相似于直线、线段、射线之一, 都是相当简单的几何.

也就是说, 在时间轴上或许可以继续沿用有限差分法, 而在空间上采用有限元的方法.
回忆一下用有限元求解的 hello world 模型, 椭圆型问题:
− a ∂ 2 u ∂ x 2 = f ( x ) , x ∈ ( 0 , 1 ) u ( 0 ) = u ( 1 ) = 0 \begin{aligned} &-a \frac{\partial^{2} u}{\partial x^{2}}=f(x), \quad x \in(0,1) \\ &u(0)=u(1)=0 \end{aligned} ax22u=f(x),x(0,1)u(0)=u(1)=0
是将其转化为弱形式, 找一个 u ∈ H 0 1 ( 0 , 1 ) u \in H_{0}^{1}(0,1) uH01(0,1), 使得:
a ∫ 0 1 v ′ u ′ d x = ∫ 0 1 f v d x a \int_{0}^{1} v^{\prime} u^{\prime} d x=\int_{0}^{1} f v d x a01vudx=01fvdx
对任意的 v ∈ H 0 1 ( 0 , 1 ) v \in H_{0}^{1}(0,1) vH01(0,1) 成立来达成的.

具体而言, 是构造一组基于空间的基函数, 使得解在基函数构成的空间中被逼近, 也就是
u ( x ) = ∑ i = 0 N c i ϕ i ( x ) u(x)=\sum_{i=0}^{N} c_{i} \phi_{i}(x) u(x)=i=0Nciϕi(x)
每一个基函数的系数是固定的, 而对于今天这个时间依赖的抛物问题, 那就构造随时间变化的系数来表示好了, 也就是
u ( x , t ) = ∑ i = 0 N c i ( t ) ϕ i ( x ) u(x, t)=\sum_{i=0}^{N} c_{i}(t) \phi_{i}(x) u(x,t)=i=0Nci(t)ϕi(x)
再用弱形式写一下, 就是找到上面的 u \mathrm{u} u, 使得对任意 v ∈ H 0 1 ( 0 , 1 ) v \in H_{0}^{1}(0,1) vH01(0,1), 都有
∫ 0 1 v ( x ) ∂ u ∂ t d x − a ∫ 0 1 v ( x ) ∂ 2 u ∂ x 2 d x = ∫ 0 1 f ( x , t ) v ( x ) d x \int_{0}^{1} v(x) \frac{\partial u}{\partial t} d x-a \int_{0}^{1} v(x) \frac{\partial^{2} u}{\partial x^{2}} d x=\int_{0}^{1} f(x, t) v(x) d x 01v(x)tudxa01v(x)x22udx=01f(x,t)v(x)dx
运用分部积分公式换一换, 得到
∫ 0 1 v ( x ) ∂ u ∂ t d x + a ∫ 0 1 v ′ ( x ) ∂ u ∂ x d x = ∫ 0 1 f ( x , t ) v ( x ) d x \int_{0}^{1} v(x) \frac{\partial u}{\partial t} d x+a \int_{0}^{1} v^{\prime}(x) \frac{\partial u}{\partial x} d x=\int_{0}^{1} f(x, t) v(x) d x 01v(x)tudx+a01v(x)xudx=01f(x,t)v(x)dx
然后由于对于固定的t, 上式的成立也等价于
∑ i = 0 N ( c i ′ ( t ) ∫ 0 1 ϕ j ( x ) ϕ i ( x ) d x + a ⋅ c i ( t ) ∫ 0 1 ϕ j ′ ( x ) ϕ i ′ ( x ) d x ) = ∫ 0 1 f ( x , t ) ϕ i ( x ) d x \sum_{i=0}^{N}\left(c_{i}^{\prime}(t) \int_{0}^{1} \phi_{j}(x) \phi_{i}(x) d x+a \cdot c_{i}(t) \int_{0}^{1} \phi_{j}^{\prime}(x) \phi_{i}^{\prime}(x) d x\right)=\int_{0}^{1} f(x, t) \phi_{i}(x) d x i=0N(ci(t)01ϕj(x)ϕi(x)dx+aci(t)01ϕj(x)ϕi(x)dx)=01f(x,t)ϕi(x)dx
写成矩阵形式
A C ′ ( t ) + a K C ( t ) = F ( t ) A C^{\prime}(t)+a K C(t)=F(t) AC(t)+aKC(t)=F(t)
其中

  1. C ( t ) = [ c 0 ( t ) , c 1 ( t ) , ⋯   , c N ( t ) ] C(t)=\left[c_{0}(t), c_{1}(t), \cdots, c_{N}(t)\right] C(t)=[c0(t),c1(t),,cN(t)], 基函数系数
  2. A = [ ∫ 0 1 ϕ i ϕ j d x ] i j A=\left[\int_{0}^{1} \phi_{i} \phi_{j} d x\right]_{i j} A=[01ϕiϕjdx]ij, 称为质量矩阵
  3. K = [ ∫ 0 1 ϕ i ′ ϕ j ′ d x ] i j K=\left[\int_{0}^{1} \phi_{i}^{\prime} \phi_{j}^{\prime} d x\right]_{i j} K=[01ϕiϕjdx]ij, 是熟悉的全局刚度阵
  4. F ( t ) = [ ∫ 0 1 ϕ i ( x ) f ( x , t ) d x ] i F(t)=\left[\int_{0}^{1} \phi_{i}(x) f(x, t) d x\right]_{i} F(t)=[01ϕi(x)f(x,t)dx]i, 是载荷向量

然后上面的式子就是更久之前所学习的常微分方程了, 当然这个常微分方程的求解还需要初值条件:
A C ( 0 ) = F ( 0 ) A C(0)=F(0) AC(0)=F(0)
其中 F ( 0 ) = [ ∫ 0 1 ϕ i ( x ) ψ ( x ) d x ] i T F(0)=\left[\int_{0}^{1} \phi_{i}(x) \psi(x) d x\right]_{i}^{T} F(0)=[01ϕi(x)ψ(x)dx]iT, 是个向量.

至此, 就可以开始求解方程了.

差分格式的介入

空间的处理交给了有限元方法, 而时间可以交给差分格式, 为了使用差分格式, 直接将时间 [ 0 , T ] [0, T] [0,T] 离散为 t 0 , t 1 , ⋅ , t M t_{0}, t_{1}, \cdot, t_{M} t0,t1,,tM .然后再用

向前 Euler 格式, 得到
A C k + 1 − C k Δ t + a K C k = F ( t k ) C 0 = A − 1 F 0 \begin{array}{r} A \frac{C_{k+1}-C_{k}}{\Delta t}+a K C_{k}=F\left(t_{k}\right) \\ C_{0}=A^{-1} F_{0} \end{array} AΔtCk+1Ck+aKCk=F(tk)C0=A1F0
或者向后 Euler 格式
A C k − C k − 1 Δ t + a K C k = F ( t k ) C 0 = A − 1 F 0 \begin{array}{r} A \frac{C_{k}-C_{k-1}}{\Delta t}+a K C_{k}=F\left(t_{k}\right) \\ C_{0}=A^{-1} F_{0} \end{array} AΔtCkCk1+aKCk=F(tk)C0=A1F0
或者二阶精度的中心 Euler 格式
A C k + 1 − C k Δ t + a K C k + C k + 1 2 = F ( t k + 1 2 ) C 0 = A − 1 F 0 \begin{array}{r} A \frac{C_{k+1}-C_{k}}{\Delta t}+a K \frac{C_{k}+C_{k+1}}{2}=F\left(t_{k+\frac{1}{2}}\right) \\ C_{0}=A^{-1} F_{0} \end{array} AΔtCk+1Ck+aK2Ck+Ck+1=F(tk+21)C0=A1F0
或者按需选取别的格式…

还需要处理的就是两处数值积分了 F ( t ) F(t) F(t) F 0 F_{0} F0 了, 如同往常, 采用Gauss积分公式求解即可.

数值算例

考虑二维抛物方程:

{ ∂ u ( x ⃗ , t ) ∂ t − Δ u ( x ⃗ , t ) = ( 2 π 2 − 1 ) sin ⁡ ( π x 1 ) sin ⁡ ( π x 2 ) e − t , ( x ⃗ , t ) ∈ Ω × ( 0 , 1 ] u ( x ⃗ , t ) = sin ⁡ ( π x 1 ) sin ⁡ ( π x 2 ) e − t , ( x ⃗ , t ) ∈ Ω × 0 u ( x ⃗ , t ) = sin ⁡ ( π x 1 ) sin ⁡ ( π x 2 ) e − t , ( x ⃗ , t ) ∈ ∂ Ω × [ 0 , 1 ] \begin{cases}\frac{\partial u(\vec{x}, t)}{\partial t}-\Delta u(\vec{x}, t)=(2\pi^2-1)\sin (\pi x_1)\sin (\pi x_2) e^{-t}, & (\vec{x}, t) \in \Omega \times (0,1]\\ u(\vec{x}, t)=\sin (\pi x_1)\sin (\pi x_2) e^{-t}, & (\vec{x}, t) \in \Omega\times 0\\ u(\vec{x}, t)=\sin (\pi x_1)\sin (\pi x_2) e^{-t}, & (\vec{x}, t) \in \partial \Omega\times[0,1]\end{cases} tu(x ,t)Δu(x ,t)=(2π21)sin(πx1)sin(πx2)et,u(x ,t)=sin(πx1)sin(πx2)et,u(x ,t)=sin(πx1)sin(πx2)et,(x ,t)Ω×(0,1](x ,t)Ω×0(x ,t)Ω×[0,1]

其中, 空间区域 Ω : = [ 0 , 1 ] × [ 0 , 1 ] \Omega:=[0,1]\times[0,1] Ω:=[0,1]×[0,1], 其真实解为 u ( x ⃗ , t ) = sin ⁡ ( π x 1 ) sin ⁡ ( π x 2 ) e − t u(\vec{x}, t)=\sin (\pi x_1)\sin (\pi x_2) e^{-t} u(x ,t)=sin(πx1)sin(πx2)et .

下面演示基于 FEALPy 求解这个算例的过程.

from fealpy.pde.parabolic_model_2d import SinSinExpData
from fealpy.mesh import MeshFactory as MF
from fealpy.functionspace import LagrangeFiniteElementSpace
from fealpy.boundarycondition import DirichletBC
from fealpy.decorator import cartesian
from scipy.sparse.linalg import spsolve
import matplotlib.pyplot as plt

t_list = []
error = []
# 创建 PDE 模型
pde = SinSinExpData()
# 创建网格
domain = [0, 1, 0, 1]
mesh = MF.boxmesh2d(domain, nx=5, ny=5, meshtype='tri')
# 时间离散
timeline = pde.time_mesh(0, 1, 100)
# 建立 Lagrange 有限元函数空间
space = LagrangeFiniteElementSpace(mesh, p=2)
# 时间步进求解
for i in range(100):
    t = timeline.next_time_level()
    # 创建 Dirichlet 边界条件对象
    @cartesian
    def dirichlet(p):
        return pde.dirichlet(p, t)
    bc = DirichletBC(space, dirichlet)
    # 创建离散代数系统
    uh = space.function()
    A = space.stiff_matrix()
    @cartesian
    def source(p):
        return pde.source(p, t)
    F = space.source_vector(source)
    # 处理边界条件
    A, F = bc.apply(A, F, uh)
    # 求解离散代数系统
    uh[:] = spsolve(A, F)

    # 计算 L2 误差并保存
    @cartesian
    def solution(p):
        return pde.solution(p, t)
    L2Error = space.integralalg.L2_error(solution, uh)
    # print('t:/t', t, '/tL2Error:', L2Error)
    t_list.append(t)
    error.append(L2Error)
    # 前进一层
    timeline.forward()
# 绘制时间方向 L2 误差
plt.xlabel('t')
plt.ylabel('error')
plt.title('L2Error')
plt.plot(t_list, error)
plt.show()
# 绘制数值解图像和网格图像
fig = plt.figure()
axes = fig.add_subplot(1, 2, 1, projection='3d')
uh.add_plot(axes, cmap='rainbow')
axes = fig.add_subplot(1, 2, 2)
mesh.add_plot(axes)
plt.show()

数值结果:
Error
solution

本人水平有限, 若有错误, 欢迎联系批评指正

作者邮箱: turingscat@126.com

  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
在 MATLAB 中求解一维 Fick 扩散方程可以通过数值解法实现。首先需要定义方程的初始条件、边界条件和相关参数,然后利用数值模拟方法求解方程。下面是具体的步骤: 1.定义初始条件和边界条件。例如,假设我们要求解的是在一个 $L$ 长度的区域中,初始时物质浓度分布为 $c(x,0)$,左端点 $x=0$ 处物质浓度为 $c_0$,右端点 $x=L$ 处物质浓度为 $c_L$,则可以定义初始条件和边界条件为: $$ c(x,0) = c(x), \quad c(0,t) = c_0, \quad c(L,t) = c_L $$ 2.确定数值解法。根据 Fick 扩散方程的特点,可以选择离散化方法求解。其中最常用的方法是差分法和有限元法。在此简单介绍差分法。离散化后的方程可以表示为: $$ \frac{c_i^{n+1}-c_i^n}{\Delta t} = D\frac{c_{i+1}^n-2c_i^n+c_{i-1}^n}{\Delta x^2} $$ 其中,$c_i^n$ 表示在时间 $n$,位置 $x_i$ 处的物质浓度,$D$ 表示扩散系数,$\Delta x$ 和 $\Delta t$ 分别表示空间步长和时间步长。 3.编写 MATLAB 程序。根据上述方程,可以采用迭代方法求解。步骤包括初始化各个变量,确定时间和空间步长,然后进行迭代。具体的 MATLAB 代码如下: ```matlab % 定义常量和初始条件 D = 1.0; % 扩散系数 c0 = 1.0; % 左端点浓度 cL = 0.0; % 右端点浓度 T = 1.0; % 总时间 L = 1.0; % 区域长度 nx = 101; % 离散化的点数 dx = L/(nx-1); % 空间步长 dt = 0.0001; % 时间步长 nt = ceil(T/dt); % 时间步数 % 初始化网格和初始条件 x = linspace(0,L,nx); c = ones(1,nx)*c0; c(nx) = cL; % 迭代求解 for n = 1:nt c_new = c; for i = 2:nx-1 c_new(i) = c(i) + D*dt/dx^2*(c(i+1)-2*c(i)+c(i-1)); end c = c_new; end % 可视化结果 plot(x,c); xlabel('位置','fontsize',14); ylabel('浓度','fontsize',14); title(['Fick扩散方程的数值解,时间:',num2str(T)],'fontsize',14); ``` 4.运行程序并查看结果。运行程序后,可以看到在 $T=1$ 时刻的物质浓度分布图像。 总之,通过上述步骤可以用 MATLAB 求解一维 Fick 扩散方程

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图灵猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值