Advertisement
Guest User

Untitled

a guest
Apr 7th, 2020
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.47 KB | None | 0 0
  1. #include <mpi.h>
  2.  
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <iostream>
  6. #include <memory>
  7.  
  8. #define ALPHA 100000.f              // параметр правой чачти
  9. #define N 144                       // размерность сетки (количество ячеек)
  10. #define EPS 1E-8                    // условие отстановы итерационного метода
  11. #define MAX_ITERS 10000             // максимальное число итераций
  12.  
  13. #define EPS_COMP 1E-2               // погрешность сравнения результатов
  14.  
  15. #define MASTER 0                    // ID мастер-процесса
  16.  
  17. // глобальные переменные
  18. const double h = 2.0 / (N - 1.0);   // размер шага вдоль осей x, y и z
  19. int i0;                             // переменная для сопоставления отдельных блоков цельному параллелепипеду
  20. int current_iteration;              // текущая итерация
  21. int my_rank, n_procs;               // данный процесс, число процессов
  22.  
  23. // искомая функция фи
  24. double CalculateFunc(const double x, const double y, const double z);
  25. // правая часть уравнения
  26. double CalculateRo(const double x, const double y, const double z);
  27. // инициализация 3-хмерного массива + инициализация границ 3х-мерного пространства
  28. void InitBounds(double* A, double* f, const int Im_min, const int Im_max, const int Im_bound_up, const int Im_bound_down);
  29. // перегрузка, что не пересчитывать f несколько раз
  30. void InitBounds(double* A, const int Im_min, const int Im_max, const int Im_bound_up, const int Im_bound_down);
  31. // рассчет новых элементов матрицы A перед обменом
  32. double DoYakobiIteration(const int imin, const int imax, double* A, double* A_next, double* f, const double _covergence_local);
  33. // непараллельное решение задачи методом Якоби
  34. double CalculateSequentialJacobi();
  35. // параллельное решение задачи методом Якоби
  36. double CalculateParallelJacobi();
  37. // проверка полученного результата
  38. void CompareResults(double* result, const int step_max);
  39.  
  40. int main(int argc, char** argv) {
  41.     MPI_Init(&argc, &argv);                     // инициализация MPI
  42.     MPI_Comm_size(MPI_COMM_WORLD, &n_procs);    // определение числа процессов в области связи
  43.     MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);    // определение номера вызывающего процесса
  44.     if (my_rank == MASTER) std::cout << "Всего процессов: " << n_procs << std::endl << std::endl;
  45.  
  46.     double covergence;
  47.     double t_start, t_end;
  48.     if (my_rank == MASTER && n_procs == 1) {
  49.         MPI_Barrier(MPI_COMM_WORLD);
  50.         t_start = MPI_Wtime();
  51.         covergence = CalculateSequentialJacobi();
  52.         MPI_Barrier(MPI_COMM_WORLD);
  53.         t_end = MPI_Wtime();
  54.         std::cout << "Условие сходимости: " << covergence << std::endl;
  55.         std::cout << "Число итераций: " << current_iteration << std::endl;
  56.         std::cout << "Затраченное время: " << t_end - t_start << std::endl;
  57.     } else if (N % n_procs == 0 && n_procs > 1) {
  58.         MPI_Barrier(MPI_COMM_WORLD);
  59.         t_start = MPI_Wtime();
  60.         covergence = CalculateParallelJacobi();
  61.         MPI_Barrier(MPI_COMM_WORLD);
  62.         t_end = MPI_Wtime();
  63.         if (my_rank == MASTER) {
  64.             std::cout << "Условие сходимости: " << covergence << std::endl;
  65.             std::cout << "Число итераций: " << current_iteration << std::endl;
  66.             std::cout << "Затраченное время: " << t_end - t_start << std::endl;
  67.         }
  68.     } else {
  69.         if (my_rank == MASTER)
  70.             std::cout << "Программа завершена, скорректруйте N (N % n_procs == 0)!" << std::endl;
  71.         MPI_Finalize();
  72.         return -1;
  73.     }
  74.  
  75. prog_exit:
  76.     MPI_Finalize();
  77.     return 0;
  78. }
  79.  
  80. // искомая функция фи
  81. double CalculateFunc(const double x, const double y, const double z) {
  82.     return x * x + y * y + z * z;
  83. };
  84.  
  85. // правая часть уравнения
  86. double CalculateRo(const double x, const double y, const double z) {
  87.     return 6 - ALPHA * (x * x + y * y + z * z);
  88. }
  89.  
  90. // инициализация 3-хмерного массива + инициализация границ 3х-мерного пространства
  91. void InitBounds(double* A, double* f, const int Im_min, const int Im_max, const int Im_bound_up, const int Im_bound_down) {
  92.     for (int i = Im_min; i < Im_max; i++)
  93.         for (int j = 0; j < N; j++)
  94.             for (int k = 0; k < N; k++) {
  95.                 // на границе
  96.                 if (i == Im_bound_up || j == 0 || k == 0 || i == Im_bound_down || j == N - 1 || k == N - 1)
  97.                     A[i * N * N + j * N + k] = CalculateFunc((i + i0) * h, j * h, k * h);
  98.                 else
  99.                     // внутри области решения
  100.                     A[i * N * N + j * N + k] = 0.0;
  101.                 // рассчитываем правые части
  102.                 f[i * N * N + j * N + k] = CalculateRo((i + i0) * h, j * h, k * h);
  103.             }
  104. };
  105.  
  106. // перегрузка функции
  107. void InitBounds(double* A, const int Im_min, const int Im_max, const int Im_bound_up, const int Im_bound_down) {
  108.     for (int i = Im_min; i < Im_max; i++)
  109.         for (int j = 0; j < N; j++)
  110.             for (int k = 0; k < N; k++) {
  111.                 if (i == Im_bound_up || j == 0 || k == 0 || i == Im_bound_down || j == N - 1 || k == N - 1)
  112.                     A[i * N * N + j * N + k] = CalculateFunc((i + i0) * h, j * h, k * h);
  113.                 else
  114.                     A[i * N * N + j * N + k] = 0.0;
  115.             }
  116. };
  117.  
  118. // рассчет новых элементов матрицы A перед обменом
  119. double DoYakobiIteration(const int imin, const int imax, double* A, double* A_next, double* f, const double _covergence_local) {
  120.     double covergence_local = _covergence_local;
  121.     double coef = 1.0 / ((6.0 / h * h) + ALPHA);
  122.     double F1, F2, F3;
  123.     for (int i = imin; i < imax; i++) {
  124.         for (int j = 1; j < N - 1; j++) {
  125.             for (int k = 1; k < N - 1; k++) {
  126.                 F1 = A[(i - 1) * N * N + j * N + k] + A[(i + 1) * N * N + j * N + k];
  127.                 F2 = A[i * N * N + (j - 1) * N + k] + A[i * N * N + (j + 1) * N + k];
  128.                 F3 = A[i * N * N + j * N + k - 1] + A[i * N * N + j * N + k + 1];
  129.                 A_next[i * N * N + j * N + k] = coef * (F1 + F2 + F3 - f[i * N * N + j * N + k]);
  130.  
  131.                 covergence_local = std::max(covergence_local, fabs(A_next[i * N * N + j * N + k] - A[i * N * N + j * N + k]));
  132.             }
  133.         }
  134.     }
  135.     return covergence_local;
  136. };
  137.  
  138. // непараллельное решение задачи методом Якоби
  139. double CalculateSequentialJacobi() {
  140.     double* A = new double[N * N * N];       // старые значения
  141.     double* A_next = new double[N * N * N];  // новые значения
  142.     double* f = new double[N * N * N];       // рассчитанные правые части
  143.     double covergence, covergence_local;
  144.    
  145.     i0 = 0;
  146.     int Im_min = 0;
  147.     int bound_up = 0;
  148.     int bound_down = N - 1;
  149.  
  150.     InitBounds(A, f, Im_min, N, bound_up, bound_down);
  151.     InitBounds(A_next, Im_min, N, bound_up, bound_down);
  152.  
  153.     current_iteration = 0;
  154.     covergence_local = 0.f;
  155.     // итерации метода Якоби
  156.     do {
  157.         covergence_local = 0;
  158.         covergence_local = DoYakobiIteration(1, N - 1, A, A_next, f, covergence_local);
  159.         std::swap(A, A_next);                   // обмен значениями
  160.         covergence = covergence_local;
  161.         current_iteration++;
  162.     } while (covergence > EPS && current_iteration < MAX_ITERS);
  163.  
  164.     delete[] A_next;
  165.     delete[] f;
  166.     // return A;
  167.     return covergence;
  168. };
  169.  
  170. // параллельное решение задачи методом Якоби
  171. double CalculateParallelJacobi() {
  172.     // идентификаторы для перессылки сообщений
  173.     MPI_Request up_s, down_s;
  174.     MPI_Request up_r, down_r;
  175.  
  176.     int Im_min, Im_max;
  177.     int bound_up, bound_down;
  178.     double covergence;              // условие выхода
  179.     int Im_block;                   // размер блока для процесса
  180.  
  181.     // место под выделаемые из общего параллелепипеда блоки
  182.     double* A;                      // старые значения
  183.     double* A_next;                 // новые значения
  184.    
  185.     double* f;                      // рассчитанные правые части
  186.  
  187.     // выделение определённому процессу куска
  188.     // граничный блок
  189.     if (my_rank == 0 || my_rank == n_procs - 1)
  190.         Im_block = N / n_procs + 1;
  191.     else
  192.         //внутренний блок
  193.         Im_block = N / n_procs + 2;
  194.  
  195.     A = new double[Im_block * N * N];
  196.     A_next = new double[Im_block * N * N];
  197.     f = new double[Im_block * N * N];
  198.     i0 = my_rank * N / n_procs - (my_rank != 0);
  199.  
  200.     if (my_rank == 0) {
  201.         Im_min = 0;
  202.         Im_max = Im_block;
  203.         bound_up = 0;
  204.         bound_down = -1;
  205.     } else if (my_rank == n_procs - 1) {
  206.         Im_min = 0;
  207.         Im_max = Im_block;
  208.         bound_up = -1;
  209.         bound_down = Im_block - 1;
  210.     } else {
  211.         Im_min = 0;
  212.         Im_max = Im_block;
  213.         bound_up = -1;
  214.         bound_down = -1;
  215.     }
  216.  
  217.     // здесь вычисляются границы для каждого блока
  218.     // для потоков больше 0 границы вычисляются неверно,
  219.     // поэтому прежде чем вычислять внутренние точки методом Якоби,
  220.     // нужно принять от других потоков внешние границы
  221.     InitBounds(A, f, Im_min, Im_max, bound_up, bound_down);
  222.     InitBounds(A_next, Im_min, Im_max, bound_up, bound_down);
  223.  
  224.     current_iteration = 0;
  225.     do {
  226.         current_iteration++;
  227.         double covergence_local = 0.f;
  228.         int _i = 1;
  229.         int _n = Im_block - 1;
  230.         if (my_rank != 0 && N / n_procs != 1) {
  231.             int i = 1;
  232.             _i++;
  233.             covergence_local = DoYakobiIteration(i, i + 1, A, A_next, f, covergence_local);
  234.             MPI_Isend(&A_next[N * N], N * N, MPI_DOUBLE, my_rank - 1, 0, MPI_COMM_WORLD, &down_s);
  235.             MPI_Irecv(&A_next[0], N * N, MPI_DOUBLE, my_rank - 1, 0, MPI_COMM_WORLD, &down_r);
  236.         }
  237.  
  238.         if (my_rank != n_procs - 1 && N / n_procs != 1) {
  239.             int i = Im_block - 2;
  240.             _n--;
  241.             covergence_local = DoYakobiIteration(i, i + 1, A, A_next, f, covergence_local);
  242.             // неблокирующая передача сообщения
  243.             MPI_Isend(&A_next[(Im_block - 2) * N * N], N * N, MPI_DOUBLE, my_rank + 1, 0, MPI_COMM_WORLD, &up_s);
  244.             // неблокирующий приём сообщения
  245.             MPI_Irecv(&A_next[(Im_block - 1) * N * N], N * N, MPI_DOUBLE, my_rank + 1, 0, MPI_COMM_WORLD, &up_r);
  246.         }
  247.         covergence_local = DoYakobiIteration(_i, _n, A, A_next, f, covergence_local);
  248.         // для (N/n_procs == 1) нужно сначала, наверное, принимать сообщения о внешних границах,
  249.         // потом вызвать метод DoYakobiIteration,
  250.         // потом отправлять вычисленную (внутреннюю) границу
  251.         if (N / n_procs == 1) {
  252.             if (my_rank == 0)
  253.                 _i = 0;
  254.             if (my_rank != 0) {
  255.                 // неблокирующая передача сообщения
  256.                 MPI_Isend(&A_next[_i * N * N], N * N, MPI_DOUBLE, my_rank - 1, 0, MPI_COMM_WORLD, &down_s);
  257.                 // неблокирующий приём сообщения
  258.                 MPI_Irecv(&A_next[(_i - 1) * N * N], N * N, MPI_DOUBLE, my_rank - 1, 0, MPI_COMM_WORLD, &down_r);
  259.             }
  260.             if (my_rank != n_procs - 1) {
  261.                 // неблокирующая передача сообщения
  262.                 // N * N – количество точек на границе (YxZ),
  263.                 // эта граница образуется при пересечении параллелепипеда с плоскостью х = _i.
  264.                 MPI_Isend(&A_next[_i * N * N], N * N, MPI_DOUBLE, my_rank + 1, 0, MPI_COMM_WORLD, &up_s);
  265.                 // неблокирующий приём сообщения
  266.                 MPI_Irecv(&A_next[(_i + 1) * N * N], N * N, MPI_DOUBLE, my_rank + 1, 0, MPI_COMM_WORLD, &up_r);
  267.             }
  268.         }
  269.         // cохранение результата в адресном пространстве всех процессов (covergence_local -> covergence)
  270.         MPI_Allreduce(&covergence_local, &covergence, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
  271.  
  272.         // если действие производится не над первым процессом
  273.         if (my_rank != 0) {
  274.             MPI_Wait(&down_s, MPI_STATUS_IGNORE);
  275.             MPI_Wait(&down_r, MPI_STATUS_IGNORE);
  276.         }
  277.         // eсли действие производится не над последним процессом
  278.         if (my_rank != n_procs - 1) {
  279.             MPI_Wait(&up_s, MPI_STATUS_IGNORE);
  280.             MPI_Wait(&up_r, MPI_STATUS_IGNORE);
  281.         }
  282.         std::swap(A, A_next);               // обмен значениями  
  283.         covergence = covergence_local;
  284.     } while (covergence >= EPS && current_iteration < MAX_ITERS);
  285.  
  286.     // проверка результата
  287.     if (my_rank == 0)
  288.         Im_min = 0;
  289.     else {
  290.         if (my_rank == n_procs - 1)
  291.             Im_min = 1;
  292.         else {
  293.             Im_min = 1;
  294.             Im_max--;
  295.         }
  296.     }
  297.     CompareResults(A, Im_max);
  298.     delete[] A_next;
  299.     delete[] f;
  300.  
  301.     // return A;
  302.     return covergence;
  303. };
  304.  
  305. // проверка результатов
  306. void CompareResults(double* result, const int step_max) {
  307.     const int step_min = 0;
  308.     int error_count = 0;
  309.     for (int i = i0 + step_min, p = step_min; i < N && p < step_max; i++, p++) {
  310.         double x = i * h;
  311.         for (int j = 0; j < N; j++) {
  312.             double y = j * h;
  313.             for (int k = 0; k < h; k++) {
  314.                 double z = k * h;
  315.                 if (fabs(result[p * N * N + j * N + k] - CalculateFunc(x, y, z)) >= EPS_COMP) {
  316.                     std::cout << i * N * N + j * N + k << "\t";
  317.                     std::cout << CalculateFunc(x, y, z) << "\t\t";
  318.                     std::cout << p * N * N + j * N + k << "\t";
  319.                     std::cout << result[p * N * N + j * N + k] << "\t";
  320.                     std::cout << "Error!" << std::endl;
  321.                     error_count++;
  322.                 }
  323.             }
  324.         }
  325.     }
  326.     if (error_count != 0)
  327.         std::cout << error_count << " Ошибка при работе процесса " << my_rank << "!" << std::endl;
  328. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement