针对温度场模型利用 f e a l p y fealpy fealpy包求解泊松方程
泊松方程:
△ u = ∂ 2 u ∂ x 2 + ∂ 2 u ∂ y 2 = f \bigtriangleup u = \frac{\partial^2u}{\partial x^2} +\frac{\partial^2u}{\partial y^2} =f △u=∂x2∂2u+∂y2∂2u=f
边界条件:
已知 f = 2 × 1 0 6 [ ( x − 0.1 2 ) 2 + ( y − 0.1 2 ) 2 ] − 10000 f = 2\times 10^6[(x-\frac{0.1}{2})^2 +(y-\frac{0.1}{2})^2 ]-10000 f=2×106[(x−20.1)2+(y−20.1)2]−10000
简化问题:将散热口所在的 y = 0 y=0 y=0边定义为 D i r i c h l e t Dirichlet Dirichlet边界条件
积分弱形式:
在方程两边同时乘以测试函数
v ∈ H 1 ( Ω ) , v ∣ Γ D = 0 \begin{matrix} v\in H^{1}(\Omega ), &v|_{\Gamma _{D} }=0 \end{matrix} v∈H1(Ω),v∣ΓD=0
并在 Ω \Omega Ω上积分:
∫ Ω f v d x d y = ∫ Ω △ u v d x d y \int\limits_{\Omega } fvdxdy=\int\limits_{\Omega }\bigtriangleup uvdxdy Ω∫fvdxdy=Ω∫△uvdxdy
分部积分:
使用分部积分公式(格林公式)可得
∫ Ω ∇ u ⋅ ∇ v d x d y = ∫ Ω △ u ⋅ n v d x d y + ∫ Ω f v d x d y \int\limits_{\Omega } \nabla u ·\nabla vdxdy= \int\limits_{\Omega }\bigtriangleup u·nvdxdy + \int\limits_{\Omega }fvdxdy Ω∫∇u⋅∇vdxdy=Ω∫△u⋅nvdxdy+Ω∫fvdxdy
将其在 Ω \Omega Ω的边界上表示就有:
∫ Ω ∇ u ⋅ ∇ v d x d y = ∫ Ω f v d x d y + ∫ Γ D △ u ⋅ n v d l + ∫ Γ N △ u ⋅ n v d l \int\limits_{\Omega } \nabla u· \nabla vdxdy= \int\limits_{\Omega } fvdxdy + \int\limits_{\Gamma _{D}}\bigtriangleup u·nvdl + \int\limits_{\Gamma _{N}}\bigtriangleup u·nvdl Ω∫∇u⋅∇vdxdy=Ω∫fvdxdy+ΓD∫△u⋅nvdl+ΓN∫△u⋅nvdl
利用边界条件:
v ∣ Γ D = 0 v|_{\Gamma_{D}}=0 v∣ΓD=0
∂ u ∂ n ∣ Γ N = 0 \frac{\partial u}{\partial n}\mid _{\Gamma_{N} } =0 ∂n∂u∣ΓN=0
可以得到:
∫ Ω ∇ u ⋅ ∇ v d x d y = ∫ Ω f v d x d y + ∫ Γ N g N v d l \int\limits_{\Omega } \nabla u· \nabla vdxdy= \int\limits_{\Omega } fvdxdy +\int\limits_{\Gamma _{N}}g_{N} vdl Ω∫∇u⋅∇vdxdy=Ω∫fvdxdy+ΓN∫gNvdl
代数系统:
离散后用代数系统表达可得:
A ⋅ u = b + b N A·u=b+b_{N} A⋅u=b+bN
其中:
A = ∫ Ω ∇ u h ⋅ ∇ v h d x d y = Σ e ∈ Γ ∫ e ∇ u h ⋅ ∇ v h d x d y A = \int\limits_{\Omega } \nabla u^{h} ·\nabla v^{h} dxdy= \underset{e\in \Gamma }{\Sigma } \int\limits_{e} \nabla u^{h} ·\nabla v^{h} dxdy A=Ω∫∇uh⋅∇vhdxdy=e∈ΓΣe∫∇uh⋅∇vhdxdy
b = ∫ Ω f ⋅ v h d x d y = Σ e ∈ Γ ∫ e f ⋅ v h d x d y b = \int\limits_{\Omega } f· v^{h} dxdy= \underset{e\in \Gamma }{\Sigma } \int\limits_{e} f· v^{h} dxdy b=Ω∫f⋅vhdxdy=e∈ΓΣe∫f⋅vhdxdy
b N = ∫ Γ N g N ⋅ v h d x d y = Σ e ∈ Γ ∫ e d g e g N ⋅ v h d x d y b_{N} = \int\limits_{\Gamma _{N} } g_{N}· v^{h} dxdy= \underset{e\in \Gamma }{\Sigma } \int\limits_{edge} g_{N}· v^{h} dxdy bN=ΓN∫gN⋅vhdxdy=e∈ΓΣedge∫gN⋅vhdxdy
下面是上述方程的程序求解过程,使用 fealpy 有限元程序包.
导入必要的包
from fealpy.functionspace import LagrangeFiniteElementSpace
from fealpy.mesh import MeshFactory
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.sparse.linalg import spsolve
from fealpy.boundarycondition import DirichletBC, NeumannBC
from fealpy.tools.show import showmultirate, show_error_table
使用python中的类来定义PDE
import numpy as np
from fealpy.decorator import cartesian,barycentric
class possion_solution:
def __init__(self):
pass
# 定义网格区域大小
@property
def domain(self):
return np.array([0, 0.1, 0, 0.1])
# 原项 f(x,y),泊松方程的右边
@cartesian
def source(self, p):
x = p[..., 0]
y = p[..., 1]
pi = np.pi
val = 2*pi*pi*np.cos(pi*x)*np.cos(pi*y)
return val
# 精确解u(x,y),泊松方程的左边
@cartesian
def exact_solution(self, p):
x = p[..., 0]
y = p[..., 1]
pi = np.pi
val = np.cos(pi*x)*np.cos(pi*y)
return val
# 真解的梯度
@cartesian
def gradient(self, p):
x = p[..., 0]
y = p[..., 1]
pi = np.pi
val = np.zeros(p.shape, dtype = np.float64)
val[..., 0] = -pi*np.sin(pi*x)*np.cos(pi*y)
val[..., 1] = -pi*np.cos(pi*x)*np.sin(pi*y)
return val
# 梯度的负方向
@cartesian
def flux(self, p):
return -self.gradient(p)
#定义边界条件
@cartesian
def is_dirichlet_boundary(self, p):
y = p[..., 1]
return (y == 0.0)
@cartesian
def dirichlet(self, p):
return self.exact_solution(p)
@cartesian
def is_neumann_boundary(self, p):
x = p[..., 0]
y = p[..., 1]
return (x == 0.0)|(x == 0.1)|(y == 0.1)
@cartesian
def neumann(self, p, n):
grad = self.gradient(p)
val = np.sum(grad * n, axis = -1)
return val
网格生成并可视化
import numpy as np
from fealpy.mesh import MeshFactory
import matplotlib.pyplot as plt
%matplotlib inline
# 加载pde模型
pde = possion_solution()
# 加载网格
mf = MeshFactory()
box = pde.domain
mesh = mf.boxmesh2d(box, nx = 10, ny = 10, meshtype = 'quad')
# 画图
figure = plt.figure()
axes = figure.gca()
mesh.add_plot(axes)
#mesh.find_node(axes, showindex = True)
#mesh.find_edge(axes, showindex = True)
mesh.find_cell(axes, showindex = True)
#获取单元,网格,节点信息
nodes = mesh.entity('node')
cells = mesh.entity('cell')
edges = mesh.entity('edge')
形成有限元空间
import sys
def possion_solution_solver(pde, n, refine, order):
"""
Input:
@pde: 定义偏微分方程
@n: 初始网格剖分段数
@refine: 网格加密的最大次数(迭代求解次数)
@order: 有限元多项式次数
Output: None
"""
mf = MeshFactory()
mesh = mf.boxmesh2d(pde.domain, nx = n, ny = n, meshtype = 'tri')
number_of_dofs = np.zeros(refine, dtype = mesh.itype)
# 建立空数组,目的把每组的自由度个数存下来
error_matrix = np.zeros((2, refine), dtype = mesh.ftype)
error_type = ['$||u - u^{h}||_{0}$', '$||\\nabla u - \\nabla u^{h}||_{0}$']
for i in range(refine):
femspace = LagrangeFiniteElementSpace(mesh, p = order)
number_of_dofs[i] = femspace.number_of_global_dofs()
uh = femspace.function()
# 返回一个有限元函数,初始自由度值全为 0
# A·u = b + b_n
A = femspace.stiff_matrix()
F = femspace.source_vector(pde.source)
# 先计算纽曼
bc = NeumannBC(femspace, pde.neumann, threshold = pde.is_neumann_boundary)
F = bc.apply(F)
#最后计算Dirichlet
bc = DirichletBC(femspace, pde.dirichlet, threshold = pde.is_dirichlet_boundary)
A, F = bc.apply(A, F, uh)
uh[:] = spsolve(A, F)
#计算误差
error_matrix[0, i] = femspace.integralalg.L2_error(pde.exact_solution, uh.value)
error_matrix[1, i] = femspace.integralalg.L2_error(pde.gradient, uh.grad_value)
print('插值点: ', femspace.interpolation_points().shape)
print('自由度数(NDof): ', number_of_dofs[i])
nodes = mesh.entity('node')
print('节点数: ', nodes.shape)
if i < refine - 1:
mesh.uniform_refine()
fig = plt.figure()
axes = fig.gca(projection = '3d')
uh.add_plot(axes, cmap = 'rainbow')
showmultirate(plt, 0, number_of_dofs, error_matrix, error_type, propsize = 20)
show_error_table(number_of_dofs, error_type, error_matrix, f='e', pre=4, sep=' & ', out=sys.stdout, end='\n')
plt.show()
使用线性元求解
possion_solution_solver(possion_solution(), 10, 5, 1)
使用二次元求解
possion_solution_solver(possion_solution(), 10, 5, 2)
def is_dirichlet_boundary(self, p):
x = p[..., 0]
y = p[..., 1]
return (y == 0.0) & ( x a)
@cartesian
def dirichlet(self, p):
val = vall * np.ones(len(p))
return val
增加:
每次print都会有几个附带的值,以两个元组为例,在Mesh2d.py文件中找到face_unit_normal()函数,里面去掉print即可,可以减少时间,感觉还是比较慢