Next:ЗАКЛЮЧЕНИЕ
Up:2. ИСПОЛЬЗОВАНИЕ БИБЛИОТЕКИ ПАРАЛЛЕЛЬНЫХ ПОДПРОГРАММ Aztec
Prev:2.2. Конфигурационные параметры библиотеки Aztec

2.3. Основные подпрограммы библиотеки Aztec

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

Функция идентификации процессоров AZ_processor_info

Эта функция является аналогом функции whoami в PSE и функций MPI_Comm_rank, MPI_Comm_size в MPI.

CALL AZ_processor_info(proc_config)

Информация возвращается в массив
integer proc_config(0:AZ_PROC_SIZE)

IAM= proc_config(AZ_node) - номер вызывающего процессора;
NPROCS= proc_config(AZ_N_procs) - общее число процессоров, доступных программе.

Функция рассылки информации по процессорам AZ_broadcast

Это аналог функции MPI_Bcast, ее реализация выполнена в стиле PVM: рассылаемая информация сначала упаковывается в системный буфер, а затем посылается одной командой. Рассылать может только 0-й процессор.

CALL AZ_broadcast(n,4,proc_config,AZ_PACK) -упаковываем в буфер целую переменную n;
CALL AZ_broadcast(d,8,proc_config,AZ_PACK) -упаковываем в буфер переменную d типа real*8;
CALL AZ_broadcast(NULL,0,proc_config,AZ_SEND) -посылаем буфер.

Ничто не запрещает вместо этих функций использовать MPI-функцию MPI_Bcast.

Функция определения параметров декомпозиции матрицы AZ_read_update

Определяет, сколько строк глобальной матрицы должно находиться в данном процессоре и номера этих строк.

CALL AZ_read_update(N_update, update, proc_config, nz, 1, 0)

N_update- целая переменная, возвращает количество строк на процессоре;
update- целый массив размерности не меньше, чем N_update+1, возвращает номера строк в глобальной нумерации;
nz- размерность глобальной матрицы (входной параметр).

Два последних параметра - константы.

После этого этапа в каждом процессоре должна быть сформирована размещенная в нем часть разреженной матрицы, представленная в виде двух массивов:

Этот этап целиком и полностью возлагается на программиста и требует от него некоторой изобретательности.
Для VBR формата требуется сохранять больше информации (в данном пособии мы его рассматривать не будем). Кроме того, должны быть сформированы векторы правых частей b и начальное приближение решения x (только расположенные в данном процессоре части).

Функция преобразования глобальной индексации в локальную AZ_transform

CALL AZ_transform(proc_config, external, bindx, val, update,
$     update_index, extern_index, data_org,
$     N_update, NULL, NULL, NULL, NULL, AZ_MSR_MATRIX)

Входные параметры этой процедуры proc_config, bindx, val, update, N_update рассмотрены выше. Выходные параметры - целые массивы external, update_index, extern_index, data_org размерности не меньше, чем N_update + 1 - будут передаваться процедуре решения системы уравнений AZ_solve. Параметры, имеющие значения NULL, в случае MSR формата не используются.

Перед обращением к процедуре решения системы вектор правых частей и начальное приближение должны быть переопределены в соответствии с новой индексацией.

do i = 0, N_update - 1
   xn(update_index(i)) = x(i)
   bn(update_index(i)) = b(i)
end do

Функция инициализации предопределенных параметров решателя AZ_defaults

CALL AZ_defaults(options, params)

В данной процедуре оба параметра выходные и были подробно рассмотрены выше. Установки по умолчанию могут быть переопределены:

options(AZ_solver)=AZ_cgs- выбор метода решения CGS;
options(AZ_precond)=AZ_dom_decomp- выбор переобуславливателя;
options(AZ_subdomain_solve)=AZ_ilu- неполное LU разложение;
options(AZ_output)=AZ_none- диагностику не выдавать;
options(AZ_max_iter)=1000- максимальное число итераций 1000;
params(AZ_tol)=1.d-8- критерий сходимости 10-8.

Функция решения системы линейных алгебраических уравнений AZ_solve

Эта функция является основной "рабочей лошадкой" пакета.

CALL AZ_solve(xn, bn, options, params, NULL, bindx, NULL, NULL,
$     NULL, val, data_org, status, proc_config)

Параметры NULL не используются в случае MSR формата хранения матрицы. Выходными параметрами являются xn - распределенное по процессорам решение системы и рассмотренный выше массив status.

При использовании пакета Aztec самой сложной для программиста задачей является формирование надлежащим образом распределенной по процессорам матрицы.

