Szczegóły o preconditionerze AMS, których nie znajdziecie w oficjalnym manualu

W tym rozdziale zostanie szczegółowo omówiony preconditioner addytywnej metody Schwarza w wersji zaimplementowanej w pakiecie PETSc. Informacje tutaj zawarte istotne są tylko dla tych czytelników, którzy chcą używać preconditionera Schwarza - część z nich została uzyskana na drodze prób i błędów, a część pochodzi dzięki konsultacji z autorami pakietu (autorzy szybko i chętnie odpowiadają na pytania których nie ma w FAQ). Czytelnik, któremu bardziej zależy na ogólnym poznaniu pakietu PETSc, może lekturę poniższego rozdziału odłożyć na później.

Załóżmy, że mamy już wygenerowany obiekt PC (nieważne, czy wydobyty ze SLESu funkcją SLESGetPC(), czy wygenerowany funkcją PCCreate()) i ustawione operatory. Aby ustawić preconditioner Schwarza, należy wywołać funkcję: PCSetType(pc,PCASM). Funkcje służące do sterowania tym preconditionerem pozwalają na automatyzację tworzenia jego struktury. W zasadzie jedyną rzeczą, jaką użytkownik musi sam zaimplementować, jest dekompozycja obszaru.

Funkcje ASM można podzielić na trzy zasadnicze typy:

Zbiory indeksów

Strukturą potrzebną do obsługi dekompozycji obszaru są zbiory indeksów (ang. Index Sets). Jak wszystkie obiekty w PETSc, zbiory indeksów muszą być powiązane ze zmienną określonego typu, w tym wypadku IS. Na potrzeby preconditionera Schwarza trzeba omówić funkcję ISCreateGeneral(MPI_Comm comm,int n,int *idx,IS *is) Pierwszy argument to jak zwykle komunikator MPI. Drugi argument to liczba indeksów w generowanym IS'ie. Trzeci argument to tablica zmiennych typu int, w której zapisane są po kolei numery indeksów, które chcemy wstawić do generowanego IS'a, a czwarty argument to wskaźnik do zmiennej IS, z którą będzie powiązany generowany obiekt. Inną funkcją służącą do generowania zbiorów indeksów jest ISCreateStride(MPI_Comm comm,int n,int first, int step, IS *is). Ta funkcja służy do generowania sekwencji indeksów o określonym kroku. Pierwszy i drugi argument mają takie samo znaczenie jak w poprzedniej funkcji. Trzeci to pierwszy indeks w ciągu, a drugi krok przyrostu kolejnych wyrazów.
Przykład 
...
#define MP MPI_COMM_WORLD
IS is;
Int indeksy[4]={0,5,12,17,22};
...
ISCreateGeneral(MP,5, indeksy,&is);
...
W tym przypadku wygenerowany zostanie zbiór indeksów {0,5,12,17,22} 
Przykład 
...
#define MP MPI_COMM_WORLD
IS is;
...
ISCreateStride(MP,5,2,3,&is);
...
W tym przypadku wygenerowany zostanie zbiór indeksów {2,5,8,11,14} Tak jakby wstawić tablicę wygenerowaną następująco: for(i=0;i<n;i++)indeksy[i]=2+i*3

Dekompozycja

Rysunek 7. Numeracja węzłów siatki.

