常用守恒格式求解双曲问题(Lax-Friedrichts、local Lax-F、Roe、Engquist-Osher、Godunov、Lax-Wendroff)
2017-12-09 03:15
1476 查看
Burgers方程的守恒格式求解(Proj No.1)
陆嵩 计算数学
分别使用不同的守恒格式求解,包括Lax-Friedrichs、Roe(迎风)、Engquist-Osher、Godunov格式和Lax-Wendorff格式。并且要求:
计算各个格式在t=0.15时的误差和收敛阶。
画出t=0.5时的精确解和数值解的对比图,每种格式单独画一个图。
x1−x0t1=u0(x0)=sin(πx0+π).
求得x0后,我们可以得到在(x1,t1)点出的值为u0(x0)。
需要注意的是,matlab的solve函数主要是用于多项式求根,对于非线性方程来说,也可以求,但是往往只给出一个最小解。在matlab里面solve命令主要是用来求解代数方程(即多项式)的解,但是也不是说其它方程一个也不能解,不过求解非代数方程的能力相当有限,通常只能给出很特殊的实数解。(该问题给出的方程就是典型的超越方程,非代数方程),在t接近0.5时(比如说t=0.4),x在原点附近(比如x=0.01),就会解错。所以改用fzero函数。
这里的λ=△t△x,△t和△x分别为时间步长和空间步长。
下面提到的几种格式,也是基于这样的一个守恒格式写法,只是数值通量函数f^有所不同。
其中, αn=maxun|f′(u)|
其中, αnj+12=maxu∈(unj,unj+1)|f′(u)|
f^j+12=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪f(uj)f(uj+1)f(uj+1)−f(uj)uj+1−uj≥0f(uj+1)−f(uj)uj+1−uj<0
f+(u)=f−(u)=∫u0max(f′(v),0)dv+f(0)∫u0min(f′(v),0)dv 对于Burgers方程而言,有 f+(u)=={f(u)0u≥0u<0 对于Burgers方程而言,有 f−(u)=={f(u)0u≤0u>0
f^j+12=12[f(uj)+f(uj+1)−λf′(uj+12)(f(uj+1)−f(uj))]
这里的λ含义同上,uj+12的值可以通过插值得到,我选的是线性插值,即,uj+12=12(uj+uj+1)。
{width=”12cm”}\
{width=”12cm”}\
{width=”12cm”}\
{width=”12cm”}\
{width=”12cm”}\
{width=”12cm”}\
一点小感悟:
一阶收敛的数值格式是很容易实现的,即使在编程过程中,那个小地方写错了,也可能会达到一阶。也就是说,一阶格式有一阶收敛程序不一定是对的,但是没有一阶收敛,那么肯定是错的。
试图将这些程序写成通用的解决一阶标量双曲守恒方程的形式,也就是说只是通量函数
f=12u2
改变,只要在fun函数脚本中修改通量函数,那么这些格式依然能用。有一个小问题在于,写成通用的,多了一些求导等不不要的计算,加大了运算量。在ENO和Godnov格式中,没有写成通用格式。主要考虑到:1、ENO格式中积分的计算,若对于一般的通量函数f,正负部截断函数的原函数不一定能求,那么这是可能要用到一些数值积分手段来求近似积分,这对Burgers方程是多余的,引入了额外误差。2、Godunov格式中,若考虑Matlab内置的fminbnd函数来求最大值最小值,无疑引入了多余的误差,以及大大降低了求解速度,所以,这里针对Burgers函数的特殊性,针对性地求解了相对应的数值通量函数。不具普适性和泛化能力。
在求解真解时,利用了特征线。在求解非线性方程根时,不能用solve函数,而应该fzero函数。因为solve函数用于求解多项式的零点尚可,当它在求解稍微复杂一点的非线性方程的零点时,若方程有多个零点,它往往只能给你找个一个零点,而这个零点未必是我们说要的那个,这样就容易出问题。
一个高阶收敛的格式,刚开始的时候收敛阶可能表现得不大稳定,比如上面提到Lax-Wendroff格式,它的收敛阶一会1.6一会儿2.3,但是随着网格的加密,它往往会稳定到它真正的收敛阶。如下所示:
陆嵩 计算数学
问题描述
我们对下述Burgers方程的初边值问题 ⎧⎩⎨⎪⎪⎪⎪ut+(u2/2)x=0u(x,0)=u0(x)=sin(πx+π)periodic B.C. for x−1≤x≤1,t>0分别使用不同的守恒格式求解,包括Lax-Friedrichs、Roe(迎风)、Engquist-Osher、Godunov格式和Lax-Wendorff格式。并且要求:
计算各个格式在t=0.15时的误差和收敛阶。
画出t=0.5时的精确解和数值解的对比图,每种格式单独画一个图。
关键算法格式
精确解
对于如([q])所示的黎曼问题的精确解,我们用特征线方法来求解精确值。过某个初值点(x0,0)的特征线是一根直线,它的斜率为u0(x),那么给定一点(x1,t1),我们知道它满足,x1−x0t1=u0(x0)=sin(πx0+π).
求得x0后,我们可以得到在(x1,t1)点出的值为u0(x0)。
需要注意的是,matlab的solve函数主要是用于多项式求根,对于非线性方程来说,也可以求,但是往往只给出一个最小解。在matlab里面solve命令主要是用来求解代数方程(即多项式)的解,但是也不是说其它方程一个也不能解,不过求解非代数方程的能力相当有限,通常只能给出很特殊的实数解。(该问题给出的方程就是典型的超越方程,非代数方程),在t接近0.5时(比如说t=0.4),x在原点附近(比如x=0.01),就会解错。所以改用fzero函数。
常用守恒格式
常用的守恒格式的写法为: un+1j=unj−λ(f^nj+12−f^nj−12)这里的λ=△t△x,△t和△x分别为时间步长和空间步长。
下面提到的几种格式,也是基于这样的一个守恒格式写法,只是数值通量函数f^有所不同。
Lax-Friedrichs格式
Lax-Friedrichs格式的数值通量函数表达式为: fnj+12=12[f(unj)+f(unj+1)−αn(unj+1−unj)]其中, αn=maxun|f′(u)|
Local Lax-Friedrichs格式
Local Lax-Friedrichs格式的数值通量函数表达式为: fnj+12=12[f(unj)+f(unj+1)−αnj+12(unj+1−unj)]其中, αnj+12=maxu∈(unj,unj+1)|f′(u)|
Roe格式
Roe(迎风)格式的数值通量函数为(为了方便省略了第n时间层上标,下如是):f^j+12=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪f(uj)f(uj+1)f(uj+1)−f(uj)uj+1−uj≥0f(uj+1)−f(uj)uj+1−uj<0
Engquist-Osher格式
数值通量函数的表达式为: f^j+12=f+(uj)+f−(uj+1) 其中,f+(u)=f−(u)=∫u0max(f′(v),0)dv+f(0)∫u0min(f′(v),0)dv 对于Burgers方程而言,有 f+(u)=={f(u)0u≥0u<0 对于Burgers方程而言,有 f−(u)=={f(u)0u≤0u>0
Godunov格式
对于Godunov格式,它的数值通量表达式为: f^j+12=⎧⎩⎨⎪⎪maxuj≤u≤uj+1f(u)minuj≤u≤uj+1f(u)uj≤uj+1uj≥uj+1 对于Burgers方程而言,有 f^j+12=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪f(uj+1)f(uj)f(max(|uj|,|uj+1|))0uj≤0,uj+1≤0uj≥0,uj+1≥0uj>0,uj+1<0uj<0,uj+1>0Lax-Wendroff格式
Lax-Wendroff格式的数值通量表达式为:f^j+12=12[f(uj)+f(uj+1)−λf′(uj+12)(f(uj+1)−f(uj))]
这里的λ含义同上,uj+12的值可以通过插值得到,我选的是线性插值,即,uj+12=12(uj+uj+1)。
数值实验与结果
对以上提到的各个格式,分别做以下工作:1、计算各个格式在t=0.15时的误差和收敛阶。2、画出t=0.5时的精确解和数值解的对比图,每种格式单独画一个图。Lax-Friedrichs格式
{width=”12cm”}\
Local Lax-Friedrichs格式
{width=”12cm”}\
Roe格式
{width=”12cm”}\
Engquist-Osher格式
{width=”12cm”}\
Godunov格式
{width=”12cm”}\
Lax-Wendroff格式
从表中可以看出,Lax-Wendroff格式格式可能是二阶收敛的。事实上,刚开始时收敛阶可能表现得不那么稳定,但是随着步长的缩短,这个格式收敛阶最终是稳定在二阶的。{width=”12cm”}\
程序源码
源代码,使用的语言为MAMTLAB2014a(盗版),Windows 10环境。% %% 真解绘图 % for t = 0:0.1:0.6 % x = -1:0.01:1; % u = ExSolu(x,t); % figure(1); % plot(x,u); % title(['t=' num2str(t)]); % drawnow; % end %% %% 1. Set initial value; run the solver; plot the solution clc clear close all scheme = 'LaxW';%选择格式 LaxF LocLaxF Roe EqOsher Godunov LaxW Nt = 5;%设置空间步 t0 = 0; tf = 0.5; dt = (tf - t0)/Nt; x1 = -1; x2 = 1; r = 0.5; dx = dt/r;%网格比设置为0.5 xx = x1:dx:x2;%空间划分 %u0 = ExSolu(xx, 0);%求解初值,离散初值满足真解,也可以取点生成 u0 = -sin(pi*xx); %plot(xx,u0,'color','r');hold on;ezplot('-sin(pi*x)',xx) uex = ExSolu(xx, tf);%tf时间后的真值 u = eval([scheme '(u0, xx, dx, dt, Nt)']); plot(xx, uex, 'r-', xx, u, 'g-'); ymin = min(u) - 0.1; ymax = max(u) + 0.1; axis([x1, x2, ymin, ymax]); xlabel('x'); ylabel('u'); legend('Exact Solution', scheme, 'Location', 'SouthWest'); title([scheme ' scheme: t=' num2str(tf)]); %% 误差计算 ne = 5;%次数 E = zeros(ne,1); Order=zeros(ne,1);%误差和收敛阶初始化 for in = 1:ne Nt = 30*2^(in-1);%时间步长两倍增长 t0 = 0; tf = 0.15; dt = (tf - t0)/Nt; x1 = -1; x2 = 1; r = 0.5; dx = dt/r; xx = x1:dx:x2; %u0 = ExSolu(xx, 0); u0 = -sin(pi*xx); uex = ExSolu(xx, tf); %u = LaxF(u0, xx, dx, dt, Nt); u = eval([scheme '(u0, xx, dx, dt, Nt)']); E(in) = sum(abs(u-uex)) * dx;%计算L1误差 if in>1 Order(in) = log(E(in-1)/E(in))/log(2); end end function f = fun( u ) %UNTITLED3 此处显示有关此函数的摘要 % 此处显示详细说明 f = 1/2*u.^2; end function u = ExSolu( x, t ) %计算精确解,这里x支持向量输入,输出多个精确解 syms x0; %u = zeros(size(x));%初始化u u0 = sin(pi*x0+pi); f = (x - x0) - t*u0; % ezplot(f); % hold on; % syms y; % ezplot(0*y); x0_ex = zeros(1,length(x)); for i = 1:length(x) % figure(2); % ezplot(f(i)); % drawnow; %x0_ex(i) = double(solve(f(i))); f_fun = matlabFunction(f(i)); if x(i) > 0 x0_ex(i) = fzero(f_fun,1); else x0_ex(i) = fzero(f_fun,-1); end if(abs(x0_ex)>1) error('计算范围超出初值'); end %x0_ex(i) = eval(x0_ex(i)); end u = double(subs(u0,x0,x0_ex));%真解计算 end function u0 = LaxF(u0, xx, dx, dt, Nt) % LaxF(u0, xx, dx, dt Nt): u0-initial values; xx-coordinates; syms u;lambda = dt/dx; nx = length(u0);%空间节点数 %f_hat = zeros(1,nx-1); f_diff = diff (fun(u),u); for it = 1:Nt t = it*dt;%时间标记 alpha = max(abs(double(subs(f_diff,u,u0))));%求解alpha,此写法便于通量函数修改后也能使用 f_hat = 1/2*( fun(u0(2:end))+fun(u0(1:end-1)) - alpha*(u0(2:end)-u0(1:end-1)) ); un = zeros(1,nx); un(2:end-1) = u0(2:end-1) - lambda*(f_hat(2:end) - f_hat(1:end-1)); un(1) = ExSolu(-1,t);%使用真解来求边界条件,其实是周期边界都为0 un(nx) = ExSolu(1,t); u0 = un; end function u0 = LocLaxF(u0, xx, dx, dt, Nt) % LaxF(u0, xx, dx, dt Nt): u0-initial values; xx-coordinates; syms u;lambda = dt/dx; nx = length(u0);%空间节点数 %f_hat = zeros(1,nx-1); f_diff = diff (fun(u),u); for it = 1:Nt t = it*dt;%时间标记 f_diff_abs = abs(double(subs(f_diff,u,u0))); alpha = max(f_diff_abs(1:end-1),f_diff_abs(2:end));%求解alpha,此写法便于通量函数修改后也能使用 f_hat = 1/2*( fun(u0(2:end))+fun(u0(1:end-1)) - alpha.*(u0(2:end)-u0(1:end-1)) ); un = zeros(1,nx); un(2:end-1) = u0(2:end-1) - lambda*(f_hat(2:end) - f_hat(1:end-1)); un(1) = ExSolu(-1,t);%使用真解来求边界条件,其实是周期边界都为0 un(nx) = ExSolu(1,t); u0 = un; end function u0 = Roe(u0, xx, dx, dt, Nt) lambda = dt/dx; nx = length(u0);%空间节点数 for it = 1:Nt t = it*dt;%时间标记 a = (fun(u0(2:end))-fun(u0(1:end-1)))./(u0(2:end) - u0(1:end-1)); ind_pick = (1:nx-1) + double((a < 0)); f_hat = fun(u0(ind_pick)); un = zeros(1,nx); un(2:end-1) = u0(2:end-1) - lambda*(f_hat(2:end) - f_hat(1:end-1)); un(1) = ExSolu(-1,t);%使用真解来求边界条件,其实是周期边界都为0 un(nx) = ExSolu(1,t); u0 = un; end function u0 = EqOsher(u0, xx, dx, dt, Nt) % LaxF(u0, xx, dx, dt Nt): u0-initial values; xx-coordinates; syms u;lambda = dt/dx; nx = length(u0);%空间节点数 %f_hat = zeros(1,nx-1); %f_diff = diff (fun(u),u); %fdf_abs_int = int(f_diff,u); %fun_dfint = matlabFunction(fdf_abs_int); for it = 1:Nt t = it*dt;%时间标记 f_plus = zeros(1,nx); f_minus = zeros(1,nx); index_plus = find(u0 > 0); index_minus = find(u0 < 0); f_plus(index_plus) = 1/2*u0(index_plus).^2; f_minus(index_minus) = 1/2*u0(index_minus).^2; %这里利用了burgers方程的特殊性,求到了其原函数。一般双曲守恒方程,这个部分得改 %导函数的正负部截断的原函数不一定能求得,需要利用数值积分手段求积分 f_hat = f_plus(1:end-1) + f_minus(2:end); % f_p = zeros(1,nx); % f_m = zeros(1,nx); % for i = 1:nx % if u0(i)>0 % f_p(i) = 1/2*(u0(i))^2; % else % f_m(i) = 1/2*(u0(i))^2; % end % end % diff_f_max = max(dfun(u0),0); % dfmax_ave = (diff_f_max(1:end-1) + diff_f_max(2:end))/2; % cumsum_dfmax_ave = cumsum(dfmax_ave); % int_temp = cumsum_dfmax_ave*dx; % f_plus = [0 int_temp(1:end-1)] + fun(0); % diff_f_min = min(dfun(u0),0); % dfmin_ave = (diff_f_min(1:end-1) + diff_f_min(2:end))/2; % cumsum_dfmin_ave = cumsum(dfmin_ave); % f_minus = cumsum_dfmin_ave*dx; % f_hat = f_plus + f_minus; % int_absdf = fun_dfint(u0(2:end)) - fun_dfint(u0(1:end-1)); % f_hat = 1/2*(fun(u0(2:end))+fun(u0(1:end-1))) - abs(1/2*(int_absdf)) ; un = zeros(1,nx); un(2:end-1) = u0(2:end-1) - lambda*(f_hat(2:end) - f_hat(1:end-1)); un(1) = ExSolu(-1,t);%使用真解来求边界条件,其实是周期边界都为0 un(nx) = ExSolu(1,t); u0 = un; end function u0 = Godunov(u0, xx, dx, dt, Nt) % Godunov(u0, xx, dx, dt Nt): u0-initial values; xx-coordinates; lambda = dt/dx; nx = length(u0);%空间节点数 for it = 1:Nt t = it*dt;%时间标记 f_hat = zeros(1,nx-1); % flag = double(u0(1:end-1) > u0(2:end)); % sign = (-1).^flag; % for i = 1:nx-1 % f_hat(i) = sign(i)*fun(fminbnd(@(u)(sign(i)*fun(u)),u0(i+flag(i)),u0(i+1-flag(i)))); % end % for i = 1:nx-1 % if(u0(i)<=u0(i+1)) % f_hat_com(i) = fun(fminbnd(@fun,u0(i),u0(i+1))); % else % f_hat_com(i) = -fun(fminbnd(@(u)(-1)*fun(u),u0(i+1),u0(i))); % end % % end % f_hat_com - f_hat vpa = 0; for i = 1:nx-1 if(u0(i)<=-vpa&&u0(i+1)<=-vpa) f_hat(i) = fun(u0(i+1)); else if(u0(i)>=vpa&&u0(i+1)>=vpa) f_hat(i) = fun(u0(i)); else if (u0(i)>vpa&&u0(i+1)<-vpa) f_hat(i) = fun( max( abs(u0(i)),abs(u0(i+1)) ) ); end end end end un = zeros(1,nx); un(2:end-1) = u0(2:end-1) - lambda*(f_hat(2:end) - f_hat(1:end-1)); un(1) = ExSolu(-1,t);%使用真解来求边界条件,其实是周期边界都为0 un(nx) = ExSolu(1,t); u0 = un; end function u0 = LaxW(u0, xx, dx, dt, Nt) % LaxF(u0, xx, dx, dt Nt): u0-initial values; xx-coordinates; syms u;lambda = dt/dx; nx = length(u0);%空间节点数 %f_hat = zeros(1,nx-1); f_diff = diff (fun(u),u); dfun = matlabFunction(f_diff); for it = 1:Nt t = it*dt;%时间标记 u0_bar = ( u0(1:end-1)+u0(2:end)) /2.0 ; f_hat = 1/2* ( fun(u0(2:end)) + fun(u0(1:end-1)) - lambda*dfun(u0_bar).*... (fun(u0(2:end))-fun(u0(1:end-1))) ); un = zeros(1,nx); un(2:end-1) = u0(2:end-1) - lambda*(f_hat(2:end) - f_hat(1:end-1)); un(1) = ExSolu(-1,t);%使用真解来求边界条件,其实是周期边界都为0 un(nx) = ExSolu(1,t); u0 = un; end
一点小感悟:
一阶收敛的数值格式是很容易实现的,即使在编程过程中,那个小地方写错了,也可能会达到一阶。也就是说,一阶格式有一阶收敛程序不一定是对的,但是没有一阶收敛,那么肯定是错的。
试图将这些程序写成通用的解决一阶标量双曲守恒方程的形式,也就是说只是通量函数
f=12u2
改变,只要在fun函数脚本中修改通量函数,那么这些格式依然能用。有一个小问题在于,写成通用的,多了一些求导等不不要的计算,加大了运算量。在ENO和Godnov格式中,没有写成通用格式。主要考虑到:1、ENO格式中积分的计算,若对于一般的通量函数f,正负部截断函数的原函数不一定能求,那么这是可能要用到一些数值积分手段来求近似积分,这对Burgers方程是多余的,引入了额外误差。2、Godunov格式中,若考虑Matlab内置的fminbnd函数来求最大值最小值,无疑引入了多余的误差,以及大大降低了求解速度,所以,这里针对Burgers函数的特殊性,针对性地求解了相对应的数值通量函数。不具普适性和泛化能力。
在求解真解时,利用了特征线。在求解非线性方程根时,不能用solve函数,而应该fzero函数。因为solve函数用于求解多项式的零点尚可,当它在求解稍微复杂一点的非线性方程的零点时,若方程有多个零点,它往往只能给你找个一个零点,而这个零点未必是我们说要的那个,这样就容易出问题。
一个高阶收敛的格式,刚开始的时候收敛阶可能表现得不大稳定,比如上面提到Lax-Wendroff格式,它的收敛阶一会1.6一会儿2.3,但是随着网格的加密,它往往会稳定到它真正的收敛阶。如下所示:
相关文章推荐
- 间断初值双曲守恒问题的Lax-Friedrichs和后向欧拉数值解法
- 参数化、网格变形中常用最小二乘问题求解
- C++实现常用的平面计算几何问题求解
- 改进的欧拉格式求解一阶微分的初值问题
- C语言初步-第39讲:问题求解——求素数(输出格式控制)
- 用显式欧拉格式和改进的欧拉格式求解常微分初值问题
- 八皇后问题回溯求解
- 关于返回json数据格式的问题
- 大白书 1.2节 问题求解常见策略
- Windows Server2008+IIS7部署网站的日期格式问题
- Servlet中web.xml 配置问题,求解!!!
- Spring+SpringMvc+MyBatis(Hibernate) 中关于时间格式的问题总结
- IO 流读取文件时候出现乱码 文件编码格式问题 怎么转换解决方法
- 【第6周 项目6 - 求解8皇后问题的程序】
- 分治法求解最大子数组问题
- “别赌运气”——高级职位面试和应聘的常用问题
- ssm后台接收前台Date类型参数格式的问题
- 记录一个decimal格式转换出现的问题
- 高级职位面试和应聘的常用问题
- sar 命令行的常用格式