На Рис. 10 представлен пример разреженной матрицы 6x6. Предположим, что мы хотим распределить ее в 3 процессора (нумерация процессоров с 0). Если бы мы воспользовались процедурой AZ_read_update, то, скорее всего, для каждого процессора N_update было бы установлено равным 2, и в массив update были бы соответственно занесены значения (0,1),(2,3),(4,5). Однако, для общности, рассмотрим реализацию другого варианта распределения. Пусть 0-й процессор хранит нулевую, первую и третью строки; 1-й процессор - одну четвертую строку; 2-й процессор - вторую и пятую строки. Этим самым мы однозначно задаем значения переменной N_update и массива update. Наша задача состоит в том, чтобы заполнить массивы bindx и val так, чтобы они адекватно описывали принятое распределение.

Массив val формируется следующим образом. Сначала в этот массив заносятся диагональные элементы хранимых строк, одна позиция пропускается (это сделано для соответствия с bindx) и затем, строка за строкой, в массив заносятся недиагональные ненулевые элементы (в том порядке, в котором они перечислены в массиве update).

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

Рис. 10. Разреженная исходная матрица

proc0:
  N_update: 3
  update: 0 1 3
  index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
  bindx: 4 7 9 14 1 3 4 0 3 0 1 2 4 5
  val: a00 a11 a33 - a01 a03 a04 a10 a13 a30 a31 a32 a34 a35
_______________________________________________________________
proc1:
  N_update: 1
  update: 4
  index: 0 1 2 3 4
  bindx: 2 5 0 2 3
  val: a44 - a40 a42 a43
______________________________
proc2:
  N_update: 2
  update: 2 5
  index: 0 1 2 3 4 5 6 7
  bindx: 3 6 8 3 4 5 2 3
  val: a22 a55 - a23 a24 a25 a52 a53
_________________________________________

На практике, как было сказано выше, библиотека Aztec применяется для решения систем линейных алгебраических уравнений, возникающих при решении дифференциальных уравнений в частных производных. Для таких матриц, как правило, заранее известно количество ненулевых матричных элементов в строке и их местоположение в матрице, поэтому алгоритмы заполнения массивов val и bindx бывают достаточно простые. В заключение рассмотрим пример программы, в которой Aztec используется для решения системы уравнений, возникающей при конечно-разностной аппроксимации 3-х мерного оператора Лапласа. Мы не будем связывать систему ни с какой реальной физической задачей, а вместо этого будем формировать правую часть таким образом, чтобы получить заранее известное решение, например,
x(i) = i + 1.

Пример 2.1. Программа решения разреженной системы линейных алгебраических уравнений с помощью библиотеки Aztec.

    program Aztec
C Параметры:
C ndim - максимальное число неизвестных,
C lda  - максимальный размер матрицы
    parameter (ndim = 125000, lda = 7*ndim)
    include 'mpif.h'
    include 'az_aztecf.h'
    integer n, nz
C
    integer i, ierror, recb(0:63), disp(0:63)
    double precision b(0:ndim), x(0:ndim), tmp(0:ndim)
    double precision s, time, total
C
    integer IAM, NPROCS
    integer N_update
    integer proc_config(0:AZ_PROC_SIZE), options(0:AZ_OPTIONS_SIZE)
    integer update(0:ndim), external(0:ndim), data_org(0:ndim)
    integer update_index(0:ndim), extern_index(0:ndim)
    integer bindx(0:lda)
    double precision params(0:AZ_PARAMS_SIZE)
    double precision status(0:AZ_STATUS_SIZE)
    double precision val(0:lda)
    common /global/ n
C Инициализация MPI (для Aztec не обязательна, если не использовать
C MPI функции)
    CALL MPI_Init(ierror)
C Получаем номер процессора и общее число процессоров,
C выделенных задаче
    call AZ_processor_info(proc_config)
    IAM = proc_config(AZ_node)
    NPROCS = proc_config (AZ_N_procs)
