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

KM2016 / Elliptic / fem4

.cpp
Скачиваний:
8
Добавлен:
14.03.2016
Размер:
17.06 Кб
Скачать
// Приложение к разделу 4 Линейное уравнение общего вида 

//fem4.h
/*
#include <ctype.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

 static double funD( double xi, double bi []);
 static double funC( double xi, double bi []);
 static double funA( double xi, double bi []);
 static double fun_q( double xi, double bi []);

  void K_ij(int N, double K [], double x [], double bi [], double a, 
	  double (*funD)( double xi, double bi []), 
	  double (*funC)( double xi, double bi []), 
	  double (*funA)( double xi, double bi []));
 void q_i(int , double [], double [], double [], double (*)( double, double *));
 double domik(double x, int j, int mp, double *xx);
 double intgD(int, int, double *, double *, double (*)(double, double *));
 double intgCq(int, int, int , double *, double *, double (*)(double, double *), double (*)(double, int, int, double *));
 double intgA(int, int ,int, int , int , double *, double *, double (*)(double, double *), double (*)(double, int, int, double *));
 int i_max(int N, int p, double V[]);
 int lufact(int, double [], double [], double []);
 void SaveToFile(int, double*, const char* ); 
 int output(int test, int N, double* x, double *b, double *solution, const char* filename);
 */
 
//fem4.cpp
#include "fem4.h"

static double funD( double xi, double b []) {
  double D [] = {1.0, 0.5};  // Задаём коэффициенты диффузии
  int Ni;
  // Находим в каком слое находится координата xi
  if (xi<=b[1]) Ni = 0;
  else Ni = 1;
  return D[Ni];
}

static double funC( double xi, double b []) {
  double C [] = {0.3, 0.6};  // Задаём коэффициенты конвекции
  int Ni;
 // Находим в каком слое находится координата xi
  if (xi<=b[1]) Ni = 0;
  else Ni = 1;
  return C[Ni];
}

static double funA( double xi, double b []) {
  double A [] = {0.7, 0.4};  // Задаём коэффициенты поглощения
  int Ni;
  // Находим в каком слое находится координата xi
  if (xi<=b[1]) Ni = 0;
  else Ni = 1;
  return A[Ni];
}

static double fun_q( double xi, double b []) {
     return (-6*xi + 1);
     //return 3.14159*3.14159*sin(3.14159*x);
    // return x*x*x*x;
}

int main(int argc,char *argv[]) {
  double a, *bx, *step, *hp, *hm;
  int NL, *ib, knots_N, test;
  double *x, h, x_max;
  double *K, *q, *solution, result;
  
  NL = 2;  //число слоёв
  a = 0.5;     // коэффициент в граничном условии
  double b[3] = {0.0, 0.2, 1.0}; // координаты границ слоёв
  int N[2] = {5, 10}; // задаём число узлов внутри слоя, не считая узел на правой границе 
  
  bx = new double[NL+1];
  step = new double[NL];
  ib = new int [NL+1];
  ib[0]=0; 
  for (int i=0; i <= NL; i++) { ib[i+1] = ib[i] + N[i]; } //задаём номера - индексы - узлов на границах 
  bx[0] = 0.0;
  for (int i = 0; i < NL; i++) {  // шаг в слое и координаты левых границ слоёв (должны совпадать с b[i])
     step[i] = (b[i+1]-b[i])/N[i];
     bx[i+1] = b[i] + step[i]*N[i];
  }
  knots_N = ib[NL]+1; // всего узлов, считая узел в начале координат
  x = new double[knots_N];  // выделение памяти для координат узлов
  hp = new double[knots_N]; // выделение памяти для шага впрёд из данного узла 
  hm = new double[knots_N]; // выделение памяти для шага впрёд из данного узла 
  K = new double [knots_N*knots_N]; // выделение памяти для матрицы 
  q = new double[knots_N]; // выделение памяти для правой части
  
  x[0] = 0.0;   // помещаем начало координат на левую границу и задаём координаты узлов
  for (int k = 0; k < NL; k++) {
    for (int i = 1; i <= (ib[k+1] - ib[k]); i++) {    
       x[i+ib[k]] = bx[k] + step[k]*i; 
    }
  }

  solution = new double[knots_N];
  K_ij(knots_N, K, x, b , a, funD, funC, funA) ; // Матрица 

  q_i(knots_N, x, b, q, fun_q);  //Правая часть матричного уравнения
  result = lufact(knots_N, K, q, solution);
  test = 0;
  result = output(test, knots_N, x, b, solution, "result.txt");

  return 1; 	
}