Dekompozycję obszaru podaje się jako tablicę zbiorów indeksów (IS), gdzie każdy zbiór indeksów podaje numery współrzędnych w jednym podobszarze. Przypuśćmy, że mamy obszar L-kształtny. Rysunek 7, pokazuje jak ponumerowane są węzły siatki. Cała funkcja siatkowa na tym obszarze zapisywana jest jako wektor o 48 współrzędnych. Wprowadzamy dekompozycję na trzy podobszary jak na rysunku 8. Dekompozycję w tym przypadku zapisujemy więc jako 3­elementową tablicę zmiennych typu IS. Będą to następujące zbiory:
Przy podawaniu dekompozycji musimy sami zdecydować, czy zdajemy się na funkcję automatycznie ustawiającą wielkość zakładki, czy zakładkę definiujemy sami i takie zbiory indeksów musimy wygenerować. W powyższym przykładzie dekompozycja została zdefiniowana z zakładkami, bo tak uzyskujemy pełną kontrolę nad geometrią podobszarów. Zbiór indeksów wstawiamy do PC przy pomocy funkcji PCASMSetTotalSubdomains(PC pc, int N, IS *is). Pierwszy argument to zmienna typu PC służąca do kontroli obiektu, drugi to całkowita liczba podobszarów a trzeci to tablica zbiorów indeksów opisująca dekompozycję. Ta funkcja automatycznie rozdysponowuje podobszary pomiędzy procesory. Można też samemu zdecydować ile podobszarów będzie w poszczególnych procesorach przy pomocy funkcji PCASMSetLocalSubdomains(PC pc, int n, IS *is) Pierwszy argument to zmienna typu PC powiązana z obiektem, drugi to liczba podobszarów w danym procesorze, a trzeci to tablica zbiorów indeksów z podobszarami dla tego procesora. Ostatnia zmienna może przyjąć wartość PETSC_NULL, gdy PETSc ma samo zdecydować, które podobszary mają trafić do danego procesora. Gdy powyższe funkcje są wywoływane w programie wykonywanym wielowątkowo, to muszą być wywołane przez wszystkie procesy w obrębie komunikatora MPI, przy czym pierwsza musi być wywołana z tą samą we wszystkich wątkach tablicą zbiorów indeksów.

Rysunek 8. Dekompozycja obszaru.

Dodatkowo autorzy dołączyli do pakietu funkcję PCASMCreateSubdomains2D(int m,int n,int M,int N,int dof,int overlap,int *Nsub,IS **is) która służy do utworzenia dekompozycji prostokątnego obszaru dwuwymiarowego z regularną siatką na M*N prostokątnych podobszarów. Argumenty tej funkcji to po kolei: rozmiar siatki w poziomie, rozmiar siatki w pionie, liczba podobszarów w poziomie, liczba podobszarów w pionie, liczba stopni swobody siatki, szerokość zakładki, wskaźnik do zmiennej, w której znajdzie się całkowita liczba utworzonych podobszarów i ostatni argument to wskaźnik do tablicy zbiorów indeksów, która zostanie wygenerowana przez tę funkcję.

Zakładka

Następnym krokiem, który trzeba wykonać jest ustawienie wielkości zakładki pomiędzy podobszarami. Robi się to funkcją PCASMSetOverlap(PC pc, int ovl). Pierwszy argument tej funkcji to zmienna typu PC powiązana z obiektem, a drugi to szerokość zakładki w punktach. Ta funkcja spowoduje, że zbiory indeksów opisujące dekompozycję zostaną automatycznie rozszerzone o zakładkę, ale dopiero po wywoływaniu funkcji PCSetUp(). Jeżeli zdefiniowaliśmy dekompozycję z zakładkami należy wywołać funkcję PCASMSetOverlap(pc,0), bo inaczej obszary zostaną rozszerzone o dodatkową zakładkę. 
Rysunek 9. Działanie funkcji PCASMSetOverlap()

Zakładka generowana automatycznie tworzona jest na podstawie niezerowej struktury macierzy. Konkretne problemy mogą prowadzić do innych struktur zakładki, na przykład funkcja PCASMCreateSubdomains2D() tworzy zakładkę w innym kształcie niż zakładka utworzona automatycznie (Rysunek 9), dlatego dobrze jest samemu zdefiniować dekompozycję wraz z zakładkami.

Wybór typu AMS

W PETSc są zaimplementowane cztery typy AMS:

PC_ASM_BASIC metoda podstawowa pełna interpolacja i restrykcja

PC_ASM_RESTRICT pełna restrykcja, lokalna interpolacja w procesorze

PC_ASM_INTERPOLATE pełna interpolacja, lokalna restrykcja w procesorze

PC_ASM_NONE lokalna restrykcja i interpolacja w procesorze

Wszystkie te metody związane są ściśle z implementacją równoległą, to znaczy wszelkie obcięcia i interpolacje odnoszą się do danych pochodzących z innych procesorów. Jeżeli jeden procesor ma kilka podobszarów to w obrębie jego danych stosowana jest zawsze metoda podstawowa - i słusznie, bo wszystkie innowacje mają na celu wyeliminowanie zbędnej komunikacji, która często jest kosztowna, stąd w obrębie jednego procesora nie warto stosować tych metod. Wszystkie metody rozwiązują układ bez grubej siatki.

Podsumowanie