C
C На 0-м процессоре печатаем пояснительную информацию и считываем
C параметр, определяющий размер решаемой задачи
C (максимально 50х50х50)
    if (IAM.eq.0) then
    write(6,2)
 2  format(' '/
 $ ' Aztec example driver'//
 $ ' This program creates an MSR matrix corresponding to'/
 $ ' a 7pt discrete approximation to the 3D Poisson operator'/
 $ ' on an n x n x n square and solves it by various methods.'/
 $ ' n must be <= 50.'//)
    write (6,*) 'input number grid point on each direction n = '
    read(*,*) n
    endif
C Рассылаем считанный параметр всем процессорам
    call MPI_BCAST(n, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
C Вычисляем количество неизвестных в системе
    nz = n*n*n
C Цикл по всем методам решения
    DO 777 K = 0,4
    time = MPI_Wtime()
C Определяем параметры разбиения матрицы по процессорам
    call AZ_read_update(N_update, update, proc_config, nz, 1, 0)
C Формируем матрицу в виде массивов val и bindx,
C в bindx(0) заносим номер позиции, с которой начнут располагаться
C недиагональные элементы, и выполняем цикл по всем строкам
    bindx(0) = N_update+1
    do 250 i = 0, N_update-1
      call add_row_7pt(update(i), i, val, bindx)
  250 continue
C Формируем правую часть системы таким образом, чтобы получить
C известное решениеа b(i) = A(i,j)*j
C
    do 341 i = 0, N_update-1
      in = bindx(i)
      ik = bindx(i+1) - 1
      s = val(i)*(update(i)+1)
      do 342 j = in,ik
        s = s + val(j)*(bindx(j)+1)
 342 continue
      tmp(i) = s
 341 continue
C Преобразуем глобальную индексацию в локальную
C
    call AZ_transform(proc_config, external, bindx, val, update,
  $                   update_index, extern_index, data_org,
  $                   N_update, NULL,NULL,NULL,NULL, AZ_MSR_MATRIX)
C Устанавливаем параметры для решателя
    call AZ_defaults(options, params)
    options(AZ_solver) = K
    options(AZ_precond) = AZ_dom_decomp
    options(AZ_subdomain_solve) = AZ_ilu
    options(4) = 1
    options(AZ_output) = AZ_none
    options(AZ_max_iter) = 2000
    params(AZ_tol) = 1.d-10
C 0-й процессор печатает параметры решения задачи
    if (IAM.eq.0) then
      write(6,780)
      write (6,*) '        The solution of method is =',К
      write (6,*) '        Dimension matrices =',nz,
 $  '   NPROCS = ', NPROCS
    endif
C Преобразуем правую часть в соответствии с новой индексацией
    do 350 i = 0, N_update-1
      x(update_index(i)) = 0.0
      b(update_index(i)) = tmp(i)
 350 continue
C Решаем систему уравнений
    call AZ_solve(x,b, options, params, NULL,bindx,NULL,NULL,
 $                NULL, val, data_org, status, proc_config)
C Полученное решение возвращаем к исходной индексации
    DO 415 I = 0,N_update - 1
 415  tmp(i) = x(update_index(i))
C
C Выполняем сборку полученных частей решения в один вектор на 0-м процессоре
C
    CALL MPI_ALLgather(N_update, 1, MPI_INTEGER, recb, 1,
 $                     MPI_INTEGER, MPI_COMM_WORLD, ierror)
    DISP(0) = 0
    DO 404 I = 1,NPROCS-1
 404  DISP(I) = DISP(I-1) + RECB(I-1)
    CALL MPI_ALLgatherv(tmp, N_update, MPI_DOUBLE_PRECISION, x, recb,
 $              disp, MPI_DOUBLE_PRECISION, MPI_COMM_WORLD, ierror)
C
C Печатаем первую и последнюю компоненты решения
    nz1 = nz-1
    if (IAM.eq.0) then
    do 348 i = 0,nz1,nz1
      s = x(i)
      write (6,956) i,s
 348  continue
 956  format (2x,'i=',I12,2x,'x(i)=',F18.8)
    endif
C
    time = MPI_Wtime() - time
C
    if(IAM.eq.0) write(*,435) time
 435  FORMAT(2x,'TIME CALCULATION (SEC.) = ',F12.4)
 777  continue
C
    CALL MPI_FINALIZE(IERROR)
    stop
    end

C Подпрограмма добавления очередной строки в массивы val и bindx
    subroutine add_row_7pt(row, location, val, bindx)
    integer row, location, bindx(0:*)
    double precision val(0:*)
    integer n3,n,k
    common /global/ n
C Входные параметры:
C row      == глобальный номер добавляемой строки
C location == локальный номер строки в процессоре

C n3 - максимально возможный номер столбца
    n3 = n*n*n - 1
C Далее выполняется проверка: существует ли в трехмерной решетке
C ближайший сосед по каждому из направлений; если да, то
C соответствующий матричный элемент заносится и
C счетчик увеличивается на 1
    k = bindx(location)
    bindx(k) = row + 1
    if (bindx(k).le.n3) then
      val(k) = -1.
      k = k + 1
    endif
    bindx(k) = row - 1
    if (bindx(k) .ge. 0) then
      val(k) = -1.
      k = k + 1
    endif
    bindx(k) = row + n
    if (bindx(k).le.n3) then
      val(k) = -1.0
      k = k + 1
    endif
    bindx(k) = row - n
    if (bindx(k).ge. 0) then
      val(k) = -1.0
      k = k + 1
    endif
    bindx(k) = row + n*n
    if (bindx(k) .le. n3) then
      val(k) = -1.0
      k = k + 1
    endif
    bindx(k) = row - n*n
    if (bindx(k) .ge. 0) then
      val(k) = -1.0
      k = k + 1
    endif
C Занесение диагонального матричного элемента
    val(location) = 6.0
C Подготовка следующего шага: заносим номер позиции, с которой
C будут располагаться недиагональные элементы следующей строки
    bindx(location+1) = k
    return
    end



Next:ЗАКЛЮЧЕНИЕ
Up:2. ИСПОЛЬЗОВАНИЕ БИБЛИОТЕКИ ПАРАЛЛЕЛЬНЫХ ПОДПРОГРАММ Aztec
Prev:2.2. Конфигурационные параметры библиотеки Aztec