//Вывод решения в файл
int output(int test, int N, double *x, double *b, double *solution, const char *filename) {
	FILE *out;
        out = fopen(filename, "wt");;
  double u_i, x_i, sol_i, q_i; 
  int result;
  if (test == 1) {
    fprintf(out,"\n x          u(x)          solution\n");
    for (int i=0;i<N;i++) {
      x_i = x[i];   
      u_i = pow(x_i,3.0) - x_i;
      //u_i = sin(3.14159*x_i);
      sol_i = solution[i];
      fprintf(out,"%-12.3g%-12.7g%-12.7g\n",x_i, u_i, sol_i);
    } 
    result = 1;
  }
  else if (test == 0) {
    fprintf(out,"\n x          q(x)        solution\n");
    for (int i=0;i<N;i++) {
      x_i = x[i];  
      sol_i = solution[i]; 
      q_i = fun_q(x_i,b);
      fprintf(out,"%-12.3g%-12.7g%-12.7g\n",x_i, q_i, sol_i);
    }
    result = 1;
  }  
  else {
    fprintf(out,"\n Sorry, no solution!\n");
    result = 0;
  }
  fclose(out);
  return result;
}


void K_ij(int N, double K [], double x [], double b [], double a, 
	  double (*funD)( double xi, double b []), 
	  double (*funC)( double xi, double b []), 
	  double (*funA)( double xi, double b [])) {
// Вычисление матричных коэффициентов уравнения Кu=q 
// N - число узлов
// x[] - координаты узлов
// a - коэффициент в граничном условии
// funD, funC, funA - функциональные коэффициенты дифф. уравннения

int n = N-1; // индекс узла на правой границе 
double hm, hp, D, C, A;
   
 for (int i=0; i < N; i++) {
   for (int j=0; j < N; j++) {
     if (i==j) { // главная диагональ
       if (i==0 && j==0) {  // главная диагональ первая строка левая граница
         hp = x[1]-x[0];
         K[i] = -a + intgD(1, i, x, b, funD)/(hp*hp) 
                    - intgCq(1, i, 1, x, b, funC, domik)/hp  
		    - intgA(1, 0, 1, 0, 1, x, b, funA, domik);
        } 
       else if (i==n && j==n) { // главная диагональ последняя строка, последний узел
         hm = x[n]-x[n-1]; 
         K[i*N+i] = intgD(-1, i, x, b, funD)/(hm*hm) 
               + intgCq(-1, i, -1, x, b, funC, domik)/hm - 
               - intgA(-1, i, -1, j, -1, x, b, funA, domik);  
       }
       else  { // главная диагональ от второй до предпоследней строк
         hm = x[i]-x[i-1];  hp = x[i+1]-x[i]; 
         K[i*N+i] =  intgD(-1, i, x, b, funD)/(hm*hm) + intgD(1, i, x, b, funD)/(hp*hp) 
                  + intgCq(-1, i, -1, x, b, funC, domik)/hm - intgCq(1, i, 1, x, b, funC, domik)/hp 
		  - intgA(-1,i,-1, i, -1, x, b, funA, domik) - intgA(1, i, 1, i, 1, x, b, funA, domik);
       }  
     }

     else if ( (i-j)==1 )     { // под главной диагональю
       hm = x[i] - x[j];
       K[i*N+j] =  - intgD(-1, i, x, b, funD)/(hm*hm) 
                  - intgCq(1, i, 1, x, b, funC, domik)/hm  
		  - intgA(-1, i, -1, i-1, 1, x, b, funA, domik);
     }
     else if  ( (j-i)==1 )     { // над главной диагональю
       hp = x[j] - x[i];
       D = intgD(1, i, x, b, funD)/(hp*hp);
       C = intgCq(1, i, 1, x, b, funC, domik)/hp; 
       A = intgA(1, i, 1, i+1, -1, x, b, funA, domik);
       K[i*N+j] = - D + C - A;
     } 
     else { K[i*N+j] = 0.; } 
   }
 }
}