Sposób tworzenia preconditionera różni się zasadniczo w zależności od tego, czy włączamy go do SLESu jako metodę PCASM, czy jako PCSHELL. W pierwszym przypadku:
  1. Deklarujemy zmienną typu PC: PC pc;
  2. Wydobywamy ze SLESu obiekt PC funkcją SLESGetPC(sles,&pc)
  3. Ustawiamy typ preconditionera na ASM przy pomocy funkcji PCSetType(pc,PCASM)
  4. Definiujemy dekompozycję jako zbiór indeksów, można to zrobić na przykład funkcją PCASMCreateSubdomains2D(), lub samodzielnie
  5. Wstawiamy dekompozycję do PC funkcją PCASMSetTotalSubdomains(pc,nsub,dekomp), gdzie nsub to liczba podobszarów a dekomp tablica zbiorów indeksów.
  6. Ustawiamy zakładkę funkcją PCASMSetOverlap(pc,zakl). Należy pamiętać o tym, że niezerowa wartość zmiennej zakl spowoduje automatyczne wygenerowanie dodatkowej zakładki o tej szerokości. Zatem gdy dekompozycja została podana z zakładkami należy zmienną zakl ustawić na 0.
Gdy tworzymy własny preconditioner oparty na metodzie dekompozycji obszaru, na przykład powłokowy (shell) należy wygenerować go w całości, to znaczy wywołać szereg funkcji, które w poprzednim przypadku są automatycznie wywoływane w funkcji SLESSolve(). Takie postępowanie jest niezbędne, gdy chcemy utworzyć preconditioner AMS z grubą siatką, gdyż dorzucenie grubej siatki wymaga dodatkowego operatora restrykcji-interpolacji, który ma inną strukturę niż analogiczne operatory dla podobszarów. Czynności którymi postępowanie w tym wypadku różni się od poprzedniego to:
Taki sposób postępowania został przedstawiony w przykładzie8_4.c

Szczegóły

Po wywołaniu funkcji PCSetUp(pc) gdzie pc jest preconditionerem AMS zostanie utworzony szereg komponentów z których składa się nasz obiekt. Obrazuje to Rysunek 10, który pokazuje strukturę preconditionera Schwarza dla czterech podobszarów.
Rysunek 10. Struktura preconditionera Schwarza dla 4 podobszarów.

Każdy z podobszarów ma własny SLES, wektor i macierze, służące do rozwiązywania zadań lokalnych. Macierze zadań lokalnych tworzone są z macierzy zadania na podstawie zbioru indeksów definiującego podobszar. Wektory zadań lokalnych mają rozmiar wynikający z wielkości macierzy zadania lokalnego. Do przenoszenia danych pomiędzy wektorem globalnym a wektorami lokalnymi służą (automatycznie generowane) obiekty typu VecScatter. Użytkownik ma możliwość kontrolowania każdego z zadań lokalnych poprzez SLES z nim związany. Aby uzyskać do niego (do nich) dostęp, należy najpierw wydobyć tablicę SLESów dla zadań lokalnych z preconditionera. Robi się to przy pomocy funkcji PCASMGetSubSLES(PC pc,int *n_local,int *first_local,SLES **sles). Pierwszy argument tej funkcji to preconditioner z którego wydobywamy SLESy. Drugi argument to liczba SLESów przechowywanych w procesorze, który wywołał tą funkcję, trzeci to numer pierwszego SLESu przechowywanego w procesorze który wywołał tę funkcję a ostatni to wskaźnik do tablicy SLESów, w której znajdą się wyciągane SLESy. Ważne jest to, że funkcja działa lokalnie, to znaczy w przypadku programu równoległego po wywołaniu tej funkcji dostajemy SLESy przechowywane jedynie w procesorze, który wywołał tą funkcję.

Przykład

Mając już tablicę SLESów możemy kontrolować sposób rozwiązywania zadań. Na przykład możemy zażądać, aby każde zadanie lokalne było rozwiązywane metodą niekompletnego rozkładu Choleskiego ICC.
#include "sles.h";
PC pc,pc1;

SLES *slesy;
...
PCASMGetSubSLES(pc,&n,&zero,&slesy);
for(i=zero;i<zero+n;i++)
{
 SLESGetPC(slesy[i],&pc1);
 PCSetType(pc1,PCICC);
}
 
PC, czyli preconditionery Spis treści II. Rozwiązywanie równań nieliniowych
 

Copyright (C) Marcin Bojko i Piotr Krzyzanowski, 1998.
Ostatnia modyfikacja: 12.X.98.