非线性变分问题之FEniCS求解

【本文仅是视频中Markdown文件内容,不包括视频中讲解】

如果你希望和我一样Win10上安装FEniCS,建议采用Win10 + WSL2 + Ubuntu。

非线性变分问题之FEniCS求解

  • 第1讲 有限元法求解偏微分方程之FEniCS入门讲解
  • (上一讲)第2讲 混合形式泊松方程之FEniCS求解

本讲要点

  • 非线性变分问题的表述
  • 变分问题的离散化
  • 求解非线性泊松方程

非线性变分问题

前面2讲都是线性偏微分方程。变换得到线性变分问题:寻求\(u\in V\),满足 \[ a(u, v) = L(v) \qquad \forall v\in \hat{V} \] 类似地,非线性变分问题表述为:寻求\(u\in V\),满足 \[ \textcolor{red}{F(u; v)} = 0 \qquad \forall v\in \hat{V} \] 我们的目标是:将这个非线性的变分问题,从局部转换为线性问题。​

变分问题的离散化

要解决上面这个目标,首先不妨将变分问题离散化,这样可以看清楚之间的关系。

我们通过限制一对离散的试探和测试空间来离散化此变分问题,寻求\(\textcolor{red}{u\in V_h} \subset V\),满足 \[ F(u_h; v) = 0 \qquad \forall \textcolor{red}{v\in \hat{V}_h} \subset \hat{V} \] 其中,\(F(\textcolor{red}{u_h;}v)\)是一个半线性形式,分号后的参数中是线性的。

可将离散试探函数\(u_h\)展开为(同时离散测试函数\(v\)可取): \[ \boxed{u_h = \sum^N_{j=1} U_j\phi_j} \\ v = \hat{\phi}_i,\quad i=1,\dots,N \] 其中:\(\{\phi_j\}^N_{j=1}\)是离散试探空间\(V_h\)的基底;\(\{\hat{\phi}_i\}^N_{i=1}\)则是离散测试空间\(\hat{V}_h\)的基底。

带入到前面这个离散化变分问题,得到一个关于N元变量非线性方程组: \[ b_i(U_1,\dots,U_N)\overset{\Delta}{=}F(u_h; \hat{\phi}_i) = 0,\quad i=1,\dots,N \] 或写成矢量方程的形式: \[ b(U)=0 \] 可用牛顿法或牛顿法的某些变体来求解这种非线性方程组。为此,要计算雅可比矩阵\(A=b'\)

我们注意到,如果半线性形式\(F\)\(u\)是可微的,那么雅可比矩阵\(A\)的各项可以写出: \[ A_{ij}(u_h)=\frac{\partial b_i(U)}{\partial U_i}=\frac{\partial}{\partial U_i}F(u_h;\hat{\phi}_i)=F'(u_h;\hat{\phi}_i)\phi_j\overset{\Delta}{=}F'(u_h;\textcolor{red}{\phi_j,\hat{\phi}_i}) \] 我们注意到:对每个固定的\(u_h\)\(a=F'(u_h;\cdot,\cdot)\)是双线性形式,并且\(L=F(u_h;\cdot)\)是线性形式。

因此,在每次牛顿迭代中,我们都是求解标准形式的线性变分问题:需求\(\textcolor{red}{\delta u \in V_{h,0}}\),满足 \[ a(\delta u,v) = L(v) \qquad \forall v\in \hat{V}_h \\ a(\delta u,v) = F'(u_h;\delta u,v) \\ L(v) = -F(u_h;v) \] 其中,\(\delta u\)是每次迭代的更新增量;\(V_{h,0}=\{v-w:v,w\in V_h\}\)

【结论】

  • 1)线性变分问题,在离散化后,就是求解一个线性方程组。
  • 2)非线性变分问题,在离散化后,得到非线性方程组。可用牛顿法时,每次迭代的更新增量,实际上就是一个线性变分问题

非线性泊松方程的求解

例如,考虑以下非线性泊松方程 \[ -\nabla\cdot[\textcolor{red}{(1+u)}\nabla u] = f \quad \text{in } \Omega \\ u|_{\Gamma_D}=u_0 \qquad \text{ on } \Gamma_D\\ \left.\frac{\partial u}{\partial n}\right|_{\Gamma_N}=g \qquad \text{ on } \Gamma_N \] 首先,按第1讲的套路,变换成 变分方程: \[ \int_\Omega (1+u)\nabla u \cdot \nabla v dx = \int_\Omega f v dx + \int_{\Gamma_N} (1+u)g v ds \] 写成前面非线性变分问题形式的等式左边: \[ \boxed{F(u;v) = \int_\Omega (1+u)\nabla u \cdot \nabla v dx - \int_\Omega f v dx - \int_{\Gamma_N} (1+u)g v ds} \] 输入函数,域和边界的设定:

  • \(\Omega=[0,1]×[0,1]\) (一个单位矩形)
  • \(\Gamma_D=\{(1,y)\subset\partial \Omega\}\)(Dirichlet边界)
  • \(\Gamma_N=\{(x,0)\cup(x,1)\cup(0,y)\subset\partial \Omega\}\) (Neumann边界)
  • \(g=0\) (法向导数)
  • \(f=x\sin(y)\) (源项)

于是就可以写代码求解了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from dolfin import *
# 第一步:为求解域创建网格,并定义函数空间
mesh = UnitSquareMesh(32, 32)
# V = FunctionSpace(mesh, "Lagrange", 1)
V = FunctionSpace(mesh, "CG", 1)

# 第二步:定义已提供的表达式
u0 = Constant(0.0)
f = Expression("x[0]*sin(x[1])", degree=2)
g = Constant(0.0)

# 第三步:创建和应用基本边界条件
#def boundary(x, on_boundary):
# return on_boundary and abs(x[0] - 1.0) < DOLFIN_EPS
#bc = DirichletBC(V, u0, boundary)
# 为基本边界条件定义子域
class DirichletBoundary(SubDomain):
def inside(self, x, on_boundary):
return on_boundary and abs(x[0] - 1.0) < DOLFIN_EPS
bc = DirichletBC(V, u0, DirichletBoundary())

# 第四步:定义变分问题
# u = TrialFunction(V)
u = Function(V)
v = TestFunction(V)
F = inner((1+u)*grad(u), grad(v))*dx - f*v*dx - (1+u)*g*v*ds

# 第五步:求解并绘图
#u = Function(V) 
solve(F == 0, u, bc,
solver_parameters={"newton_solver":{"relative_tolerance":1e-6}})
plot(u, title="Solution")
plot(grad(u), title="Solution gradient")
# 将解保存为VTK格式
file = File("nonlinear_poisson.pvd")
file << u

END