void q_i(int knots_N, double x [], double b[], double q[], double (*fun_q)( double xi, double b [])) {
//построение правой части уравнения Кu=q 
//  knots_N - число узлов
// x[] - координаты узлов
// funq(x) - правая часть диф. уравнения
 int n = knots_N - 1; // n - индекс последнего узла
 for (int i=0; i <= n; i++) {
   if (i == 0) {
     q[i] =  intgCq(1, i, 1, x, b, fun_q, domik); 
   }
   else if (i == n) {
     q[i] =  intgCq(-1, i, -1, x, b, fun_q, domik); 
   }
   else {
     q[i] =  intgCq(-1, i, -1, x, b, fun_q, domik) + intgCq(1, i, 1, x, b, fun_q, domik); 
   }
 }
}

double domik(double x, int j, int mp, double *xx) {
// Пробная функция   
// x - переменная интегрирования
// j - индекс узла
// mp = 1 - интегрируем вперёд, = -1 - назад
// xx[] - координаты всех узлов

// x_j - координата узла крыши домика  
// h - шаг между узлами
  double x_j, h, value ;
  x_j = xx[j];
  if (mp > 0) {
    h = xx[j+1] - x_j;
    value = -1.0/h*(x - x_j - h);
  }
  else {
    h = x_j - xx[j-1];
    value = 1.0/h*(x - x_j + h);  
  }
  return  value;

}

double intgD(int mp, int i, double *x, double *b, double (*fun)(double, double *) ) {
// Интегрируем функцию funD из j-того узла i-той строки 
// i - индекс строки, j - индекс узла
// mp - направление интегрирования от узла с индексом равным индексу строки: вперёд 1, или назад -1 
// x[] - координаты узлов
// b [] - координаты границ слоёв
// fun(x,b) - интегрируемая функция, x - переменная интегрирования

     int m = 10;
     double x0, h, dh, x_k, x_kp, s;
     
     if (mp > 0) { // интегрируем вперёд от узла с индексом равным индексу строки
       x0 = x[i]; // нижний предел интеграла
       h = x[i+1] - x0;
     }
     else { // интегрируем назад
       x0 = x[i-1];
       h = x[i] - x0;
     }
     dh = h/m; // шаг интегрирования
     s = 0;
     for (int k=0; k<m; k++) {
         x_k = x0 + dh*k;
         x_kp = x_k + dh;
         s = s + 0.5*dh*(fun(x_k, b) + fun(x_kp, b) );
     }  
     return s;
 }

