Добавил:
Upload
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:KM2016 / Hyperbolic / hyperb
.cpp// Решатель гиперболического уравнения
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int COUNT;
static int N,Nt,porjadok; //Число узловых точек по пространству
static double *Xi,*Tj; //Узловые значения пространственной переменной
static double *dery,*solution, //Решение начальной задачи
(*pfunK)(double,double t),(*pfunA)(double,double t),
(*pfunB)(double,double t),(*pfunG)(double,double t),
(*pfunF)(double,double t), (*pfunU0)(double,double t),
(*pfunU1)(double,double t),(*pfunC)(int nbound,double t),
(*pfunD)(int nbound,double t),(*pfunE)(int nbound,double t);
//Параметры квадратурной формулы Гаусса
static double xiGauss[3][4]={//Узлы
{-0.577350269189626,0.577350269189626,0,0},//n=2
{0,-0.77459666924183,0.77459666924183,0},//n=3
{0.339981043584856,-0.339981043584856,0.861136311594053,-0.861136311594053}
};
static double wiGauss[3][4]={//Веса
{1,1,0,0},//n=2
{0.888888888888889,0.555555555555556,0.555555555555556,0},//n=3
{0.652145154862546,0.652145154862546,0.347854845137454,0.347854845137454}
};
//Тип ядра усредняющего опретора
enum KERNEL_TYPE{
TIMINUS,TIPLUS,SIMINUS,SIPLUS,QIMINUS,QIPLUS
};
//Ядро усредняющего опретора
static inline double Kernel(double x,int kernel_type,double Xi[],int i) {
switch(kernel_type) {
case TIMINUS:return x-Xi[i-1];
case TIPLUS:return Xi[i+1]-x;
case SIMINUS:return 1;
case SIPLUS:return -1;
case QIMINUS:return (x-Xi[i-1])*(x-Xi[i]);
case QIPLUS:return (Xi[i+1]-x)*(x-Xi[i]);
}
}
static inline double HMINUS(int N,double Xi[],int iuzla) {
if(iuzla==0) return 0;
return Xi[iuzla]-Xi[iuzla-1];
}
static inline double HPLUS(int N,double Xi[],int iuzla) {
if(iuzla==N-1)return 0;
return Xi[iuzla+1]-Xi[iuzla];
}
static double OperatorMinus(double (*fun)(double x,double t),
int N,double Xi[],int iuzla,int porjadok,int kernel_type,double t) {
if(iuzla==0) return 0;
if(porjadok>4||porjadok<2) porjadok=4;
double a=Xi[iuzla-1],b=Xi[iuzla],ab=0.5*(b-a),ba=0.5*(b+a),sum=0,x,Hminus=HMINUS(N,Xi,iuzla);
int i,j=porjadok-2;
for(i=0;i<porjadok;i++){
x=ab*xiGauss[j][i]+ba;
sum+=wiGauss[j][i]*fun(x,t)*Kernel(x,kernel_type,Xi,iuzla);
}
return sum*ab*2/(Hminus*Hminus);
}
static double OperatorPlus(double (*fun)(double x,double t),
int N,double Xi[],int iuzla,int porjadok,int kernel_type,double t) {
if(iuzla==N-1) return 0;
if(porjadok>4||porjadok<2) porjadok=4;
double a=Xi[iuzla],b=Xi[iuzla+1],ab=0.5*(b-a),ba=0.5*(b+a),sum=0,x, Hplus=HPLUS(N,Xi,iuzla);
int i,j=porjadok-2;
for(i=0;i<porjadok;i++) {
x=ab*xiGauss[j][i]+ba;
sum+=wiGauss[j][i]*fun(x,t)*Kernel(x,kernel_type,Xi,iuzla);
}
return sum*ab*2/(Hplus*Hplus);
}
static double OperatorT_Sred(double (*fun)(double x,double t),
int N,double Xi[],int iuzla,int porjadok,double t) {
double Hminus=HMINUS(N,Xi,iuzla),Hplus=HPLUS(N,Xi,iuzla),
hbar2=Hplus+Hminus;
return (Hminus*OperatorMinus(fun,N,Xi,iuzla,porjadok,TIMINUS,t)
+Hplus*OperatorPlus(fun,N,Xi,iuzla,porjadok,TIPLUS,t))/hbar2;
}
static void progon(int n,double c[],double a[],double b[],double g[]) {
// solves a linear tridiagonal system by eliminal
// int n; order of matrix
// double c[]; input vector of lenth n, the matrix elements behind diagonal, c[0] never used
// double a[]; input vector of lenth n, the diagonal matrix elements destroyed in computation
// double b[];/*input vector of lenth n,the matrix elements ahead of diagonal,
// b[n-1] never used
// double g[]; input vector of lenth n, the right hand side of a system,
// destroyed in computation and replaced by resultant solution
int i,i1; double r;
for(i=1; i<n; i++) {
i1=i-1;
r=c[i]/a[i1];
a[i]-=r*b[i1];
g[i]-=r*g[i1];
}
g[n-1]/=a[n-1];
for(i=n-2;i>=0;g[i]=(g[i]-b[i]*g[i+1])/a[i],i--);
}
//Расчет производных для метода Рунге-Кутта
static double OperatorGamma(double t,double y[],int i) {
if(i>0&&i<N-1) return 0;
int nbound; double D,h;
if(i==0) {
nbound=0;
h=Xi[i+1]-Xi[i];
}
else {
nbound=1;
h=Xi[i]-Xi[i-1];
}
D=pfunD(nbound,t);
if(D==0) return y[i]*pfunE(nbound,t)/pfunC(nbound,t);
return 2*(pfunE(nbound,t)-y[i]*pfunC(nbound,t))*pfunA(Xi[i],t)/(D*h);
}
static void OperatorL(double t,double y[],double res[]) {
int i,N1=N-1;
double *hiplus=new double[N],*Siplus=new double[N], *Qiplus=new double[N],Tiplus,Timinus,hbar;
for(i=0;i<N;i++) {
hiplus[i]=HPLUS(N,Xi,i);
Siplus[i]=OperatorPlus(pfunA,N,Xi,i,porjadok,SIPLUS,t);
Qiplus[i]=OperatorPlus(pfunG,N,Xi,i,porjadok,QIPLUS,t);
res[i]=OperatorT_Sred(pfunF,N,Xi,i,porjadok,t)-
y[i]*OperatorT_Sred(pfunG,N,Xi,i,porjadok,t)+OperatorGamma(t,y,i);
}
for(i=0;i<N;i++) {
Timinus=OperatorMinus(pfunB,N,Xi,i,porjadok,TIMINUS,t);
Tiplus=OperatorPlus(pfunB,N,Xi,i,porjadok,TIPLUS,t);
if(i==0) {
hbar=hiplus[i];
res[i]+=(y[i+1]-y[i])*(Tiplus-Qiplus[i]-Siplus[i])/hbar;
continue;
}
if(i==N1) {
hbar=hiplus[i-1];
res[i]+=(y[i]-y[i-1])*(Timinus+Qiplus[i-1]+Siplus[i-1])/hbar;
continue;
}
hbar=hiplus[i-1]+hiplus[i];res[i]+=
((y[i+1]-y[i])*(Tiplus-Qiplus[i]-Siplus[i])+
(y[i]-y[i-1])*(Timinus+Qiplus[i-1]+Siplus[i-1]))/hbar;
}//i
delete hiplus; delete Siplus; delete Qiplus;
}
void fct(double t,double sol[],double dery[],int ndim) {
OperatorL(t,sol,dery+N);
double hp,hm,hbar,*Qiplus=new double[N];
int i,N1=N-1,iN;
for(i=0;i<N;i++) Qiplus[i]=OperatorPlus(pfunK,N,Xi,i,porjadok,QIPLUS,t);
//Усреднение производной по времени
for(i=0;i<N;i++) {
hm=HMINUS(N,Xi,i);
hp=HPLUS(N,Xi,i);
hbar=(hp+hm);
iN=i+N;dery[i]=2*sol[iN]/3;
if(i==0) dery[i]+=sol[iN+1]/3.;
else if(i==N1) dery[i]+=sol[i+N1]/3.;
else dery[i]+=(hm*sol[i+N1]+hp*sol[iN+1])/(3*hbar);
}//i
double *a=new double[N],*b=new double[N],*c=new double[N];
for(i=0;i<N;i++) {
hm=HMINUS(N,Xi,i);
hp=HPLUS(N,Xi,i);
hbar=0.5*(hp+hm);
a[i]=2./3.;
b[i]=hp/(6.*hbar);
c[i]=hm/(6.*hbar);
}//i
progon(N,c,a,b,dery);
//Усреднение второй производной по времени
for(i=0;i<N;i++) {
hm=HMINUS(N,Xi,i);
hp=HPLUS(N,Xi,i);
hbar=(hp+hm);
iN=i+N;
dery[iN]-=OperatorT_Sred(pfunK,N,Xi,i,porjadok,t)*sol[iN];
if(i==0) {
dery[iN]+=(dery[i+1]-dery[i])*(-Qiplus[i])/hp;
}
else if(i==N1) {
dery[iN]+=(dery[i]-dery[i-1])*(Qiplus[i-1])/hm;
}
else {
dery[iN]+=((dery[i+1]-dery[i])*(-Qiplus[i])+
(dery[i]-dery[i-1])*(Qiplus[i-1]))/hbar;
}
}//i
for(i=0;i<N;i++) {
hm=HMINUS(N,Xi,i);
hp=HPLUS(N,Xi,i);
hbar=0.5*(hp+hm);
a[i]=2./3.;
b[i]=hp/(6.*hbar);
c[i]=hm/(6.*hbar);
}//i
progon(N,c,a,b,dery+N);
delete Qiplus;delete a;delete b;delete c;
COUNT++;
}
static void out(double x,double y[],double dery[],int ihlf,
int ndim,double prmt[])
{;}
static void rkgs(double prmt[],double y[],double dery[],int ndim,int *ihlf,
void (*fct)(double x,double y[],double dery[],int ndim),
void (*out)(double x,double y[],double d[],int ihlf,int ndim,double prmt[])) {
// НАЗНАЧЕНИЕ: решение системы дифференциальных уравнений первого порядка
// методом Рунге-Кутта.
// ВХОДНЫЕ ПАРАМЕТРЫ
// prmt[] - входной массив длиной не менее 5, служит для связи с
// пользовательской программой out; Первые 4 элемента сохраняются и означают:
// prmt[0] - нижняя граница интервала интегрирования
// prmt[1] - верхняя граница интервала интегрирования
// prmt[2] - начальный шаг интегрирования
// prmt[3] - верхняя граница погрешности
// prmt[4] - используется для связи с пользовательской программой out.
// При входе в out всегда prmt[4]=0. Если необходимо закончить
// вычисления в текущей точке, в out необходимо присвоить
// prmt[4] любое ненулевое значение.
// y[n] - входной массив начальных данных. В дальнейшем становится решением
// системы в промежуточных точках интервала интегрирования.
// dery[n] - входной массив весовых коэффициентов погрешности(сумма его
// компонент должна быть равна 1). В дальнейшем становится вектором
// производных в промежуточных точках интервала интегрирования.
// ndim - число уравнений
// void (*fct)();
// внешнее имя задаваемой пользователем функции для вычисления правых
// частей уравнений. Эта функция может иметь любое имя.
// Спецификация функции имеет вид:
// void fct(double x,double y[],double dery[],int ndim)
// x - текущая точка(входной параметр)
// y[] - решение в точке x (входной параметр)
// dery[] - правая часть системы в точке x (выходной параметр)
// ndim - число уравнений
// void (*out)();
// внешнее имя задаваемой пользователем подпрограммы для обработки
// данных, полученных в текущей точке.Эта функция может иметь любое имя.
// Спецификация функции имеет вид:
// void out(double x,double y[],double dery[],int ihlf,int ndim,int prmt[])
// x - текущая точка(входной параметр)
// y[] - решение в точке x (входной параметр)
// dery[] - правая часть системы в точке x (входной параметр)
// ihlf - число делений пополам начального шага. Е
// ndim - число уравнений (входной параметр)
// prmt[] - входной массив длиной не менее 5, служит для связи с
// пользовательской программой rkgs; Первые 4 элемента сохраняются и означают:
// prmt[0] - нижняя граница интервала интегрирования
// prmt[1] - верхняя граница интервала интегрирования
// prmt[2] - начальный шаг интегрирования
// prmt[3] - верхняя граница погрешности
// При входе в out всегда prmt[4]=0. Если необходимо закончить
// вычисления в текущей точке, в out необходимо присвоить
// prmt[4] любое ненулевое значение.
// ВЫХОДНЫЕ ПАРАМЕТРЫ
// *ihlf - число делений пополам начального шага. Если для достижения
// заданной точности требуется более 10 делений, происходит выход
// из rkgs со значением ihlf=11.
// Выход из rkgs со значением ihlf=12 происходит при prmt[2]=0.
// Выход из rkgs со значением ihlf=13 происходит при
// sign(prmt[2])!=sign(prmt[1]-prmt[0])
// МЕТОД: точность проверяется сравнением результатов при делении
// шага пополам
double *aux,a[4],b[4],c[4],x,xend,h,s,aj,bj,cj,delt,r1,r2;
int i,j,irec,istep,iend,imod,itest;
aux=(double*) calloc(ndim*8,sizeof(double));
for(i=0;i<ndim;i++)aux[7*ndim+i]=0.0666666666666666666666667*dery[i];
x=prmt[0];
xend=prmt[1];
h=prmt[2];prmt[4]=0.;
(*fct)(x,y,dery,ndim);
s=h*(xend-x);
if(s>0.) {
// Runge - Kutta method
s=sqrt(0.5);
a[0]=0.5;
a[1]=1.-s;
a[2]=1.+s;
a[3]=0.16666666666666666666666667;
b[0]=2.;
b[1]=1.;
b[2]=1.;
b[3]=2.;
c[0]=0.5;
c[1]=1.-s;
c[2]=1.+s;
c[3]=0.5;
for(i=0;i<ndim;i++) {
aux[i]=y[i];
aux[ndim+i]=dery[i];
aux[2*ndim+i]=0.;
aux[5*ndim+i]=0.;
}
irec=0;
h=h+h;
*ihlf=-1;
istep=0;
iend=0;
L4:
s=(x+h-xend)*h;
if(s>=0.) {
if(s>0.) h=xend-x;
iend=1;
}
(*out)(x,y,dery,irec,ndim,prmt);
if(prmt[4]!=0.) goto L40;
L8:
itest=0;
L9:
istep++;
j=0;
L10:
aj=a[j];
bj=b[j];
cj=c[j];
for(i=0;i<ndim;i++) {
r1=h*dery[i];
r2=aj*(r1-bj*aux[5*ndim+i]);y[i]+=r2;
r2*=3.;aux[5*ndim+i]+=(r2-cj*r1);
}
if(j<3) {
j++;
if(j!=2) x+=(0.5*h);
(*fct)(x,y,dery,ndim);
goto L10;
}
if(itest<=0) {
for(i=0;i<ndim;i++) aux[3*ndim+i]=y[i];
itest=1;
istep*=2;
istep-=2;
L18:
*ihlf=*ihlf+1;
x-=h;h*=0.5;
for(i=0;i<ndim;i++) {
y[i]=aux[i];
dery[i]=aux[ndim+i];
aux[5*ndim+i]=aux[2*ndim+i];
}
goto L9;
}
imod=istep/2;
if(istep!=2*imod) {
(*fct)(x,y,dery,ndim);
for(i=0;i<ndim;i++) {
aux[4*ndim+i]=y[i];
aux[6*ndim+i]=dery[i];
}
goto L9;
}
delt=0.;
for(i=0;i<ndim;i++) delt+=aux[7*ndim+i]*fabs(aux[3*ndim+i]-y[i]);
if(delt>prmt[3]) {
if(*ihlf>=10) {
*ihlf=11;
(*fct)(x,y,dery,ndim);
goto L39;
}
for(i=0;i<ndim;i++) aux[3*ndim+i]=aux[4*ndim+i];
istep*=2;
istep-=4;
x-=h;
iend=0;
goto L18;
}
(*fct)(x,y,dery,ndim);
for(i=0;i<ndim;i++) {
aux[i]=y[i];
aux[ndim+i]=dery[i];
aux[2*ndim+i]=aux[5*ndim+i];
y[i]=aux[4*ndim+i];
dery[i]=aux[6*ndim+i];
}
(*out)(x-h,y,dery,*ihlf,ndim,prmt);
if(prmt[4]!=0.) goto L40;
for(i=0;i<ndim;i++) {
y[i]=aux[i];
dery[i]=aux[ndim+i];
}
irec=*ihlf;
if(iend>0) goto L39;
*ihlf=*ihlf-1;
istep/=2;
h*=2;
if(*ihlf<0) goto L4;
imod=istep/2;
if(istep!=2*imod) goto L4;
if(delt>0.02*prmt[3]) goto L4;
*ihlf=*ihlf-1;
istep/=2;
h*=2;
goto L4;
}
else {
if(s==0.) *ihlf=12;
else *ihlf=13;
}
L39 : (*out)(x,y,dery,*ihlf,ndim,prmt);
L40 : free((void*)aux);
}
void hyperbol(
double (*funK)(double x,double t),
double (*funA)(double x,double t),double (*funB)(double x,double t),
double (*funG)(double x,double t),double (*funF)(double x,double t),
double (*funU0)(double x,double t),//Начальные значения решения
double (*funU1)(double x,double t),//Начальные значения решения производной по времени
int ind,// параметр, характеризующий гладкость начальных данных по
// пространственным переменным; ind=0-данные непрерывны;
// ind=1-данные кусочно непрерывны;
double (*funC)(int nbound,double t),//Граничные условия
double (*funD)(int nbound,double t),//Граничные условия
double (*funE)(int nbound,double t),//Граничные условия
int a_N,//Число узлов по пространственной переменной
double a_Xi[],//Узлы по пространственной переменной
int a_porjadok,//порядок квадратурной формулы Гаусса при интегрировании по
//пространственной переменной, a_porjadok<=4
int a_Nt,//Число узлов по времени
double a_Tj[],//Узлы по времени
// ВЫХОДНЫЕ ПАРАМЕТРЫ
float **Uij,//Решение в узловых точках по пространству и времени,
//Uij[i][j]=u(x_i,t_j)
float **derUij,//2-я производная по времени решения в узловых точках
//по пространству и времени, derUij[i][j]=u'(x_i,t_j)
float **der2Uij//Производная по времени решения в узловых точках
//по пространству и времени, derUij[i][j]=u''(x_i,t_j)
) {
// НАЗНАЧЕНИЕ: найти решение одномерной по пространству
// нестационарной задачи общего параболического типа.
// ВХОДНЫЕ ПАРАМЕТРЫ
// double (*funK)(),(*funA)(),(*funB)(),(*funG)(),(*funF)() -
// внешние имена задаваемых пользователем функций для вычисления
// функциональных коэффициентов уравнения.
// Эти функции могут иметь любые имена, но у каждой
// список параметров должен включать входные параметры x,t, где
// x - пространственный аргумент функции, t-временной аргумент функции.
// double (*funU0)(),double (*funU1)() -
// внешние имена задаваемых пользователем функций для вычисления
// начальных данных
// double (*funC)(),(*funD)(),(*funE)() -
// внешние имена задаваемых пользователем функций для вычисления
// функциональных коэффициентов, определяющих граничные условия.
// Эти функции могут иметь любые имена. У каждой функции список параметров
// должен включать входные параметры nbound,t, где nbound -
// индекс границы(nbound=0 - левая граница, nbound=1 - правая граница),
// t - время;
//------------------------------------
//Инициализировать глобальные переменные
int i,j;
N=a_N;
Nt=a_Nt;
porjadok=a_porjadok;
Xi=new double[N];
Tj=new double[Nt];
for(i=0;i<N;i++) Xi[i]=a_Xi[i];
for(j=0;j<Nt;j++)Tj[j]=a_Tj[j];
pfunK=funK; pfunA=funA; pfunB=funB; pfunG=funG; pfunF=funF;
pfunU0=funU0; pfunU1=funU1; pfunC=funC;pfunD=funD;pfunE=funE;
solution=new double[2*N];
dery=new double[2*N];
//Сформировать начальные данные
if(ind==0)for(i=0;i<N;i++) {
solution[i]=pfunU0(Xi[i],Tj[0]);
solution[i+N]=pfunU1(Xi[i],Tj[0]);
}
else {
double *Tiplu=new double[N], *Timin=new double[N];
for(i=0;i<N;i++) {
solution[i]=OperatorT_Sred(pfunU0,N,Xi,i,porjadok,Tj[0]);
solution[i+N]=OperatorT_Sred(pfunU1,N,Xi,i,porjadok,Tj[0]);
double hp=HPLUS(N,Xi,i),hm=HMINUS(N,Xi,i),hbar=0.5*(hm+hp);
dery[i]=2./3.;
dery[i+N]=2./3.;
Tiplu[i]=hp/(6.*hbar);
Timin[i]=hm/(6.*hbar);
}
progon(N,Timin,dery,Tiplu,solution);
progon(N,Timin,dery+N,Tiplu,solution+N);
delete Tiplu; delete Timin;
}
for(i=0;i<N;i++) {
Uij[i][0]=solution[i];
derUij[i][0]=solution[i+N];
}
//Найти решение на каждом временном слое
double prmt[5], ERROR=1.e-7;
int ihlf,N2=2*N;
COUNT=0;
for(j=1;j<Nt;j++) {
prmt[0]=Tj[j-1];
prmt[1]=Tj[j];
prmt[2]=(prmt[1]-prmt[0])*0.5;
prmt[3]=ERROR;
for(i=0;i<N2;i++) dery[i]=1./N2;
rkgs(prmt,solution,dery,N2,&ihlf,fct,out);
for(i=0;i<N;i++) {
Uij[i][j]=solution[i];
derUij[i][j]=dery[i];
der2Uij[i][j]=dery[i+N];
}
}//j
delete solution; delete Xi; delete Tj; delete dery;
}