next up previous contents
Next: Практическое использование средств разработки Up: PVM - параллельная виртуальная Previous: Матричное умножение   Contents

Одномерное температурное уравнение

Здесь представлена программа PVM, которая вычисляет температурную диффузию в некой среде - в данном случае это проводник. Уравнение, описывающие одномерную температурную диффузию тонкого проводника:


\begin{displaymath}
\frac{\partial A}{\partial t}=\frac{\partial ^{2}A}{\partial x^{2}}\end{displaymath}

Дискретизация:


\begin{displaymath}
\frac{A_{i+1,j}-A_{i,j}}{\Delta x}=\frac{A_{i,j+1}-2A_{i,j}+A_{i,j-1}}{\Delta x^{2}}\end{displaymath}

В результате получаем явную формулу:


\begin{displaymath}
A_{i+1,j}=A_{i,j}+\frac{\Delta t}{\Delta x^{2}}(A_{i,j+1}-2A_{i,j}+A_{i,j-1})\end{displaymath}

Начальные и граничные условия:

$A(t,0)=0,A(t,1)=0$- для всех $t$;

$A(0,x)=sin(\pi x)$- для $0\leq t\leq 1$.

Псевдокодом для таких вычислений будет следующий:

for i = 1:tsteps-1;

  t = t+dt;

  a(i+1,1)=0;

  a(i+1,n+2)=0;

for j = 2:n+1;

  a(i+1,j)=a(i,j) + mu*(a(i,j+1)-2*a(i,j)+a(i,j-1));

end;

  t;

  a(i+1,1:n+2);

  plot(a(i,:))

end

Для данного примера используется модель ``ведущий - ведомый''. Ведущий - heat.c порождает пять копий программы heatslv. Ведомые параллельно вычисляют температурную диффузию ``подсекций'' проводника. На каждом шаге, ведомые обмениваются граничной информацией: в нашем случае температура проводника ``определяет'' границы между процессорами.

В программе heat.c массив solution будет содержать результаты решений уравнения температурной диффузии на каждом шаге. Этот массив - в формате xgraph - будет ``выходным'' при завершении программы (xgraph - программа, предназначенная для вывода данных на графопостроитель). Сначала порождаются задачи heatslv. Далее, подготавливается исходный набор данных. Обратите внимание на то, что для конца проводника значением начальной температуры будет ноль.

Потом, четыре раза исполняется основная часть программы - каждый раз с новым значением $\Delta t$. Для определения продолжительности времени, прошедшего от начала вычислений данной фазы, используется таймер. Начальный набор данных рассылается задачам heatslv. При каждой посылке совместно с начальным набором данных передаются идентификаторы задач-соседей слева и справа. Задачи heatslv будут использовать эту информацию при граничных коммуникациях. (В противном случае нужно использовать групповые вызовы PVM для отображения задач на сегменты проводника. При использовании групповых вызовов можно избежать явных указаний идентификаторов задач ведомых процессов).

После рассылки исходных данных ведущий процесс ожидает получения результатов. Когда результаты поступают, они интегрируются в результирующую матрицу, вычисляется затраченное время, и решение записывается в xgraph-файл.

Как только все четыре фазы завершены, а результаты сохранены, ведущая программа выводит на экран информацию о временных затратах и принудительно завершает ведомые процессы.

Программа-пример heat.c:

/*

heat.c

Применение PVM для решения дифференциального уравнения,

описывающего простейшую температурную диффузию,

с использованием 1 ведущей программы и 5 ведомых

Ведущая программа формирует данные, посылает их ведомым

и ожидает результаты, возвращаемые ведомыми.

Файлы с полученными результатами

готовы для использования программой xgraph

*/

#include "pvm3.h"

#include <stdio.h>

#include <math.h>

#include <time.h>

#define SLAVENAME "heatslv"

#define NPROC 5

#define TIMESTEP 100

#define PLOTINC 10

#define SIZE 1000

int num_data = SIZE/NPROC;

 

main()

