Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

KM2016 / Hyperbolic / hyperb

.cpp
Скачиваний:
7
Добавлен:
14.03.2016
Размер:
20.63 Кб
Скачать
// Решатель гиперболического уравнения
#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;
}


Соседние файлы в папке Hyperbolic
  • #
    14.03.201620.63 Кб7hyperb.cpp
  • #
    14.03.2016186 б7testhyp.aux
  • #
    14.03.201612 Кб7testhyp.fdb_latexmk
  • #
    14.03.201610.89 Кб7testhyp.fls
  • #
    14.03.201615.06 Кб7testhyp.log
  • #
    14.03.2016423 б8testhyp.m