利用Python中的fealpy包求解泊松方程

针对温度场模型利用 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=x22u+y22u=f

边界条件:

  • 恒温条件(散热口): u = T 0 = 298 K u = T_{0} =298K u=T0=298K
  • 绝热条件(其余边): ∂ u ∂ n = 0 \frac{\partial u}{\partial n}=0 nu=0

已知 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[(x20.1)2+(y20.1)2]10000

简化问题:将散热口所在的 y = 0 y=0 y=0边定义为 D i r i c h l e t Dirichlet Dirichlet边界条件

  • 定义问题:
    { △ u = f i n Ω u = g D o n Γ D ∂ u ∂ n = 0 o n Γ N \left\{\begin{matrix} \triangle \begin{matrix} u=f & in\Omega \end{matrix} \\ \begin{matrix} u=g_{D} & on\Gamma _{D} \end{matrix} \\ \begin{matrix} \frac{\partial u}{\partial n} =0 & on\Gamma _{N} \end{matrix} \end{matrix}\right. u=finΩu=gDonΓDnu=0onΓN
    求解区域:
    Ω = [ 0 , 0.1 ] × [ 0 , 0.1 ] \Omega = \left [ 0,0.1 \right ] \times \left [ 0,0.1 \right ] Ω=[0,0.1]×[0,0.1]
    边界条件:
  1. 在y=0上: D i r i c h l e t Dirichlet Dirichlet边界条件
  2. 在y=0.1,x=0,x=0.1: N e u m a n n Neumann Neumann边界条件
  • 积分弱形式:
    在方程两边同时乘以测试函数
    v ∈ H 1 ( Ω ) , v ∣ Γ D = 0 \begin{matrix} v\in H^{1}(\Omega ), &v|_{\Gamma _{D} }=0 \end{matrix} vH1(Ω),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 Ωuvdxdy=Ωunvdxdy+Ω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 Ωuvdxdy=Ωfvdxdy+ΓDunvdl+ΓNunvdl

    利用边界条件:

  • v ∣ Γ D = 0 v|_{\Gamma_{D}}=0 vΓD=0

  • ∂ u ∂ n ∣ Γ N = 0 \frac{\partial u}{\partial n}\mid _{\Gamma_{N} } =0 nuΓ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 Ωuvdxdy=Ωfvdxdy+ΓNgNvdl

  • 代数系统:
    离散后用代数系统表达可得:
    A ⋅ u = b + b N A·u=b+b_{N} Au=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=Ωuhvhdxdy=eΓΣeuhvhdxdy
    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=Ωfvhdxdy=eΓΣefvhdxdy
    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=ΓNgNvhdxdy=eΓΣedgegNvhdxdy

下面是上述方程的程序求解过程,使用 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)

改进方案:

  • 将Dirichlet边界从y==0缩减到:y == 0&x∈(a,b)
    这时候需要将边界条件改动
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即可,可以减少时间,感觉还是比较慢

你可能感兴趣的:(possion,python,深度学习,算法)