DG半离散格式转换微分方程组
基于下面四种类型的例子,来编写matlab程序进行转换:
例1、标量守恒方程的转换
如下图为标量守恒律的形式:
通过Galerkin法转化为下面的变分形式:
其中的the global Lax-Friedrichs flux:
matlab代码如下:
clear all;clc
syms uh_dt x j x_jleft12 x_jright12 h u0_j u1_j u2_j
x_jright12 = x_jleft12+h;
xj=(x_jleft12+x_jright12)/2;
%基函数的选定
% v=x.^0;%v=1
% v=2*(x-xj)/h;
v=(6*(x-xj).^2)/(h.^2)-1/2;
dv=diff(v,'x',1);
%编写基函数
q0=1;
q1=2*(x-xj)/h;
q2=(6*(x-xj).^2)/(h.^2)-1/2;
f(x)=x;
u=u0_j*q0+u1_j*q1+u2_j*q2;%u由基函数表出
re_u_dt=int(uh_dt*v.^2,x,x_jleft12,x_jright12);
a=0.5;%设置the global Lax-Friedrichs flux的a
fj_left12=0.5*(f(SpendU(-1,j-1/2,j,0))+f(SpendU(1,j-1/2,j,0))-a*(SpendU(1,j-1/2,j,0)-SpendU(-1,j-1/2,j,0)));
fj_right12=0.5*(f(SpendU(-1,j+1/2,j,0))+f(SpendU(1,j+1/2,j,0))-a*(SpendU(1,j+1/2,j,0)-SpendU(-1,j+1/2,j,0)));
re=(int(f(u)*dv,x,x_jleft12,x_jright12)+fj_left12*SolveV(1,j-1/2,v,0)-fj_right12*SolveV(-1,j+1/2,v,0))/diff(re_u_dt,'uh_dt');
re=expand(re);%u_dt 上标由v的选择决定
pretty(re)
上面求出来的结果如下:
5 u1_j 5 u0_j 5 u2_j 15 u0_j_left1 15 u1_j_left1 15 u2_j_left1 5 u0_j_right1 5 u1_j_right1
------ - ------ - ------ + ------------- + ------------- + ------------- - ------------- + -------------
h 2 h 2 h 4 h 4 h 4 h 4 h 4 h
5 u2_j_right1
- -------------
4 h
上面为d(u2)/dt的结果(程序中left表示-,right表示+)。变换v的选定,同样可以得到d(u0)/dt和d(u1)/dt,从而得到d(u)/dt=F(u)这种形式的微分方程组。
SpendU.m
function [u]=SpendU(a,b,j,d)
%输入: u的极限属性a,a为1代表+(右极限),a为-1代表-(左极限);
% u(x)中x的下标,即x所在的单元位置;
% u的导数阶数d,d为1代表u有一阶导数,d为0代表u无导数。
%输出: 由勒德让基函数表出的u,系数数值化。
syms u0 u1 u2 h x
t=Pandingu(a,b);
%基函数的计算
if(d==1)
q0=0;
q1=2/h;
q2=Pandingq(t(1),t(2))*6/h;
else if(d==0)
q0=1;
q1=Pandingq(t(1),t(2));
q2=1;
end
end
%u的坐标变换
if(t(1)==j-1)
for i=0:2
eval(sprintf('syms u%d_j_left1',i));
eval(['u' num2str(i) '= u' num2str(i) '_j_left1']);
end
else if(t(1)==j+1)
for i=0:2
eval(sprintf('syms u%d_j_right1',i));
eval(['u' num2str(i) '= u' num2str(i) '_j_right1']);
end
else if(t(1)==j)
for i=0:2
eval(sprintf('syms u%d_j',i));
eval(['u' num2str(i) '= u' num2str(i) '_j']);
end
end
end
end
u=u0*q0+u1*q1+u2*q2;
end
Pandingu.m
function [re] = Pandingu(a,b)
%1代表+;-1代表-。
if(a==1)
re(1)=b+1/2;
else if(a==-1)
re(1)=b-1/2;
end
end
re(2)=b;
end
Pandingq.m
function [result] = Pandingq(a,b)
a_l=a-1/2;
a_r=a+1/2;
if(b==a_r)
result=1;
else if(b==a_l)
result=-1;
end
end
end
SolveV.m
function[qv] = SolveV(a,b,v,dif)
%输入:基函数q的极限属性a,a为1代表+(右极限),a为-1代表-(左极限);
% q(x)中x的下标,即x所在的单元位置;
% q的导数阶数dif,dif为1代表u有一阶导数,dif为0代表u无导数。
%输出:由三个输入参数决定的q的数值。
syms h x_jleft12 x
x_jright12 = x_jleft12+h;
xj=(x_jleft12+x_jright12)/2;
q0=1;
q1=2*(x-xj)/h;
q2=(6*(x-xj).^2)/(h.^2)-1/2;
if(dif==1)
if(v==q2)%因为v取q2的时候才会有±6/h的变化,其余情况取q0为0,q1为2/h。
re=Pandingu(a,b);
qv=Pandingq(re(1),re(2))*6/h;
else if(v==q1)
qv=2/h;
else if(v==q0)
qv=0;
end
end
end
else if(dif==0)
if(v==q1)%因为v取q1的时候才会有±1的变化,其余情况取q0和q2都为1
re=Pandingu(a,b);
qv=Pandingq(re(1),re(2));
else
qv=1;
end
end
end
end
例2、转化下面的形式为常微分方程组(通量不含对x的偏导)
matlab代码如下:
clear all;clc
syms uh_dt x j x_jleft12 x_jright12 h u0_j u1_j u2_j
x_jright12 = x_jleft12+h;
xj=(x_jleft12+x_jright12)/2;
%基函数的选定
% v=x.^0;%v=1
% v=2*(x-xj)/h;
v=(6*(x-xj).^2)/(h.^2)-1/2;
dv=diff(v,'x',1);
q0=1;
q1=2*(x-xj)/h;
q2=(6*(x-xj).^2)/(h.^2)-1/2;
f(x)=x;
u=u0_j*q0+u1_j*q1+u2_j*q2;
re_u_dt=int(uh_dt*v.^2,x,x_jleft12,x_jright12);
re=(-int(f(u)*dv,x,x_jleft12,x_jright12)+SpendU(1,j+1/2,j,0)*SolveV(-1,j+1/2,v,0)-SpendU(1,j-1/2,j,0)*SolveV(-1,j-1/2,v,0))/diff(re_u_dt,'uh_dt');
re=expand(re);%u_dt 上标由v的选择决定
pretty(re)
运行结果如下:
5 u0_j_right1 5 u1_j 5 u2_j 5 u0_j 5 u1_j_right1 5 u2_j_right1
------------- - ------ - ------ - ------ - ------------- + -------------
h h h h h h
上面为d(u2)/dt的结果(程序中left表示-,right表示+)。变换v的选定,同样可以得到d(u0)/dt和d(u1)/dt,从而得到d(u)/dt=F(u)这种形式的微分方程组。
例3、转化下面的形式为常微分方程组(通量含有对x的偏导ULDG格式)
matlab代码如下:
clear all;clc
syms uh_dt x j x_jleft12 x_jright12 h u0_j u1_j u2_j
x_jright12 = x_jleft12+h;
xj=(x_jleft12+x_jright12)/2;
% v=x.^0;%v=1
v=2*(x-xj)/h;
% v=(6*(x-xj).^2)/(h.^2)-1/2;
d2v=diff(v,'x',2);
q0=x.^0;
q1=2*(x-xj)/h;
q2=(6*(x-xj).^2)/(h.^2)-1/2;
f(x)=x;
u=u0_j*q0+u1_j*q1+u2_j*q2;
re_u_dt=int(uh_dt*v.^2,x,x_jleft12,x_jright12);
re=(int(u*d2v,x,x_jleft12,x_jright12)+SpendU(1,j+1/2,j,1)*SolveV(-1,j+1/2,v,0)-SpendU(1,j-1/2,j,1)*SolveV(1,j-1/2,v,0)-SpendU(1,j+1/2,j,0)*SolveV(-1,j+1/2,v,1)+SpendU(1,j-1/2,j,0)*SolveV(1,j-1/2,v,1))/diff(re_u_dt,'uh_dt');
re=expand(re);%u_dt 上标由v的选择决定
pretty(re)
运行结果如下:
6 u0_j 12 u2_j 6 u0_j_right1 12 u1_j_right1 24 u2_j_right1
------ - ------- - ------------- + -------------- - --------------
2 2 2 2 2
h h h h h
上面为d(u1)/dt的结果(程序中left表示-,right表示+)。变换v的选定,同样可以得到d(u0)/dt和d(u2)/dt,从而得到d(u)/dt=F(u)这种形式的微分方程组。
由于程序所得出的结果是化简后的最简式,若想要将半离散格式转化为矩阵格式,需要有成对的变量出现,例如出现a*u1_j,就需要有±b*u1_j±1配对,才能生成矩阵的格式,而且其系数a和b最好相同(会生成元素为1的稀疏矩阵)。此时就要对变量进行适当的加减配对。
例如:
结果只有60*u1_j,那么就对半生成(30*u1_j+30*u1_j+1)+(30*u1_j-30*u1_j+1);
结果为20*u1_j+40*u1_j+1,那么加减配凑为(30*u1_j+30*u1_j+1)+(-10*u1_j+10*u1_j+1)。
例4、转化下面的形式为常微分方程组(含两个以上的通量组合LDG格式)
这里假设b(u)=u,在原来的基础上多出了p和q这两个变量,需要再创建这两个变量:
function [u]=SpendUAdd(a,b,j,d)
% 创建p
syms v0 v1 v2 h x
t=Pandingu(a,b);
%基函数的计算
if(d==1)
q0=0;
q1=2/h;
q2=Pandingq(t(1),t(2))*6/h;
else if(d==0)
q0=1;
q1=Pandingq(t(1),t(2));
q2=1;
end
end
%u的坐标变换
if(t(1)==j-1)
for i=0:2
eval(sprintf('syms v%d_j_left1',i));
eval(['v' num2str(i) '= v' num2str(i) '_j_left1']);
end
else if(t(1)==j+1)
for i=0:2
eval(sprintf('syms v%d_j_right1',i));
eval(['v' num2str(i) '= v' num2str(i) '_j_right1']);
end
else if(t(1)==j)
for i=0:2
eval(sprintf('syms v%d_j',i));
eval(['v' num2str(i) '= v' num2str(i) '_j']);
end
end
end
end
u=v0*q0+v1*q1+v2*q2;
end
代码同上(里面所有的v换成Q),再创建q生成SpendUAdd1.m文件。主要的运行过程如下:
clear all;clc
syms uh_dt x j x_jleft12 x_jright12 h u0_j u1_j u2_j v0_j v1_j v2_j Q0_j Q1_j Q2_j
x_jright12 = x_jleft12+h;
xj=(x_jleft12+x_jright12)/2;
% v=x.^0;%v=1
v=2*(x-xj)/h;
% v=(6*(x-xj).^2)/(h.^2)-1/2;
dv=diff(v,'x',1);
q0=x.^0;
q1=2*(x-xj)/h;
q2=(6*(x-xj).^2)/(h.^2)-1/2;
a=0.5;
f(x)=x;
u=u0_j*q0+u1_j*q1+u2_j*q2;
u1=v0_j*q0+v1_j*q1+v2_j*q2;
u2=Q0_j*q0+Q1_j*q1+Q2_j*q2;
re_u_dt=int(uh_dt*v.^2,x,x_jleft12,x_jright12);
re=(-int(f(u)*u1*dv,x,x_jleft12,x_jright12)+f(SpendU(-1,j+1/2,j,0))*SpendUAdd(1,j+1/2,j,0)*SolveV(-1,j+1/2,v,0)-f(SpendU(-1,j-1/2,j,0))*SpendUAdd(1,j-1/2,j,0)*SolveV(1,j-1/2,v,0)+a*(int(u2*dv,x,x_jleft12,x_jright12)-SpendUAdd1(1,j+1/2,j,0)*SolveV(-1,j+1/2,v,0)+SpendUAdd1(1,j-1/2,j,0)*SolveV(1,j-1/2,v,0)))/diff(re_u_dt,'uh_dt');
re=expand(re);%u_dt 上标由v的选择决定
pretty(re)
运行结果如下:
3 Q0_j 3 Q1_j 3 Q2_j 3 Q0_j_right1 3 Q1_j_right1 3 Q2_j_right1 6 u0_j v0_j 2 u1_j v1_j
------ + ------ - ------ - ------------- + ------------- - ------------- - ----------- - -----------
2 h 2 h 2 h 2 h 2 h 2 h h h
6 u2_j v2_j 3 u0_j_left1 v0_j 3 u0_j_left1 v1_j 3 u0_j_left1 v2_j 3 u1_j_left1 v0_j
- ----------- + ----------------- - ----------------- + ----------------- + -----------------
5 h h h h h
3 u1_j_left1 v1_j 3 u1_j_left1 v2_j 3 u2_j_left1 v0_j 3 u2_j_left1 v1_j 3 u2_j_left1 v2_j
- ----------------- + ----------------- + ----------------- - ----------------- + -----------------
h h h h h
3 u0_j v0_j_right1 3 u1_j v0_j_right1 3 u2_j v0_j_right1 3 u0_j v1_j_right1 3 u1_j v1_j_right1
+ ------------------ + ------------------ + ------------------ - ------------------ - ------------------
h h h h h
3 u2_j v1_j_right1 3 u0_j v2_j_right1 3 u1_j v2_j_right1 3 u2_j v2_j_right1
- ------------------ + ------------------ + ------------------ + ------------------
h h h h
上面为d(u1)/dt的结果(程序中left表示-,right表示+),代码中Q代表q,v代表p。变换v的选定,同样可以得到d(u0)/dt和d(u2)/dt,从而得到d(u)/dt=F(u)这种形式的微分方程组。
意义
通过程序将DG的半离散格式快速转化为ut=F(u)的常微分方程组,解决了手动计算的失误率,以便于直接进行时间离散 Runge-kutta 的过程,大大提高了在空间离散中执行的效率。
附录
通过对基函数q的具体计算,得出了以下的数据:
q=(,,) | m | l | k | m | q^3 | q^3_x | ||||||
j-1/2 | - | j-1 | j-1/2 | 1 | 1 | 1 | 1 | 0 | 2/h | 6/h | 12/h | |
j-1/2 | + | j | j-1/2 | 1 | -1 | 1 | -1 | 0 | 2/h | -6/h | 12/h | |
j+1/2 | - | j | j+1/2 | 1 | 1 | 1 | 1 | 0 | 2/h | 6/h | 12/h | |
j+1/2 | + | j+1 | j+1/2 | 1 | -1 | 1 | -1 | 0 | 2/h | -6/h | 12/h |
此数据和上面程序的计算过程基于基函数取2个,且含非积分项含有u或q的一阶导数形式,若出现二阶及其更高阶的导数,请完善附录的数据再改变上面的代码运行。程序修改参考意见,修改基函数的数量值,请修改基函数的编写处;修改离散格式,按照具体的格式修改re;修改u或q的高阶导数,修改SpendU.m和SolveV.m文件,并分别设定相应的d和diff值。