double intgCq(int mp, int i, int mpi, double *x, double *b, double (*fun)(double, double *), double (*domik)(double, int, int, double *)) { 
// Интегрируем функцию fun * domik  ( это funC,fun_q)
// i - индекс узла (строки для funq)
// mpi - верхний индекс у пробной функции "домик"
// mp = 1 интегрируем на шаг вперёд, или -1 назад из этого узла
// b [] - координаты границ слоёв
// x[] - координаты узлов
// fun(x,b) - интегрируемая функция, x - переменная интегрирования
// domik(x,j,mp,xx[]) - x - переменная интегрирования, j -индекс узла, 

     double x0, h, dh, x_k, x_kp, s;
     int m = 3;
     if (mp < 0 ) { // интегрируем назад 
       x0 = x[i-1]; // нижний предел интеграла
       h = x[i] - x0;  
     }
     else { // интегрируем вперёд
       x0 = x[i];
       h =  x[i+1] - x0;       
     }
     dh = h/m;
     s = 0;
     for (int k=0; k<m; k++) {
         x_k = x0 + dh*k;
         x_kp = x_k + dh;
         s = s + 0.5*dh*( domik(x_k,i,mpi,x)*fun(x_k, b) + domik(x_kp,i,mpi,x)*fun(x_kp, b) );
     }
     return s;
 }
 
 double intgA(int mp, int i, int mpi, int j, int mpj, double *x, double *b,
	      double (*fun)(double, double *), double (*domik)(double, int, int, double *)) { 
// Интегрируем функцию domik*funA*domik 
// mp - направление интегрирования от узла с индексом равным индексу строки: вперёд 1, или назад -1 
// i - индекс узла равный индексу строки
// j - индекс того же или соседнего справа или слева узла
// mpi, mpj = (+) (-) - верхние индексы у пробной функции "домик"
// b [] - координаты границ слоёв
// x[] - координаты узлов
// fun(x,b) - интегрируемая функция, x - переменная интегрирования
// domik(x,j,mp,xx[]) - x - переменная интегрирования, j -индекс узла, 
 
     int m = 3;
     double x_j, x0, h, dh, x_k, x_kp, s;
 
     if (mp<0) {   // интегрируем назад
       x0 = x[i-1];   // нижний предел интеграла
       h = x[i] - x0; 
     }
     else { // интегрируем вперёд
       x0 = x[i];
       h = x[i+1]- x0;
     }
     dh = h/m;
     s = 0;
     for (int k=0; k<m; k++) {
       x_k = x0 + dh*k;
       x_kp = x_k + dh;
       s = s + 0.5*dh*( domik(x_k,i,mpi,x)*fun(x_k, b)*domik(x_k,j,mpj,x) + 
			  domik(x_kp,i,mpi,x)*fun(x_kp, b)*domik(x_kp,j,mpj,x) );
     }  
     return s;
 }

 int lufact(int N, double A [], double q[], double solution[]) {
// Вход - K - матрица размера NxN
//      - q - матрица размера Nx1
// Выход - Х - матрица размера Nx1, содержащая решение KX=q
// Инициализация X, Y, временное сохранение матрицы С и строк
// заданной матрицы перестановок R
//   double A[500][500]; 
   double *X, *Y, *C, *V, mult; 
   int *R, *J, ip, d, result;

   X = new double[N];  
   Y = new double[N];  
   C = new double[N];  
   V = new double[N];
   R = new int [N];  
   J = new int [N]; 
   for (int i = 0; i < N; i++) {
      X[i] = 0.0; Y[i] = 0.0; C[i] = 0.0; J[i] = 0;
      R[i] = i; 
  }	

  int j;
  for (int p=0; p < N; p++) {
// Выписываем столбец p    
    for (int i=0; i < N; i++) {
      V[i] = A[i*N+p];
    }  
// Находим номер строки главного элемента для столбца p  
    j = i_max(N,p,V);
    J[p] = j; // смещение строки с главным элементом относительно строки p
// Меняем местами строки p и j
    for (int i=0; i < N; i++) {
// Записывваем p-ю строку в С
      C[i] = A[p*N+i];
    } 
// Записываем в p+j-ую строку p-ую строку 
    for (int i=0; i < N; i++) {
      A[p*N+i] = A[(p+j)*N+i];
    }
// Записываем p-ую строку в p+j-ую строку     
    for (int i=0; i < N; i++) {
      A[(p+j)*N+i] = C[i];
    }
    d = R[p];
    R[p] = R[p+j];
    R[p+j] = d;
    if (A[p*N+p] == 0) {
      printf("Матрица вырождена. Нет единственного решения!");
      result = 0;
      return result;
    }
// Вычисление множителя и размещение под диагональю матрицы К
    for (int k=p+1; k<N; k++) {
       mult = A[k*N+p]/A[p*N+p];
       A[k*N+p] = mult;
       for (int i = p+1; i<N; i++) {
         A[k*N+i] = A[k*N+i] - mult*A[p*N+i];  
       }
    }
  }

// Прямая прогонка: Решение для Y
  Y[0] = q[R[0]];
//Y[0] = q[0];
  double M;
  for (int k=1; k<N; k++) {
    M = 0.0;
    for (int i=0; i<k; i++) { // Скалярное произведение векторов
       M = M  +  A[k*N+i]*Y[i]; 
    }
    Y[k] = q[R[k]] -  M;
// Y[k] = q[k] -  M;
  }
  
  
// Обратная прогонка: Решение для X
  X[N-1] = Y[N-1]/A[(N-1)*N+N-1];
  solution[N-1] = X[N-1];
  for (int k=N-2; k>=0; k--) {
    M = 0.0;
    for (int i=k+1; i<N; i++) {
      M = M  +  A[k*N+i]*X[i];
    }
    X[k] = (Y[k] - M)/A[k*N+k]; 
    solution[k] = X[k];
  }
 
  result = 1;
  
  return result;
}

int i_max(int N, int p, double V[]) {
  int max_i;
  max_i = 0;
  for (int i=p; i < N-2; i++) {
    if ( fabs(V[i+1]) > fabs(V[i]) ) max_i = i+1-p;
  }
  return max_i;
}

void SaveToFile(int knots_N, double* x, const char* filename)
{
	FILE *out;
        out = fopen(filename, "wt");;
 
  	for (int i=0; i < knots_N; i++) 
	{
                fprintf(out,"%g\n",x[i]);
     	} 
    
  fclose(out);
}

Соседние файлы в папке Elliptic