RatemirTheRed

Untitled

Dec 27th, 2016
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.83 KB | None | 0 0
  1. #include <math.h>
  2. #include <stdio.h>
  3. #include <iostream>
  4.  
  5. #ifndef M_PI
  6. #define M_PI 3.1415926
  7. #endif
  8.  
  9. // TODO: При повышении точности по X погрешность падает.
  10. // А по T — растёт. Почему?
  11. /// Координатные интервалы.
  12. const double L = 1.0;
  13. const double T_MAX = 2.0;
  14. const int mult = 5;
  15. // Число ячеек сетки по X
  16. const int X_CELLS_COUNT = (8*mult);
  17. // Число ячеек сетки по T
  18. const int T_CELLS_COUNT = (8);
  19.  
  20. using namespace std;
  21.  
  22. /*     Задаваемые функции     */
  23.  
  24. // u(x, 0) = uStart(x);
  25. double uStart(double x)
  26. {
  27.     // return 0;
  28.     return 1.0;
  29. }
  30. // u(0, t) = uLeft(t)
  31. double uLeft(double t)
  32. {
  33.     double u0 = 1;
  34.     double a = 0.5;
  35.     // double a = 15;
  36.     double omega = 2*M_PI;
  37.     return u0 + a*sin(omega*t);
  38. }
  39. // u(L, t) = uRight(t)
  40. double uRight(double t)
  41. {
  42.     return 0;
  43. }
  44. // Точное решение уравнения
  45. double u(double x, double t)
  46. {
  47.     return 0.0;
  48. }
  49.  
  50. double max(double a, double b)
  51. {
  52.   if (a > b) return a;
  53.   else return b;
  54. }
  55.  
  56. /* Всё остальное */
  57.  
  58. typedef struct ReturnData
  59. {
  60.     // Двухмерный массив значений приблизительного решения уравнения
  61.     double** y;
  62. } ReturnData;
  63.  
  64. // Схема Кранка-Николсона
  65. ReturnData* solve(int N_x, int N_t);
  66.  
  67. // Схема Кранка-Николсона, решение с
  68. ReturnData* solve2(int N_x, int N_t);
  69.  
  70. int main()
  71. {
  72.     FILE* out;
  73.     FILE* out_t;
  74.     FILE* out_eps;
  75.     double t, x, h, tau, eps;
  76.     int k2, i2;
  77.     ReturnData* rd;
  78.     ReturnData* rd2;
  79.  
  80.     // Номер точки на x = 0.5;
  81.     int j = (0.5 / L) * X_CELLS_COUNT;
  82.  
  83.     if (j >= X_CELLS_COUNT) {
  84.       printf("j (%d) out of bounds, changing to %d\n", j, X_CELLS_COUNT/2);
  85.       j = X_CELLS_COUNT / 2;
  86.     }
  87.  
  88.     /*       Уравнение с условием u = 0 на правой границе     */
  89.  
  90.     fopen_s(&out, "solution.txt", "w");
  91.     fopen_s(&out_t, "sol_x=0_x=0.5.txt", "w");
  92.     fopen_s(&out_eps, "eps.txt", "a");
  93.  
  94.     rd  = solve(X_CELLS_COUNT, T_CELLS_COUNT);
  95.     // Решение, по которому будем оценивать погрешность.
  96.     rd2 = solve(X_CELLS_COUNT * 4, T_CELLS_COUNT);
  97.  
  98.     eps = 0;
  99.     t   = 0;
  100.     x   = 0;
  101.     h   = L / X_CELLS_COUNT;
  102.     tau = T_MAX / T_CELLS_COUNT;
  103.  
  104.     // Вывод в файл множества найденных точек
  105.     for (int k = 0; k <= T_CELLS_COUNT; ++k)
  106.     {
  107.         x = 0;
  108.         for (int i = 0; i <= X_CELLS_COUNT; ++i)
  109.         {
  110.             // Нахождение ошибки как максимум модуля отклонения
  111.             // от в 4 раза более точного решения.
  112.             eps = max(eps, fabs(rd->y[i][k] - rd2->y[i*4][k]));
  113.  
  114.             fprintf(out, "%f %f %f\n", x, t, rd->y[i][k]);
  115.             x += h;
  116.         }
  117.         // Запись значений при x = 0 и x = 0.5.
  118.         // (Первой колонкой t, второй и третьей -- значения)
  119.         fprintf(out_t, "%f %f %f\n", t, rd->y[0][k], rd->y[j][k]);
  120.  
  121.         t += tau;
  122.     }
  123.     fprintf(out_eps, "%d %d %f\n", X_CELLS_COUNT, T_CELLS_COUNT, eps);
  124.  
  125.     fclose(out);
  126.     fclose(out_t);
  127.     fclose(out_eps);
  128.  
  129.     /*       Уравнение с условием теплоизоляции на правой границе     */
  130.  
  131.     fopen_s(&out, "solution2.txt", "w");
  132.     fopen_s(&out_t, "sol2_x=0_x=0.5.txt", "w");
  133.     fopen_s(&out_eps, "eps2.txt", "a");
  134.  
  135.     rd = solve2(X_CELLS_COUNT, T_CELLS_COUNT);
  136.     // Решение, по которому будем оценивать погрешность.
  137.     rd2 = solve2(X_CELLS_COUNT * 4, T_CELLS_COUNT * 4);
  138.  
  139.     eps = 0;
  140.     t   = 0;
  141.     x   = 0;
  142.     h   = L / X_CELLS_COUNT;
  143.     tau = T_MAX / T_CELLS_COUNT;
  144.  
  145.     // Вывод в файл множества найденных точек
  146.     for (int k = 0; k <= T_CELLS_COUNT; ++k)
  147.     {
  148.         x = 0;
  149.         for (int i = 0; i <= X_CELLS_COUNT; ++i)
  150.         {
  151.             // Нахождение ошибки как максимум модуля отклонения
  152.             // от в 4 раза более точного решения.
  153.             eps = max(eps, fabs(rd->y[i][k] - rd2->y[i*4][k*4]));
  154.             fprintf(out, "%f %f %f\n", x, t, rd->y[i][k]);
  155.             x += h;
  156.         }
  157.         // Запись значений при x = 0 и x = 0.5.
  158.         // (Первой колонкой t, второй и третьей -- значения)
  159.         fprintf(out_t, "%f %f %f\n", t, rd->y[0][k], rd->y[j][k]);
  160.  
  161.         t += tau;
  162.     }
  163.     fprintf(out_eps, "%d %d %f\n", X_CELLS_COUNT, T_CELLS_COUNT, eps);
  164.  
  165.     fclose(out);
  166.     fclose(out_t);
  167.     fclose(out_eps);
  168.  
  169.     return 0;
  170. }
  171.  
  172. double getX(int i, int N_x)
  173. {
  174.     return i * L / N_x;
  175. }
  176. double getT(int k, int N_t)
  177. {
  178.     return k * T_MAX / N_t;
  179. }
  180.  
  181. // Установка начальных условий на границе
  182. void setInitialConditions(int N_x, int N_t, double** y)
  183. {
  184.     for (int i = 0; i <= N_x; ++i)
  185.     {
  186.         y[i] = new double[N_t+1];
  187.         // Установка значений на границе t=0:
  188.         y[i][0] = uStart( getX(i, N_x) );
  189.     }
  190.     for (int k = 0; k <= N_t; ++k)
  191.     {
  192.         // Установка значений на границе x=0
  193.         y[ 0 ][k] = uLeft( getT(k, N_t) );
  194.         // Установка значений на границе x=L:
  195.         y[N_x][k] = uRight( getT(k, N_t) );
  196.     }
  197. }
  198.  
  199. // Схема Кранка-Николсона
  200. // a, b -- границы для x, a <= x <= b
  201. // c, d -- границы для t, c <= t <= d
  202. ReturnData* solve(int N_x, int N_t)
  203. {
  204.     const int n = N_x-1;
  205.  
  206.     double** y = new double*[N_x+1];
  207.     setInitialConditions(N_x, N_t, y);
  208.  
  209.     double tau = T_MAX / N_t;
  210.     double h   = L / N_x;
  211.     // Матричные коэффициенты (не меняются, поэтому сохранены скалярными
  212.     // величинами).
  213.     const double A = -0.5 / (h*h);
  214.     const double B = 1.0 / tau + 1.0 / (h*h);
  215.     const double C = -0.5 / (h*h);
  216.  
  217.     // Правые части уравнений в методе прогонки
  218.     double* F = new double[N_x];
  219.     // Массивы для метода прогонки.
  220.     double* C2 = new double[N_x];
  221.     double* F2 = new double[N_x];
  222.  
  223.     /****** Метод прогонки ******/
  224.     // См. https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm
  225.  
  226.     for (int k = 0; k < N_t; ++k) {
  227.         double t = tau * (k+1);
  228.  
  229.         /* Вычисление вектора F */
  230.  
  231.         for (int j = 1; j <= n; ++j)
  232.         {
  233.             F[j] = y[j][k]/tau;
  234.             F[j] += 0.5 * (y[j+1][k] - 2*y[j][k] + y[j-1][k]) / h / h;
  235.         }
  236.         // 1-ая строка матрица не может содержать элемент y_0, т.к. он
  237.         // является свободным слагаемым.
  238.         // Поэтому y_0 переносится в F.
  239.         F[1] -= A * y[ 0 ][k+1];
  240.         // n-ая строка матрица не может содержать элемент y_{n+1}, т.к. он
  241.         // является свободным слагаемым. (где n -- размер матрицы)
  242.         // Поэтому y_n+1 переносится в F.
  243.         F[n] -= C * y[n+1][k+1];
  244.  
  245.         /* Вычисление вспомогательных коэффициентов */
  246.  
  247.         for (int i = 1; i <= n-1; i++)
  248.         {
  249.             if (i == 1)
  250.             {
  251.                 C2[i] = C / B;
  252.                 F2[i] = F[i] / B;
  253.             }
  254.             else
  255.             {
  256.                 C2[i] = C                  / (B - A*C2[i-1]);
  257.                 F2[i] = (F[i] - A*F2[i-1]) / (B - A*C2[i-1]);
  258.             }
  259.         }
  260.         F2[n] = (F[n] - A*F2[n-1]) / (B - A*C2[n-1]);
  261.  
  262.         /* Вычисление y_j^{k+1} */
  263.  
  264.         y[n][k+1] = F2[n];
  265.         for (int j = n-1; j >= 1; j--)
  266.         {
  267.             y[j][k+1] = F2[j] - C2[j]*y[j+1][k+1];
  268.         }
  269.     }
  270.  
  271.     // Выходные данные передаются отдельной структурой.
  272.     ReturnData* rd = new ReturnData();
  273.     rd->y = y;
  274.  
  275.     return rd;
  276. }
  277.  
  278. // Схема Кранка-Николсона, вариант с условием теплоизоляции
  279. // (при x=L du/dx = 0)
  280. // a, b -- границы для x, a <= x <= b
  281. // c, d -- границы для t, c <= t <= d
  282. ReturnData* solve2(int N_x, int N_t)
  283. {
  284.     const int n = N_x-1;
  285.  
  286.     double** y = new double*[N_x+1];
  287.     setInitialConditions(N_x, N_t, y);
  288.  
  289.     double tau = T_MAX / N_t;
  290.     double h   = L / N_x;
  291.     // Матричные коэффициенты (не меняются, поэтому сохранены скалярными
  292.     // величинами).
  293.     const double A = -0.5 / (h*h);
  294.     const double B = 1.0 / tau + 1.0 / (h*h);
  295.     const double C = -0.5 / (h*h);
  296.  
  297.     // Правые части уравнений в методе прогонки
  298.     double* F = new double[N_x];
  299.     // Массивы для метода прогонки.
  300.     double* C2 = new double[N_x];
  301.     double* F2 = new double[N_x];
  302.  
  303.     /****** Метод прогонки ******/
  304.     // См. https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm
  305.  
  306.     for (int k = 0; k < N_t; ++k) {
  307.         double t = tau * (k+1);
  308.  
  309.         /* Вычисление вектора F */
  310.  
  311.         for (int j = 1; j <= n; ++j)
  312.         {
  313.             F[j] = y[j][k]/tau;
  314.             F[j] += 0.5 * (y[j+1][k] - 2*y[j][k] + y[j-1][k]) / h / h;
  315.         }
  316.         // 1-ая строка матрица не может содержать элемент y_0, т.к. он является
  317.         // свободным слагаемым.
  318.         // Поэтому y_0 переносится в F.
  319.         F[1] -= A * y[ 0 ][k+1];
  320.         // При условии теплоизоляции на правой границе
  321.         // f_n модифицировать не нужно.
  322.         // F[n] -= C * y[n+1][k+1];
  323.  
  324.         /* Вычисление вспомогательных коэффициентов */
  325.  
  326.         for (int i = 1; i <= n-1; i++)
  327.         {
  328.             if (i == 1)
  329.             {
  330.                 C2[i] = C / B;
  331.                 F2[i] = F[i] / B;
  332.             }
  333.             else
  334.             {
  335.                 C2[i] = C                  / (B - A*C2[i-1]);
  336.                 F2[i] = (F[i] - A*F2[i-1]) / (B - A*C2[i-1]);
  337.             }
  338.         }
  339.         // Поскольку в этом уравнении на правой границе условие теплоизоляции,
  340.         // в методе прогонки используется A_L = A + C;
  341.  
  342.         double A_L = A + C;
  343.         F2[n] = (F[n] - A_L*F2[n-1]) / (B - A_L*C2[n-1]);
  344.  
  345.         // Использование производной с точностью O(h),
  346.         // при большом числе точек по X решение уходит на
  347.         // бесконечность.
  348.         // double B_L = B + C;
  349.         // F2[n] = (F[n] - A*F2[n-1]) / (B_L - A*C2[n-1]);
  350.  
  351.         /* Вычисление y_j^{k+1} */
  352.  
  353.         y[n][k+1] = F2[n];
  354.         for (int j = n-1; j >= 1; j--)
  355.         {
  356.             y[j][k+1] = F2[j] - C2[j]*y[j+1][k+1];
  357.         }
  358.         // Поскольку на правой границе условие теплоизоляции,
  359.         // функция на границе принимают следующее значение:
  360.         y[n+1][k+1] = y[n-1][k+1];
  361.     }
  362.  
  363.     // Выходные данные передаются отдельной структурой.
  364.     ReturnData* rd = new ReturnData();
  365.     rd->y = y;
  366.  
  367.     return rd;
  368. }
Advertisement
Add Comment
Please, Sign In to add comment