一、题目
本人新手入门,从最简单最基础的例子学习,不足之处请大家多多批评指正。
以何晓明老师的课件例题为例:例题三和例题四
【本篇】博客只讨论一次线性的基函数下,三种边界条件和三种范数误差。
【下篇】博客将讨论二次多项式基函数下,三种边界条件和三种范数误差。
二、程序功能
1.主文件:run_1D_linear.m
%% 初始化
clc,clear
Bound = 1; %选择边界条件,狄利克雷为1,纽曼为2,罗宾为3
N=32; % N是有限元个数
xx=[0:1:N];
h=1.0/N; % h是步长
xxx=xx.*h; % xxx是刻度
nn=10; % nn是积分精度
A=zeros(N+1,N+1);
B=zeros(N+1,1);
%% 古典解及其导数
uu = @(x) x.*cos(x);
duu = @(x) cos(x) - x.*sin (x);
%% 逐个循环,生成单元刚度矩阵,并且组装总刚度矩阵
for j = 1:N
x1=xxx(j);x2=xxx(j+1);
[AA,BB] = unit_1D_linear(N,h,j,x1,x2);
A(j:j+1,j:j+1)=AA+A(j:j+1,j:j+1);
B(j:j+1, 1 )=BB+B(j:j+1, 1 );
end
%% 边界条件
switch fix(Bound)
case 1
[A,B] = Dirichlet_1D(A,B,uu);
case 2
[A,B] = Neu_1D(A,B,uu);
case 3
[A,B] = Robin_1D(A,B,uu);
end
%% 求解,作图
u = inv(A) * B;
uu(xxx);
plot(xxx,u,'b-*',xxx,uu(xxx),'r-')
legend('数值解(蓝色虚线)','真实解(红色实线)','Location', 'best');
%% 误差,L_2范数,L_wq,H1_semi范数,最大节点误差
[L_2,L_wq,H1_semi,max_err_node]= error_1D_linear(u,uu,duu,xxx,nn);
2.单元刚度/载荷矩阵:unit_1D_linear.m
function [AA,BB] = unit_1D_linear(N,h,j,x1,x2)
% unit_1D_linear.m生成单个单元刚度/载荷矩阵
% 请修改为自己的c(x)
% AA是单元刚度矩阵
% BB是单元载荷矩阵
c = @(x) exp(x)*(1/h)*(1/h);
FB1 = @(x) -exp(x).*(cos(x)-2.*sin(x)-x.*cos(x)-x.*sin(x)).*(x2-x)*(1/h);
FB2 = @(x) -exp(x).*(cos(x)-2.*sin(x)-x.*cos(x)-x.*sin(x)).*(x-x1)*(1/h);
AA=zeros(2,2);
BB=zeros(2,1);
AA(1,1)= integral(c,x1,x2);
AA(1,2)= -integral(c,x1,x2);
AA(2,1)= -integral(c,x1,x2);
AA(2,2)= integral(c,x1,x2);
BB(1,1)= integral(FB1,x1,x2);
BB(2,1)= integral(FB2,x1,x2);
end
3.边界条件的处理
(1)Dirichlet_1D.m 处理狄利克雷边界条件
function [A,B] = Dirichlet_1D(A,B,uu)
% 一维问题的狄利克雷边界条件
A(1,:)=0;
A(1,1)=1;
A(end,:)=0;
A(end,end)=1;
B(1)=uu(0);
B(end)=uu(1);
end
(2)Neu_1D.m 处理纽曼边界条件
function [A,B] = Neu_1D(A,B,uu)
% 一维问题的纽曼边界条件
A(1,:)=0;
A(1,1)=1;
B(1)=uu(0);
%注意,因为我们选择一次线性“帽子”函数
%只有z最后一个基函数的内积不为零,在b点值为1,而不是为0
B(end)=B(end)+(cos(1)-sin(1)).*exp(1).*1 ;
end
(3)Robin_1D.m 处理罗宾边界条件
function [A,B] = Robin_1D(A,B,uu)
%一维问题的罗宾边界条件
A(end,:)=0;
A(end,end)=1;
B(end)=uu(1);
%注意,因为我们选择一次线性“帽子”函数
%只有第一个基函数及其内积,在a点值为1,而不是为0
A(1,1) = A(1,1)+1.*exp(0).*1.*1;
B(1) = B(1)-1.*exp(0).*1;
end
4.误差分析:error_1D_linear.m
function [L_2,L_wq,H1_semi,max_err_node]= error_1D_linear(u,uu,duu,Mesh,nn)
%% 节点误差
max_err_node = max(abs(u-uu(Mesh)'));%无穷范数为例
%% L_2范数,L_wq,H1_semi范数
L_2 = 0;
H1_semi =0;
L_wq = 0;
L_wq_Max = [2,1:size(Mesh,2)-1];
for k = 1 : size(Mesh,2)-1
% 每个有限元上,只有两个“帽子”函数(基函数)不为零
% L_2范数,用Uh乘以基函数,使数值解连续
% L_wq范数找每个区间最大差值,再找整个定义域上的最大值
u_L2 = @(x) -u( k ).*( x - Mesh(k+1) )./(Mesh(k+1) - Mesh(k)) ...
+u(k+1).*( x - Mesh( k ) )./(Mesh(k+1) - Mesh(k));
%注意,fminbn取的反而是极小值,绝对值加了负号
uu_L = @(x) -abs ( feval(u_L2,x) - feval(uu,x) ) ;
uuu_L2 = @(x) ( feval(u_L2,x) - feval(uu,x) ).^2;
u_Hhalf = @(x) (u(k+1) - u( k ))./(Mesh(k+1) - Mesh(k));
% H1_semi范数同理
uu_Hhalf = @(x) ( feval(u_Hhalf,x) - feval(duu,x) ).^2;
%存储每个区间的极大值
%注意,fminbn取的反而是极小值,绝对值加了负号
[L_wq_Max(1,k) , L_wq_Max(2,k)] = fminbnd(uu_L,Mesh(k),Mesh(k+1));
%分段数值积分
L_2 = L_2 + mysimp( uuu_L2 , Mesh(k+1) , Mesh(k) , nn);
H1_semi = H1_semi + mysimp( uu_Hhalf , Mesh(k+1) , Mesh(k) , nn);
end
%L_2和H1_semi开根号
L_2 = sqrt( L_2 );
H1_semi = sqrt(H1_semi);
%L_wq取最大值,注意,fminbn取的反而是极小值,这里去掉负号
L_wq = max ( -L_wq_Max(2,:));
%% 展示输出
format long;
fprintf('L_2范数:');
L_2
fprintf('L_wq范数:');
L_wq
fprintf('H1_semi范数:');
H1_semi
fprintf('节点最大误差:')
max_err_node
end
5.辛普森积分:mysimp.m
function I = mysimp(f,b,a,nn)
% 注释
% nn是子区间的个数,可以调节的精度;
%f为被积函数;
%a,b分别为积分的上下限;
%I是梯形总面积,即所求积分数值;
%feval('f',x)是泛函,例如sin(x),
%% 辛普森公式求积分
h=(b-a)/(2*nn);
s1=0;
s2=0;
for k=1:nn
x=a+h*(2*k-1);
s1=s1+feval(f,x);
end
for k=1:(nn-1)
x=a+h*2*k;
s2=s2+feval(f,x);
end
I = h*(feval(f,a)+feval(f,b)+4*s1+2*s2)/3;
end
三、结果与分析
1.对比三种边界条件的误差(h=4)
(1)狄利克雷边界条件
(2)纽曼边界条件
(3)罗宾边界条件
2.范数误差
(1)双对数图
(2)误差数值
3.小结
(1)我的L2和H1半范的结果都与标准答案精确符合,我的L∞范数与标准答案保持在同一个数量级,但是略大10%左右,通过图像分析,我猜想可能是我使用MATLAB内置函数fminbnd寻找极值,找到的最大误差可能更大,导致出现了一些偏差,但不清楚具体原因。
(2)为什么改变边界条件后,L2和L∞范数具有较大改变,H1半范几乎不变?请高手指点。
(3)有没有更简易的程序格式,更方便地调用基函数或导数?请高手指点?