{

  int mytid, task_ids[NPROC], i, j;

  int left, right, k, l;

  int step = TIMESTEP;

  int info;

  double init[SIZE], solution[TIMESTEP][SIZE];

  double result[TIMESTEP*SIZE/NPROC], deltax2;

  FILE *filenum;

  char *filename[4][7];

  double deltat[4];

  time_t t0;

  int etime[4];

  filename[0][0] = "graph1";

  filename[1][0] = "graph2";

  filename[2][0] = "graph3";

  filename[3][0] = "graph4";

  deltat[0] = 5.0e-1;

  deltat[1] = 5.0e-3;

  deltat[2] = 5.0e-6;

  deltat[3] = 5.0e-9;

  /* регистрация в PVM */

  mytid = pvm_mytid();

  /* порождение ведомых задач */

  info = pvm_spawn(SLAVENAME,(char **)0, 

   PvmTaskDefault,"", NPROC,task_ids);

  /* создание исходного набора данных */

  for (i = 0; i < SIZE; i++)

    init[i] = sin(M_PI * ( (double)i /

        (double)(SIZE-1) ));

    init[0] = 0.0;

    init[SIZE-1] = 0.0;

    /* четырехразовое выполнение с разными

         значениями дельты t */

    for (l = 0; l < 4; l++) {

      deltax2 = (deltat[l]/pow(1.0/(double)SIZE,2.0));

      /*засекаем время*/

      time(&t0);

      etime[l] = t0;

      /* передача исходных данных ведомым */

      /* дополнение информацией о соседях -

        для реализации возможности

       обмена граничными данными */

      for (i = 0; i < NPROC; i++) {

        pvm_initsend(PvmDataDefault);

        left = (i == 0) ? 0 : task_ids[i-1];

        pvm_pkint(&left, 1, 1);

        right = (i == (NPROC-1)) ? 0 : task_ids[i+1];

        pvm_pkint(&right, 1, 1);

        pvm_pkint(&step, 1, 1);

        pvm_pkdouble(&deltax2, 1, 1)

        pvm_pkint(&num_data, 1, 1);

        pvm_pkdouble(&init[num_data*i], num_data, 1);

        pvm_send(task_ids[i], 4);

      }

      /* ожидание результатов */

      for (i = 0; i < NPROC; i++) {

        pvm_recv(task_ids[i], 7);

        pvm_upkdouble(&result[0], num_data*TIMESTEP, 1);

       /* ``обновление'' решения */

        for (j = 0; j < TIMESTEP; j++)

          for (k = 0; k < num_data; k++)

            solution[j][num_data*i+k] = result[wh(j,k)];

      }

      /* остановка формирования временных интервалов */

      time(&t0);

      etime[l] = t0 - etime[l];

      /* получение выходных данных */

      filenum =(fopen_filename[l][0], "w");

      fprintf(filenum,

        "TitleText: Wire Heat over Delta Time: %e\n",

         deltat[l]);

      fprintf(filenum,

        "XUnitText: Distance\nYUnitText: Heat\n");

      for (i = 0; i < TIMESTEP; i = i + PLOTINC) {

        fprintf(filenum,"\"Time index: %d\n",i);

        for (j = 0; j < SIZE; j++)

          fprintf(filenum,"%d %e\n",j, solution[i][j]);

          fprintf(filenum,"\n");

      }

      fclose (filenum);

    }

    /* вывод на экран информации о

      временных интервалах */

    printf("Problem size: %d\n",SIZE);

    for (i = 0; i < 4; i++)

      printf("Time for run %d: %d sec\n",i,etime[i]);

    /* принудительное завершение ведомых процессов */

    for (i = 0; i < NPROC; i++) pvm_kill(task_ids[i]);

      pvm_exit();

  }

 

  int wh(x, y)

  int x, y;

  {

    return(x*num_data+y);

  }

Программы heatslv фактически реализуют вычисление температурной диффузии ``на протяжении'' проводника. Ведомая программа состоит из бесконечного цикла: прием исходного набора данных, итеративное вычисление результата на основе этого набора (с обменом граничной информацией с соседями при каждой итерации) и посылка результирующего частичного решения назад, ведущему процессу.

Вместо создания бесконечных циклов в ведомых задачах, можно передавать специальные сообщения с приказами о завершении. Однако во избежание сложностей, связанных с обменом сообщениями, в ведомых задачах используются бесконечные циклы, а завершаются они принудительно из ведущей программы. Третьим вариантом может быть реализация с ведомыми, исполняющимися только один раз и самостоятельно завершающимися после обработки единственных наборов данных, полученных от ведущего. Если бы этот вариант был воплощен, то к полезной нагрузке добавилась бы бесполезная.

В течение каждого шага каждой фазы, происходит обмен граничными значениями температурных матриц. Сначала левосторонние граничные элементы передаются задаче-соседу слева и соответствующие граничные элементы принимаются от задачи-соседа справа. Затем, симметрично, правосторонние граничные элементы передаются правому соседу, а соответствующие - принимаются от левого. Идентификаторы задач-соседей проверяются с целью гарантирования того, что не возникнет попыток передать сообщения несуществующим задачам (или принять их).

Программа-пример heatslv.c:

/*

heatslv.c

Ведомые принимают исходные данные от хоста,

обмениваются граничной информацией с соседями

и вычисляют изменение температуры проводника.

Это делается за несколько итераций -

``под руководством'' ведущего

*/

#include "pvm3.h"

#include <stdio.h>

int num_data;

 

main()

{

  int mytid, left, right, i, j, master;

  int timestep;

  double *init, *A;

  double leftdata, rightdata, delta, leftside,

     rightside;

  /* регистрация в PVM */

  mytid = pvm_mytid();

  master = pvm_parent();

  /* прием своих данных от ведущей программы */

  while(1) {

    pvm_recv(master, 4);

    pvm_upkint(&lef, 1, 1);

    pvm_upkint(&right, 1, 1);

    pvm_upkint(&timestep, 1, 1);

    pvm_upkdouble(&delta, 1, 1);

    pvm_upkint(&num_data, 1, 1);

    init = (double *) malloc(num_data*sizeof(double));

    pvm_upkdouble(init, num_data, 1);

    /* копирование исходных данных

      в свой рабочий массив */

    A = (double *)

      malloc(num_data * timestep * sizeof(double));

    for (i = 0; i < num_data; i++) A[i] = init[i];

     /* реализация вычисления */

     for (i = 0; i < timestep-1; i++) {

      /* обмен граничной информацией

        со своими соседями */

      /* передача налево, прием справа */

      if (left != 0) {

        pvm_initsend(PvmDataDefault);

        pvm_pkdouble(&A[wh(i,0)],1,1);

        pvm_send(left, 5);

      }

      if (right != 0) {

        pvm_recv(right, 5);

        pvm_upkdouble(&rightdata, 1, 1);

        /* передача направо, прием слева */

        pvm_initsend(PvmDataDefault);

        pvm_pkdouble(&A[wh(i,num_data-1)],1,1);

        pvm_send(right, 6);

      }

      if (left != 0) {

        pvm_recv(left, 6);

        pvm_upkdouble(&leftdata, 1, 1);

      }

      /* выполнение вычислений данной итерации */

      for (j = 0; j < num_data; j++) {

        leftside = (j == 0) ? leftdata : A[wh(i,j-1)];

        rightside =

        (j == (num_data-1)) ? rightdata : A[wh(i,j+1)];

        if ((j == 0) && (left == 0))

          A[wh(i+1,j)] = 0.0;

        else if ((j == (num_data-1)) && (right == 0))

          A[wh(i+1,j)] = 0.0;

        else

          A[wh(i+1,j)] =

          A[wh(i,j)]+

          delta*(rightside-2*A[wh(i,j)]+leftside);

      }

    }

    /* передача результатов назад ведущей программе */

    pvm_initsend(PvmDataDefault);

    pvm_pkdouble(&A[0],num_data*timestep,1);

    pvm_send(master,7);

  }

  /* только при удачном исходе */

  pvm_exit();

}

 

int wh(x, y);

int x, y;

{

  return(x*num_data+y);

}


next up previous contents
Next: Практическое использование средств разработки Up: PVM - параллельная виртуальная Previous: Матричное умножение   Contents
2004-06-22