Równania liniowej sprężystości

W module tym przedstawimy sformułowanie silne i słabe dla równań liniowej teorii sprężystości, bardzo powszechnie używanych podczas obliczeń za pomocą metody elementów skończonych naprężeń w różnych konstrukcjach. Rozważmy dla przykładu trójwymiarową platformę podpartą pięcioma podporami stojącą na płaszczyźnie OXY, przedstawioną na rysunku 1. Platforma ta posiada swój ciężar i pojawiają się na niej pewne naprężenia wynikające z oddziaływania pola grawitacyjnego ziemi. Dla wygodniejszego sformułowania równań różniczkowych cząstkowych opisujących przemieszczenie platformy, będziemy teraz używać oznaczenia  \(  (x_1,x_2,x_3)  \) w dowolnym punkcie platformy. Poszukiwanym rozwiązaniem będzie tym razem pole wektorowe przemieszczeń  \(  \Omega \ni (x_1,x_2,x_3) \rightarrow (u_1(x_1,x_2,x_3),u_2(x_1,x_2,x_3),u_3(x_1,x_2,x_3)) \) Oznacza to w szczególnosci że przemieszczenia punktów platformy pod jej ciężarem, w kierunku osi  \( z  \), wynoszą  \(  u_3(x_1,x_2,x_3)  \) w punkcie  \(  (x_1,x_2,x_3)  \). Oczywiście pole grawitacyjne działa jedynie w kierunku osi Z, ale punkty platformy są ze sobą "sklejone" i jeśli punkt platformy przemieszcza się w kierunku osi Z pod wpływem ciężaru platformy, często związane jest to z przesunięciem punktu w kierunkach Y oraz Z, co opisują składowe  \(  u_1(x_1,x_2,x_3)  \) oraz  \(  u_2(x_1,x_2,x_3)  \). Oczywiście przewidzenie jak będą odkształcać się te punkty nie jest rzeczą prostą, i w szczególności wymaga rozwiązania skomplikowanych równań liniowej teorii sprężystości. Dla uproszczenia zapisu oraz ze względu na stosowanie tradycyjnego zapisu wprowadzamy oznaczenie na pochodne cząstkowe poszczególnych współrzędnych wektora przemieszczeń  \(  u_{i,j}=\frac{\partial u_i}{\partial x_j } \) oznacza to pochodną w kierunku  \(  j \)-tym ze składowej pola wektorowego  \(  i \)-tego. W celu wyprowadzenia równań liniowej teorii sprężystości wprowadza się tak zwany tensor prędkości odkształceń  \(  \epsilon_{ij}=\frac{\frac{\partial u_i}{\partial x_j}+\frac{\partial u_j}{\partial x_i}}{2}=\frac{u_{i,j}+u_{j,i } }{2 } \) oraz tensor naprężeń  \(  \sigma_{ij}=c_{ijkl}\epsilon_{kl}  \) Używamy tutaj tak zwanej konwencji sumacyjnej Einsteina, w której zduplikowanie indeksów we wzorze oznacza iż sumujemy po tych indeksach czyli na przykład w równaniu spreżystości  \(  \sigma_{ij}=c_{ijkl}\epsilon_{kl }  \) powtórzenie indeksów  \( k,l  \) oznacza sumowanie po tych indeksach  \(  \sigma_{ij}=\sum_{k=1,...,3;l=1,...,3} c_{ijkl}\epsilon_{kl }  \). Mamy tutaj czterowymiarową macierz współczynników  \( c_{ijkl},i=1,...,3;j=1,...,3,k=1,...,3;l=1,..,3  \). Określenie tych 3*3*3*3 liczb definiuje mi jednoznacznie materiał z którego zbudowana jest analizowana przeze mnie konstrukcja (w naszym przykładzie platforma). Większość tych współczynników na szczęście będzie równa zero. Tak zwane sformułowanie silne problemu liniowej teorii sprężystości formułuje się następująco:  \( \sigma_{ij,j}+f_i=0  \) dla i=1,2,3, w obszarze  \(  \Omega  \). Wyraz  \(  f_i  \) oznacza 3 współrzędne wektora siły działającej na naszą konstrukcję, na przykłąd będzie to siła grawitacji  \(  f=(g_1(x_1,x_2,x_3),g_2(x_1,x_2,x_3),g_3(x_1,x_2,x_3))  \) Wprowadzamy tutaj następującą konwencję. Indeksy powtórzone po przecinku  \(  ij,j  \) oznaczają obliczanie pochodnej pochodne cząstkowej po indeksowanej zmiennej. Zapis  \( \sigma_{ij,j}=\frac{\partial \sigma_{ij} }{\partial x_j } \) oznacza więc  sumę pochodnych cząstkowych z tensora naprężeń. Ponadto mamy defacto trzy równania (co ma sens ponieważ szukamy trzech współrzędnych wektora przemieszczeń).  \( \sum_{j=1,2,3}\frac{\partial \sigma_{1j}}{\partial x_j}+f_1=0 \\ \sum_{j=1,2,3}\frac{\partial \sigma_{2j}}{\partial x_j }+f_2=0 \\ \sum_{j=1,2,3}\frac{\partial \sigma_{3j}}{\partial x_j }+f_3=0  \) czyli uwzględniając defincje tensora naprężeń  \(  \sum_{j=1,2,3}\frac{\partial (\sum_{k=1,...,3;l=1,...,3} c_{1jkl}\epsilon_{kl})}{\partial x_j}+f_1=0 \\ \sum_{j=1,2,3}\frac{\partial (\sum_{k=1,...,3;l=1,...,3} c_{2jkl}\epsilon_{kl})}{\partial x_j}+f_2=0 \\ \sum_{j=1,2,3}\frac{\partial (\sum_{k=1,...,3;l=1,...,3} c_{3jkl}\epsilon_{kl})}{\partial x_j }+f_3=0   \) czyli uwzględniając definicje tensora prędkości odkształceń  \(  \sum_{j=1,2,3}\frac{\partial (\sum_{k=1,...,3;l=1,...,3} c_{1jkl}(\frac{u_{k,l}+u_{l,k}}{2}))}{\partial x_j }+f_1=0 \\ \sum_{j=1,2,3}\frac{\partial (\sum_{k=1,...,3;l=1,...,3} c_{2jkl}(\frac{u_{k,l}+u_{l,k}}{2}))}{\partial x_j }+f_2=0 \\ \sum_{j=1,2,3}\frac{\partial (\sum_{k=1,...,3;l=1,...,3} c_{3jkl}\frac{u_{k,l}+u_{l,k}}{2})}{\partial x_j }+f_3=0   \) Nasze niewiadome tutaj to pole wektorowe przemieszczeń: Szukamy  \(  \Omega \ni (x_1,x_2,x_3) \rightarrow (u_1(x_1,x_2,x_3),u_2(x_1,x_2,x_3),u_3(x_1,x_2,x_3)) \) Żeby rozwiązać równania liniowej teorii sprężystości na danej konstrukcji, konieczne jest zdefiniowanie warunków brzegowych, czyli określenie tego co się dzieje na brzegu konstrukcji. Na części brzegu możemy wprowadzić warunek Dirichleta, na przykład określając że podstawa konstrukcji jest przymocowana do podłoża, i dlatego przemieszczenie podstawy konstrukcji jest zerowe  \(  u_1(x_1,x_2,x_3)=0, u_2(x_1,x_2,x_3)=0, u_3(x_1,x_2,x_3)=0, \textrm{ dla } (x_1,x_2,x_3)\in \Gamma_D  \) Możliwe jest również określenie warunku brzegowego Neumanna  \(  \sigma_{ij}n_j = h_i \textrm{ dla } (x_1,x_2,x_3)\in \Gamma_N   \) gdzie  \(  n(x_1,x_2,x_3)=(n_1(x_1,x_2,x_3),n_2(x_1,x_2,x_3),n_3(x_1,x_2,x_3))  \) oznacza wersor prostopadły do brzegu w punkcie  \(  (x_1,x_2,x_3)  \), oraz  \(  h=(h_1(x_1,x_2,x_3),h_2(x_1,x_2,x_3),h_3(x_1,x_2,x_3))  \) oznacza zadaną funkcję na brzegu Neumanna  \(  \Gamma_N  \), czyli uwzględniając konwencje sumacyjną Einsteina  \(  \sigma_{11}n_2+\sigma_{12}n_2+\sigma_{13}n_3 = h_1 \\ \sigma_{21}n_2+\sigma_{22}n_2+\sigma_{23}n_3 = h_2 \\ \sigma_{31}n_2+\sigma_{32}n_2+\sigma_{33}n_3 = h_3   \) czyli uwzględniając definicje tensora naprężeń  \(  (\sum_{k=1,...,3;l=1,...,3} c_{11kl}\epsilon_{kl})n_2+ (\sum_{k=1,...,3;l=1,...,3} c_{12kl}\epsilon_{kl})n_2+ (\sum_{k=1,...,3;l=1,...,3} c_{13kl}\epsilon_{kl})n_3 = h_1 \\ (\sum_{k=1,...,3;l=1,...,3} c_{21kl}\epsilon_{kl})n_2+(\sum_{k=1,...,3;l=1,...,3} c_{22kl}\epsilon_{kl})n_2+ (\sum_{k=1,...,3;l=1,...,3} c_{23kl}\epsilon_{kl})n_3 = h_2 \\ (\sum_{k=1,...,3;l=1,...,3} c_{31kl}\epsilon_{kl})n_2+ (\sum_{k=1,...,3;l=1,...,3} c_{32kl}\epsilon_{kl})n_2+ (\sum_{k=1,...,3;l=1,...,3} c_{33kl}\epsilon_{kl })n_3 = h_3  \). Warunek Neumanna określa siły w równaniach teorii sprężystości Sformułowanie słabe (zwane też wariacyjnym) które nadawać się będzie do implementacji za pomocą metody elementów skończonych podajemy na podstawie książki [1] Sformułowanie słabe (wariacyjne) podane poniżej obowiązuje dla pewnej szerokiej klasy materiałów dla której własności mechaniczne da się opisać za pomocą dwóch parametrów, modułu Younga  \(  E  \) oraz współczynnika Poissona  \(  \mu  \). Dla zadanego pola sił  \(  \Omega \ni (x_1,x_2,x_3) \rightarrow f=(g_1(x_1,x_2,x_3),g_2(x_1,x_2,x_3),g_3(x_1,x_2,x_3))  \) oraz zadanej funkcji warunku brzegowego Neumanna  \(  \Omega \ni (x_1,x_2,x_3) \rightarrow h=(h_1(x_1,x_2,x_3),h_2(x_1,x_2,x_3),h_3(x_1,x_2,x_3))  \) oraz zadanego tensora (macierzy czterowymiarowej) współczynników materiałowych  \(  c_{ijkl}  \) obliczyć pole przemieszczeń materiału konstrukcji  \(  \Omega \ni (x_1,x_2,x_3) \rightarrow (u_1(x_1,x_2,x_3),u_2(x_1,x_2,x_3),u_3(x_1,x_2,x_3))  \) spełniające równania liniowej sprężystości  \(  a(w,u)=(w,f)+(w,h)_{\Gamma}  \) dla dowolnych funkcji testujących  \(  w=(w_1(x_1,x_2,x_3),w_2(x_1,x_2,x_3),w_3(x_1,x_2,x_3))  \), gdzie  \( (w,f)=\int_{\Omega}w_1(x_1,x_2,x_3)f_1(x_1,x_2,x_3)dx+\int_{\Omega}w_2(x_1,x_2,x_3)f_2(x_1,x_2,x_3)dy+\int_{\Omega}w_3(x_1,x_2,x_3)f_3(x_1,x_2,x_3)dz \\ (w,h)=\int_{\Gamma}w_1(x_1,x_2,x_3)h_1(x_1,x_2,x_3)dS+\int_{\Gamma}w_2(x_1,x_2,x_3)h_2(x_1,x_2,x_3)dS+\int_{\Gamma}w_3(x_1,x_2,x_3)f_3(x_1,x_2,x_3)dS \\a(w,u)=\int_{\Omega}\epsilon(w)^T D \epsilon(u) dxdydz   \), gdzie  \(  \epsilon(u) = \begin{bmatrix} u_{1,1} \\ u_{2,2} \\ u_{3,3} \\ u_{2,3}+u_{3,2} \\ u_{1,3}+u_{3,1} \\ u_{1,2}+u_{2,1} \\ \end{bmatrix}, \epsilon(w) = \begin{bmatrix} w_{1,1} \\ w_{2,2} \\ w_{3,3} \\ w_{2,3}+w_{3,2} \\ w_{1,3}+w_{3,1} \\ w_{1,2}+w_{2,1} \\ \end{bmatrix}  \) natomiast  \(  D = \frac{E}{(1+2\mu)(1-2\mu) } \begin{bmatrix} 1-\mu & \mu & \mu & 0 & 0 & 0 \\ \mu & 1-\mu & \mu & 0 & 0 & 0 \\ \mu & \mu & 1-\mu & 0 & 0 & 0 \\ 0 & 0 & 0& \frac{1-2\mu}{2} & 0 & 0 \\ 0 & 0 & 0& 0 & \frac{1-2\mu}{2}  & 0 \\ 0 & 0 & 0& 0 & 0&  \frac{1-2\mu}{2} \\ \end{bmatrix}   \) wyprowadzona jest przy założeniu że ogólny tensor własności materiału  \(  c_{ijkl}  \) da się opisać za pomocą dwóch parametrów modułu Younga  \(  E  \) oraz współczynnika Poissona  \(  \mu  \). Podane sformułowanie wariacyjne jest stosunkowo wygodne do implementacji. Można w tym celu zastosować izogeometryczną metodę elementów skończonych, lub klasyczną metodę elementów skończonych. Rozważmy następujący przykład.


Klasyczna i izogeometryczna metoda elementów skończonych

Rozdział 3. Sformułowania wariacyjne dla różnych problemów i metod

Sformułowania wariacyjne a całkowanie numeryczne

Dla wszystkich wymienionych powyżej sformułowań wariacyjnych w celu rozwiązania ich metodą elemntów skończonych konieczne jest wygenerowanie układu równań liniowych. W szczególności konieczne jest wygenerowanie całek do układu równań liniowych. Pamiętając, że wiersze i kolumny w globalnym układzie równań odpowiadają poszczególnym funkcjom bazowym, w naszym przykładzie odpowiadają one funkcjom B-spline, generujemy następujący układ równań  \(  \begin{bmatrix} & \color{red}{B^x_{1,p} B^y_{1,p}} & \cdots &\color{red}{ B^x_{N_x,p}B^y_{1,p}}  \\ \color{blue}{ B^x_{1,p}B^y_{1,p}}&    a(\color{blue}{ B^x_{1,p}B^y_{1,p}}, \color{red}{ B^x_{1,p}B^y_{1,p}}) & \cdots &  a(\color{blue}{ B^x_{1,p}B^y_{1,p}},\color{red}{ B^x_{N_x,p}B^y_{N_y,p}}) \\ \color{blue}{ B^x_{2,p}B^y_{1,p}} &    a(\color{blue}{ B^x_{2,p}B^y_{1,p}},\color{red}{ B^x_{1,p}B^y_{1,p}}) & \cdots &  a(\color{blue}{ B^x_{2,p}B^y_{1,p}},\color{red}{ B^x_{N_x,p}B^y_{N_y,p}}) \\ \vdots & \vdots & \vdots &  \vdots \\ \color{blue}{ B^x_{N_x,p}B^y_{N_y,p} } & a(\color{blue}{ B^x_{N_x,p}B^y_{N_y,p}}, \color{red}{ B^x_{1,p}B^y_{1,p}}) & \cdots &  a(\color{blue}{ B^x_{N_x,p}B^y_{N_y,p}},\color{red}{  B^x_{N_x,p}B^y_{N_y,p}}) \\ \end{bmatrix} \begin{bmatrix} u_{1,1} \\ u_{2,1} \\ \vdots \\ u_{N_x,N_y } \\ \end{bmatrix}  \)  \(  = \begin{bmatrix} l(B^x_1(x)*B^y_1(y)) \\ l(B^x_1(x)*B^y_2(y)) \\ \vdots \\ l(B^x_{N_x}(x)*B^y_{N_y}(y)) \\ \end{bmatrix}  \) Funkcje B-spline które nie mają wspólnej dziedziny (które znajdują się daleko od siebie na siatce) generują zerowe całki, czyli zerowa wartości macierzy. Podczas generacji macierzy nie należy więc iterować po wierszach i kolumnach macierzy, za to należy iterować po elementach siatki obliczeniowej, oraz po funkcjach bazowych które są określone na danym elemencie. Wynikowe fragmenty całek należy dopisywać do globalnej macierzy. Wówczas to nasza generacja całek omijać będzie wszystkie niezerowe wyrazy macierzy. Algorytm zawiera sześć zagnieżdżonych pętli: 1  for  \(  nex=1,N_x \) (pętla po elementach wzdłuż osi  \( x \)) 2  for  \( ney=1,N_y \) (pętla po elementach wzdłuż osi  \( y \)) 3  for  \( ibx1=1,p+1 \) (pętla po  \( p+1 \) B-spline elementu wzdłuż osi  \( x \)) 4  for  \( iby1=1,p+1 \) ( pętla po  \( p+1 \) B-spline elementu wzdłuż osi  \( y \)) 5   \( i = f(nex,ibx1) \) ( oblicz indeks B-spline wzdłuż osi  \( x \)) 6   \( j = f(ney,iby1) \)  ( oblicz indeks B-spline wzdłuż osi  \( y \)) 7    \( irow = g(nex,ibx1,ney,iby1) \)(globalny indeks 2D B-spline'a) 8    \( l(irow)+= l(B^x_{i,p}B^y_{j,p}) \) 9  for  \( ibx2=1,p+1 \) ( pętla po  \( p+1 \) B-spline elementu wzdłuż osi  \( x \)) 10  for  \( iby2=1,p+1 \) (pętla po  \( p+1 \) B-spline elementu wzdłuż osi  \( y \)) 11   \( k = f(nex,ibx2) \) (oblicz indeks B-spline wzdłuż osi  \( x \)) 12   \( l = f(ney,iby2) \) (oblicz indeks B-spline wzdłuż osi  \( y \)) 13   \( irow = g(nex,ibx2,ney,iby2) \) (globalny indeks 2D B-spline'a) 14   \( M(irow,icol)+= a(B^x_{i,p}B^y_{j,p},B^x_{k,p}B^y_{l,p}) \) Funkcje  \(  f(nex,ibx)  \) obliczają indeks funkcji  \(  ibx \)-tej B-spline rozpiętej na elemencie wzdłuż osi  \(  x  \) numer  \(  nex  \). Dla jednorodnych B-spline'ów stopnia  \(  p  \) indeks ten wynosi  \(  nex+ibx-1  \). Funkcje  \(  g(nex,ibx,ney,iby)  \) obliczają globalny indeks dwuwymiarowego B-spline'a. W przypadku jednorodnych funkcji B-spline wzór ten wynosi  \(  irow = i*j  \) gdzie \( i=f(nex,ibx)  \) oraz  \(  j=f(ney,iby)  \). Z koleji obliczanie całek realizowane jest w ogólnym przypadku przez całkowanie numeryczne z wykorzystaniem punktów i wag kwadratury, na przykład kwadratury Gaussa. Całkę jednowymiarowaz wielomianu na przedziale da się obliczyć dokładnie jako suma wartości tego wielomianu w wybranych punktach kwadrtury rozpostartych na przedziale, przemnożonych przez odpowiednie wagi. Wyprowadzenie i teoria kwadratur numerycznych wykracza poza zakres tego podręcznika. Nazwa kwadratur Gaussa pochodzi od  nazwiska niemieckiego matematyka który wynalazł tą metodę całkowania w 1826 roku. [1] Wybór kwadratury zależy od rodzaju funkcji bazowych, i możliwa jest redukcja liczby punktów kwadratury stosując specjalne kwadratury dedykowane poszczególnym funkcjom bazowym. W ogólnym przypadku na pojedynczym elemencie funkcje bazowe są wielomianami i stosowanie kwadratur Gaussa jest uzasadnione. Poniższy przykład ilustruje tworzenia punktów i wag kwadratury Gaussa na przedziale  \( [0,1]  \)  gwarantującej dokładne policzenie całki z funkcji dwuwymiarowych B-spline stopnia nie większego niż 10. m_points[1]= (1.0-0.973906528517171720077964)*0.5; m_points[2]= (1.0-0.8650633666889845107320967)*0.5; m_points[3]= (1.0-0.6794095682990244062343274)*0.5; m_points[4]= (1.0-0.4333953941292471907992659)*0.5; m_points[5]= (1.0-0.1488743389816312108848260)*0.5; m_points[6]= (1.0+0.1488743389816312108848260)*0.5; m_points[7]= (1.0+0.4333953941292471907992659)*0.5; m_points[8]= (1.0+0.6794095682990244062343274)*0.5; m_points[9]= (1.0+0.8650633666889845107320967)*0.5; m_points[10]= (1.0+0.9739065285171717200779640)*0.5; m_weights[1]=0.0666713443086881375935688*0.5; m_weights[2]=0.1494513491505805931457763*0.5; m_weights[3]=0.2190863625159820439955349*0.5; m_weights[4]=0.2692667193099963550912269*0.5; m_weights[5]=0.2955242247147528701738930*0.5; m_weights[6]=0.2955242247147528701738930*0.5; m_weights[7]=0.2692667193099963550912269*0.5; m_weights[8]=0.2190863625159820439955349*0.5; m_weights[9]=0.1494513491505805931457763*0.5; m_weights[10]=0.0666713443086881375935688*0.5; Kwadratury oryginalnie tablicowane są dla przedziału  \(  [-1,1]  \) tak więc tutaj przeskalowujemy je do przedziału  \(  [0,1] \) dodając 1 do współrzędnej punktów oraz dzieląc współrzędne punktow przez 2 (czyli mnożąc przez 0.5). Wówczas całkowanie numeryczne wykonywane w linii 14 (i kolejnych) w powżzszym kodzie wymaga dwóch dodatkowych pętli po punktach i wagach kwadratury Gassua i wygląda ono następująco: 15 for  \( m=1,p/2+1 \) (pętla po punktach kwadratury Gaussa wzdłuż osi  \( x \) dla B-spline'ów stopnia  \( p \)) 16 for  \( n=1,p/2+1 \) (pętla po punktach kwadratury Gaussa wzdłuż osi  \( y \) dla B-spline'ów stopnia  \( p \)) 17  \( M(irow,icol)+=\\ a(B^x_{i,p}(m\_points[m])B^y_{j,p}(m\_points[n]), B^x_{k,p}(m\_points[m])B^y_{l,p}(m\_points[n])) \\ *m\_weights[m]*m\_weights[n]*area \) W szczególności dla naszego przykładowego problemu transportu ciepła na obszarze w ksztalcie litery L mamy  \( M(u,v) = \left(\frac{\partial B^x_{i,p} }{\partial x }(m\_points[m])\frac{\partial B^x_{k,p} }{\partial x }(m\_points[m])\right)*m\_weights[m] \\ + \left(\frac{\partial B^y_{j,p} }{\partial y }(m\_points[n]) \frac{\partial B^y_{l,p} }{\partial y }(m\_points[n]) \right)*m\_weights[n]   \) W powyższym wzorze obliczamy wartości pochodnych z jednowymiarowych funkcji B-spline w punktach kwadratury Gaussa  \( (m\_points[m],m\_points[n]) \) przemnożone przez wagi kwadratury Gaussa  \( *m\_weights[m]*m\_weights[n] \).


Implementacja w MATLABie problemu transportu ciepła

Poniżej przedstawiam kod MATLABa obliczający problem transportu ciepła na obszarze w kształcie litery L.

Kod może zostać uruchomiony w darmowym środowisku Octave. Kod uuchamia się otwierając go w Octave oraz wpisując komendę  \( heat' \) Po chwili obliczeń kod otwiera dodatkowe okienko i rysuje w nim rozwiązanie. W linii 641  \( knot = [0, 0, 0, 1, 2, 2, 2]; \) znajduje się zapisany wektor węzłów [0 0 0 1 2 2 2] który używany będzie na każdym z trzech grup elementów (patchów) stosowanych do rozwiązania problemu transportu ciepła.

Autorzy kodów w MATLABie: Marcin Łoś i Maciej Woźniak.


Implementacja w MATLABie problemu adwekcji-dyfuzji metodą Galerkina

Poniżej przedstawiam kod MATLABa obliczający problem adwekcji-dyfuzji metodą Galerkina, dla modelowego problemu Erikksona-Johnsona. Przypomnijmy najpierw problem adwekcji-dyfuzji, problem modelowy Erikksona-Johnsona (opisany na przykład w dokumencie [1]) zdefiniowany na obszarze kwadratowym  \(  \Omega = [0,1]^2  \) w następujący sposób: Szukamy funkcji \(  u  \) takiej że  \(  a(u,v)=l(v) \forall v  \) gdzie  \(  a(u,v) =\int_{\Omega} \beta_x(x,y) \frac{\partial u(x,y) }{\partial x } dxdy + \int_{\Omega} \beta_y(x,y) \frac{\partial u(x,y)}{\partial y } dxdy  \\ +\epsilon \int_{\Omega} \frac{\partial u(x,y) }{\partial x}  \frac{\partial v(x,y)}{\partial x } dxdy +\epsilon \int_{\Omega} \frac{\partial u(x,y)}{\partial y }  \frac{\partial v(x,y)}{\partial y } dxdy  \)  \(  l(v) = \int_{\partial \Omega } f(x,y) v dxdy  \)  \(  f(x,y)=sin(\pi y)(1-x)  \) jest rozszerzeniem warunku brzegowego Dirichleta na cały obszar, natomiast  \(  \beta = (1,0)  \) reprezentuje prędkość wiatru wiejącego z lewej strony na prawą, natomiast  \(  \epsilon = 10^{-2} \) oznacza współczynnik dyfuzji. Wielkość  \( Pe=1/ \epsilon = 100 \) nazywa sie liczbą Pecleta, i definiuje ona wrażliwość problemu adwekcji-dyfuzji. Przedstawiona tutaj definicja liczby Pecleta obowiązuje w przypadku jednostkowej prędkości wiatru  \(  \| \beta \|_2=\sqrt{\beta_x^2+\beta^2_y}=1  \). W ogólnym przypadku rozważa się liczbę Pecleta zdefiniowaną na poszczególnych elementach siatki zgodnie ze wzorem  \(  Pe=\| \beta \|_2*h/(2*\epsilon)  \). Tak zdefiniowaną liczbę Pecleta użyć można do badania stabilności symulacji, liczba Pecleta mniejsza od jeden daje nam warunek stabilności symulacji na elemencie.

Kod można uruchomić w darmowym środowisku Octave.

W linii 835 tworzony jest knot vector zbudowany z 5 elementów dla wielomianów kwadratowych  \(  knot = simple\_knot(5, 2);  \) W linii 836 ustawiany jest współczynnik dyfuzji (dla współczynnika adwekcji równego 1).  \(  epsilon = 2e-2;   \) Kod może zostać uruchomiony w darmowym środowisku Octave. Kod uruchamia się otwierając go w Octave oraz wpisując komendę  \( advection' \) Po chwili obliczeń kod otwiera dodatkowe okienko i rysuje w nim rozwiązanie numeryczne oraz dokładne. Kod oblicza również normę L2 i H1 z rozwiązania  \( Error: L2 24.00 procent, H1 72.08 procent \) i porównuje do normy L2 i H1 z projekcji rozwiazania dokładnego na przestrzeń funkcji B-spline rozpiętych na danej siatce.  \( Best possible: L2 1.56 procent, H1 53.16 procent \) Zauważmy że norma te nie będzie równa zero, ponieważ rozwiązanie dokładne nie jest dane funkcjami B-spline, i nawet jeśli znamy rozwiązanie dokładne, to nie jest ona zadane funkcjami B-spline, i jego projekcja na przestrzeń funkcji B-spline również jest przybliżeniem rozwiązania dokładnego. Porównanie to pokazuje jaki bład popełnia nasza metoda numeryczna, i jakie normy mogła by osiągnąć na aktualnej siatce z funkcjami B-spline. Widać że metoda Galerkina nie daje dokładnego rozwiązania.


Rozdział 4. Solwery układów równań liniowych generowanych podczas obliczeń MES

Algorytm eliminacji Gaussa

Algorytm eliminacji Gaussa jest podstawowym najszerzej znanym algorytmem rozwiązywania układów równań liniowych. Algorytm eliminacji, zwany algorytmem Gaussa został wynaleziony w Chinach w roku 179. W Europie algorytm zaczął być używany w roku 1771 przez Leonarda Eulera, który nazywał go najbardziej naturalnym sposobem rozwiązywania układów równań. Jest on niesłusznie nazywany algorytmem eliminacji Gaussa, od matematyka Carla Friedricha Gaussa (1777-1855), który nie żył jeszcze w roku 1771 [1].

Algrorytm który wykonywaliśmy krok po kroku podsumować można w następujący sposób:

Ostatni krok to uruchomienie algorytmu podstawiania wstecz, który rozwiąże nasz układ równań począwszy od ostatniego równania.

Algorytm eliminacji Gaussa jest kosztowny. Oznacza to że dla macierzy o rozmiarze  \( N \) wierszy i kolumn, wykona on około  \( N^3 \) operacji. Jaką więc największą macierz możemy przetworzyć algorytmem eliminacji Gaussa w ciągu 24 godzin? Na dzień dzisiejszy (styczeń 2019) zegary procesorów mają około 3 GHz. Oznacza to iż w ciągu każdej sekundy procesor potrafi wykonać  \( 3\times 10^9 \) dodawań lub mnożeń. Doba ma  \( 24*60*60=86400 \) sekund. Procesor potrafi więc wykonać  \( 86400*10^9=86400000000000 \) operacji dodawania i mnożenia wciągu doby. Pierwiastek trzeciego stopnia z tej liczby to  \( (86400000000000)^{\frac{1}{3}}\approx44208 \) tak więc największa macierz którą potrafimy przetworzyć ma rozmiar 44208. Reprezentuje ona siatkę obliczeniową o 210 elementach w każdym kierunku. Jest to więc względnie mała siatka obliczeniowa. Potrzebujemy lepszych algorytmów. Co więcej wszystkie nasze szacowania przeprowadziliśmy przy założeniu że procesor nie czeka na dane, tylko cały czas je przetwarza, oraz że posiadamy wystarczająco dużo pamięci RAM oraz optymalną strukturę cache'a procesora. Zazwyczaj sytuacji nie jest tak idealna, w praktyce więc trudno jest przetworzyć eliminacją Gaussa macierze o rozmiarze większym niż 10000 co odpowiada siatce 100 na 100.

W przypadku wielu prawych stron, dla każdej prawej strony konieczne jest wykonywanie eliminacji Gaussa ponownie. Możliwe jest zapamiętanie liczb przez które przemnażamy prawą stronę, tak żeby wykonować eliminację tylko raz, i dla każdej nowej prawej strony wykonać jedynie update. Wykonuje to algorytm LU faktoryzacji.

Algorytm eliminacji Gaussa nie opuszcza zer. Przemnażanie zer przez liczby niezerowe nie ma generalnie sensu, jest stratą czasu. Algorytm elimiancji Gaussa można przyspieszyć opuszczając wszystkie zera. Jeśli będziemy wiedzieć apriori gdzie w macierzy znajdują się zera, możemy je opuścić i przyspieszyć elimiancję Gaussa. To będzie przedmiotem algorytmu frontalnego i wielo-frontalnego.

Jeśli macierz posiada strukturę produktu Kroneckera dwóch mniejszych macierzy (co miało miejsce w przykładzie z rozdziału pierwszego) wówczas możliwe jest zastosowanie algorytmu solwera zmienno-kierunkowego, o liniowej złożoności obliczeniowej.

Jeśli zadowala nas wynik z pewną dokładnością, np. 0.0001, możliwe jest obliczenie rozwiązania przybliżonego stosując tak zwane algorytmy iteracyjne. Jednakże algorytmy te nie działają dla wszystkich rodzajów macierzy.


Algorytm eliminacji Gaussa z pivotingiem

Zacznijmy ponownie eliminacje Gaussa dla naszego przykładu, tym razem z zaminą wierszy (zwaną również "pivotingiem" w dosłownym tłumaczeniu z języka angielskiego).

Algorytm elimiancji Gaussa należy wzbogacić o zamianę wierszy


Algorytm LU faktoryzacji

Algorytm LU faktoryzacji został wynaleziony przez polskiego matematyka Tadeusza Banachiewicza w roku 1938, co opisano w pracy [1] Zauważmy iż algorytm eliminacji Gaussa generuje macierz trójkątną górną  \( U \). Możliwe jest również wygenerowanie macierzy trójkątnej dolnej  \( L \) takiej że oryginalna macierz naszego problemu  \( A=LU \). Jaka jest zaleta w posiadaniu takiej dekompozycji? Załóżmy że chcemy rozwiązać dwa problemy dla dwóch prawych stron  \( b_1, b_2 \). Załóżmy również że na początku znamy prawą stronę  \( b_1 \) a że drugą prawą stronę  \( b_2 \) poznamy dopiero po rozwiązaniu problemu dla pierwszej prawej strony. Czy da się w jakiś sposób wykorzystać faktoryzację macierzy  \( A \) wykonaną dla poprzedniej prawej strony  \( b_1 \)? Najpierw obliczamy LU faktoryzację macierzy  \( A=LU \) następnię mamy równanie  \( LUx=b_1 \). Obliczamy najpierw  \( z \) wykonując podstawianie w przód na podstawie  równania  \( Lz=b_1 \). Następnie obliczamy x rozwiązując równanie  \( Ux=z \) przez podstawianie wstecz.


Algorytm LU faktoryzacji z pivotingiem

Jak przerobić algorytm eliminacji Gaussa z pivotingiem na algorytm LU faktoryzacji z pivotingiem? Należy wprowadzić dodatkową macierz  \( {\bf P} \) która będzie odpowiadać za pivoting.


Algorytm solwera frontalnego

Algorytm solwera frontalnego był pierwszym algorytmem modyfikującym eliminację Gaussa wykorzystującym dodatkową wiedzę o metodzie elementów skończonych. Celem modfikacji algorytmu było omijanie zer w macierzy wynikających z interakcji pomiędzy funkcjami bazowymi rozpiętymi na siatce obliczeniowej, znajdującymi się daleko od siebie. Algorytm ten został zaproponowany przez Bruce'a Irons'a w 1970 roku. Bruce Irons zdobył szeroką sławę po wynalezieniu tego algorytmu [1]. Jego wyniki naukowe tym bardziej zasługują na uznanie, ponieważ zmagał się z nieuleczalną chorobą jaką było stwardnienie rozsiane. Idea algorytmu polega na generowaniu lokalnych macierzy wynikających z interakcji funkcji bazowych na poszczególnych elementach skończonych. Zamiast generować całą macierz, cały układ równań, układamy elementy skończone w wybranej kolejności. Generujemy fragment macierzy wynikający z interakcji funkcji bazowych na pierwszym elemencie. Wiersze i kolumny macierzy elementowych odpowiadają poszczególnym funkcjom bazowym rozpostartym na danym elemencie. Wyrazy macierzy oznaczają interakcję funkcji bazowych z wiersza i kolumny (całkę z iloczynu funkcji bazowych policzoną na danym elemencie). Niektóre wiersze macierzy mogą zostać w pełni wygenerowane, niektóre jedynie częściowo, ponieważ brakować będzie pewnych kolumn wynikających z faktu iż funkcje bazowe występujące na danym elemencie rozpościerają się na sąsiednich elementach, i oddziaływują (mając wspólne nośniki dziedziny) z funkcjami bazowymi z kolejnych elementów (z kolejnych kolumn macierzy).

Zróbmy teraz następujące obserwacje. Algorytm solwera frontalnego w przedstawionej powyżej formie pozwala jedynie na pivoting w ramach wierszy które zostały w pełni zsumowane. Jeśli macierz nie jest symetryczna i dodatnio określona stanowi to problem, i uzyskane rozwiązanie może nie być poprawne. Algorytm solwera frontalnego można uogólnić na dowolne siatki wielowymiarowe, jednakże główną jego wadą jest fakt iż rozmiar macierzy frontalnej (do której dokładamy poszczególne elementy) może rosnąć i koszt tego solwera jest duży. Rozmiar macierzy frontalnej związany jest z rozmiarem interfejsu pomiędzy dodawanymi i eliminowanymi elementami, a całą resztą siatki obliczeniowej. Interfejs ten nazywa się frontem solwera. W ogólnym przypadku rozmiar frontu zależy od rozmiaru i ksztaltu siatki obliczeniowej. Podczas przeglądania elementów siatki dwuwymiarowej wiersz po wierszu, wiersze których funkcje rozpościerają się również na kolejnych wierszach będą musiały poczekać z eliminacją do momentu w którym włączymy do macierzy frontalnej kolejny wiersz. Dlatego też rozmiar macierzy frontalnej będzie równy liczbie funkcji bazowych na przekroju siatki, co w dwóch wymiarach wynosi  \( N^{0.5} \) gdzie  \( N \) to liczba funkcji bazowych na siatce.


Algorytm solwera wielo-frontalnego

Algorytm solwera wielo-frontalnego zaproponowany został w 1987 roku jako modyfikacja algorytmu solwera frontalnego przez Iain'a Duff'a i Johna Reida [1]. Ideą algorytmu wielo-frontalnego jest wykonywanie eliminacji tak jak w algorytmie solwera frontalnego ale jednocześnie nad wieloma elementami.


Algorytm solwera zmienno-kierunkowego

Algorytm solwera zmiennokierunkowego został po raz pierwszy zaproponowany przez Jima Douglasa w 1956 roku w kontekście symulacji niestacjonarnych za pomocą metody różnic skończonych  [1]. Wersję stacjonarną dla izogeometrycznej metody elementów skończonych zaproponował Longfei Gao w rozprawie doktorskiej pod kierunkiem prof. Victora Manuela Calo [2]. Rozważmy układ równań który dostaliśmy w rozdziale pierwszym (związany z problemem projekcji bitmapy). W wyniku generacji układu równień dostaliśmy tam macierz zapisaną w postaci produktu Kroneckera dwóch macierzy pięcio-przekątniowych, oraz prawą stronę policzoną z mozołem dla poszczególnych B-spline'ów testujących.  \( 	\begin{bmatrix}    \frac{1}{20} & \frac{13}{120} & \frac{1}{120} & \cdots \\    \frac{13}{120} & \frac{1}{2} &  \frac{13}{60} & \frac{1}{120} & \cdots \\       \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} & \cdots \\    \vdots & \vdots & \vdots &  \vdots & \vdots \\    \cdots & \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} & \cdots \\  &  \vdots &  \vdots & \vdots & \vdots &  \vdots &  \\ &   \cdots & \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} \\   & &  \cdots &  \frac{1}{120} & \frac{13}{60} &  \frac{1}{2} & \frac{13}{120}  \\   & & &  \cdots &   \frac{1}{20} & \frac{13}{120} & \frac{1}{120}  \\  \end{bmatrix} \otimes 	\begin{bmatrix}    \frac{1}{20} & \frac{13}{120} & \frac{1}{120} & \cdots \\    \frac{13}{120} & \frac{1}{2} &  \frac{13}{60} & \frac{1}{120} & \cdots \\       \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} & \cdots \\    \vdots & \vdots & \vdots &  \vdots & \vdots \\    \cdots & \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} & \cdots \\  &  \vdots &  \vdots & \vdots & \vdots &  \vdots &  \\ &   \cdots & \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} \\   & &  \cdots &  \frac{1}{120} & \frac{13}{60} &  \frac{1}{2} & \frac{13}{120}  \\   & & &  \cdots &   \frac{1}{120} & \frac{13}{120} & \frac{1}{20}  \\  \end{bmatrix}  \begin{bmatrix}   u_{1,1} \\ u_{2,1} \\ u_{3,1} \\ \vdots \\ u_{k,l} \\ \vdots \\ u_{N_{x-2},N_y} \\ u_{N_{x-1},N_y} \\ u_{N_x,N_y} \\  \end{bmatrix} \\ =    \begin{bmatrix} \int BITMAP(x,y) {\color{blue}B^x_1(x)*B^y_1(y)} dxdy \\ \int BITMAP(x,y) {\color{blue}B^x_2(x)*B^y_1(y)} dxdy \\ \int BITMAP(x,y) {\color{blue}B^x_3(x)*B^y_1(y)} dxdy \\ \vdots \\ \int BITMAP(x,y) {\color{blue}B^x_k(x)*B^y_l(y)} dxdy \\  \vdots \\ \int BITMAP(x,y) {\color{blue}B^x_{N_x-2}(x)*B^y_{N_y}(y)} dxdy \\ \int BITMAP(x,y) {\color{blue}B^x_{N_x-1}(x)*B^y_{N_y}(y)} dxdy \\  \int BITMAP(x,y) {\color{blue}B^x_{N_x}(x)*B^y_{N_y}(y)} dxdy \\ \end{bmatrix}    \) Rozwiązanie układu równań w którym macierz ma strukturę produktu Kroneckera jest możliwe w bardzo krótkim czasie. Co to znaczy w bardzo krótkim czasie? Koszt obliczeniowy wyraża się liczbą operacji takich jak mnożenie czy dodawanie liczb, koniecznych do rozwiązania układu równań. W przypadku układu równań w którym macierz ma strukturę produktu Kroneckera możliwe jest rozwiązanie układu równań za pomocą algorytmu w którym liczba operacji wynosi  \( const*N \) gdzie N to jest liczba niewiadomych (liczba współczynników aproksymacji bitmapy na siatce, wyrażona dokładnie poprzez  \( N=N_x*N_y \) gdzie  \( N_x,N_y \) to rozmiary siatki, natomiast  \( const \) oznacza pewną stałą liczbę. Algorytm stosowany w tym przypadku nazywany jest algorytmem solwera zmiennokierunkowego. Rozważamy dwa etapy procesu rozwiązania. Pierwszy etap polega na wzięciu pierwszej z podmacierzy, oraz poukładaniu wektora niewiadomych i wektora prawych stron w wiele podwektorów, po jednym wektorze dla każdej kolumny elementów siatki obliczeniowej. Zostało to zilustrowane na Rys. 1, oraz we wzorze poniżej.  \( 	\begin{bmatrix}    \frac{1}{20} & \frac{13}{120} & \frac{1}{120} & \cdots \\    \frac{13}{120} & \frac{1}{2} &  \frac{13}{60} & \frac{1}{120} & \cdots \\       \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} & \cdots \\    \vdots & \vdots & \vdots &  \vdots & \vdots \\    \cdots & \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} & \cdots \\  &  \vdots &  \vdots & \vdots & \vdots &  \vdots &  \\ &   \cdots & \frac{1}{120} & \frac{13}{60}  & \frac{11}{20} & \frac{13}{60} & \frac{1}{120} \\   & &  \cdots &  \frac{1}{120} & \frac{13}{60} &  \frac{1}{2} & \frac{13}{120}  \\   & & &  \cdots &   \frac{1}{120} & \frac{13}{120} & \frac{1}{20}  \\  \end{bmatrix}  \begin{bmatrix}  w_{1,1} & w_{1,2} & \cdots & w_{1,N_{y-1}} & w_{1,N_y} \\   w_{2,1} & w_{2,2} & \cdots & w_{2,N_{y-1}} & w_{2,N_y} \\   w_{3,1} & w_{3,2} & \cdots & w_{3,N_{y-1}} & w_{3,N_y} \\   \vdots & \vdots & \cdots & \vdots & \vdots \\  w_{N_{x-2},1} & w_{N_{x-2},2} & \cdots & w_{N_{x-2},N_{y-1}} & w_{N_{x-2},N_y} \\   w_{N_{x-1},1} & w_{N_{x-1},2} & \cdots & w_{N_{x-1},N_{y-1}} & w_{N_{x-1},N_y} \\   w_{N_x,1} & w_{N_x,2} & \cdots & w_{N_x,N_{y-1}} & w_{N_x,N_y} \\     \end{bmatrix} \\ =    \begin{bmatrix} \int BITMAP(x,y) {\color{blue}B^x_1(x)*B^y_1(y)} dxdy & \cdots &  \int BITMAP(x,y) {\color{blue}B^x_1(x)*B^y_{N_y}(y)}dxdy \\ \int BITMAP(x,y) {\color{blue}B^x_2(x)*B^y_1(y)} dxdy & \cdots &  \int BITMAP(x,y) {\color{blue}B^x_2(x)*B^y_{N_y}(y)}dxdy \\ \int BITMAP(x,y) {\color{blue}B^x_3(x)*B^y_1(y)} dxdy &  \cdots &  \int BITMAP(x,y) {\color{blue}B^x_3(x)*B^y_{N_y}(y)}dxdy \\ \vdots \\ \int BITMAP(x,y) {\color{blue}B^x_{N_x-2}(x)*B^y_1(y)} dxdy  & \cdots &  \int BITMAP(x,y) {\color{blue}B^x_{N_x-2}(x)*B^y_{N_y}(y)}dxdy \\ \int BITMAP(x,y) {\color{blue}B^x_{N_x-1}(x)*B^y_1(y)} dxdy & \cdots &  \int BITMAP(x,y) {\color{blue}B^x_{N_x-1}(x)*B^y_{N_y}(y)}dxdy \\ \int BITMAP(x,y) {\color{blue}B^x_{N_x}(x)*B^y_1(y)} dxdy & \cdots &  \int BITMAP(x,y) {\color{blue}B^x_{N_x}(x)*B^y_{N_y}(y)}dxdy \\  \end{bmatrix}   \) Wprowadziliśmy tutaj pomocnicze niewiadome  \( w* \) które służą do rozwiązania pierwszego układu równań. Oryginalne niewiadome  \( u* \) dostaniemy po rozwiązaniu drugiego układu równań, w którym prawą stronę stanowić będą niewiadome pomocnicze  \( w* \). Dostaliśmy więc układ równań z macierzą pięcio-przekątniową, o wielu prawych stronach. Każdy podwektor, każda prawa strona, odpowiada jednej kolumnie na siatce elementów, ma więc ustaloną współrzędną  \( y \), oraz współrzędną  \( x \)  zmieniającą się od 1 do  \( N_x \). Podobnie uporządkowane są niewiadome $u*$, w których to wierszami zmieniają się drugie indeksy, na przyklad  \( w_{1,1}, w_{1,2}, ..., w_{1,N_y} \) natomiast w kolumnach zmieniają się pierwsze indeksy. Rozwiązujemy ten układ równań (jak to zrobić o tym za chwilę), dostajemy rozwiązania  \( w* \) i przechodzimy do drugiego kroku algorytmu solwera zmiennokierunkowego. Drugi etap polega więc na wzięciu analogicznej drugiej macierzy produktu Kroneckera, wzięciu rozwiązań  \( w* \) z pierwszego układu równań, poukładaniu ich zgodnie z wierszami elementów z siatki (porównaj  Rys. 1, poukładaniu w podobny sposób poszukiwanych niewiadomych  \( u* \) i na rozwiązaniu otrzymanego układu równań o wielu prawych stronach.


Preconditioner

Kiedy nasz układ równań  \( Ax=b \) jest trudny do rozwiązania, wówczas skonstruować możemy preconditioner  \( M \) (inną macierz) taki że jeśli przemnożymy nasze równanie z lewej strony przez  \( M^{-1} \) czyli  \( M^{-1}Ax=M^{-1}b \) oraz dodatkowo pomiędzy  \( Ax \)  wsadzimy  \( M^{-1}M \) (możemy to zrobić bo  \( M^{-1}M \) jest macierzą identycznościową), wówczas nowy układ równań  \( M^{-1} A M^{-1} M x = M^{-1} b \) może być łatwiejszy do rozwiązania. Oczywiście wszystko zależy od sprytnej konstrukcji macierzy  \( M \). Przykład podano w rozdziałe Solwer iteracyjny. Preconditionery dla algorytmów iteracyjnych opisane są w rodziałe 8 podręcznika Józefa Saada [1].


Algorytm solwera iteracyjnego

Algorytmy solwerów iteracyjnych opisane zostały są w bardzo obszerny sposób w podręczniku Józefa Saada [1]. Prawdopodobnie pierwszym solwerem iteracyjnym była metoda rozwiązywania układu równań 4 na 4 zaproponowana przez Gaussa. Omawianie ogólnej rodziny algorytmów solwerów iteracyjnych wybiega poza ramy tego podręcznika. W module tym omówimy solwer iteracyjny mający zastosowanie w problemie projekcji w którym obszar ma dowolny kształt, nie będący regularnym kwadratem.

Niech naszym przykładowym problemem do rozwiązania będzie problem projekcji dwuwymiarowej zdefiniowany na obszarze który nie ma kształtu kwadratu (na przykład problem projekcji bitmapy na pofałdowany obszar). Wówczas nasz układ równań do rozwiązania wygląda następująco  \( 	\begin{bmatrix}    \int_{\Omega}{B^x_{1,p}B^y_{1,p}}{B^x_{1,p}B^y_{1,p}} & \int_{\Omega}{B^x_{1,p}B^y_{1,p}}{B^x_{2,p}B^y_{1,p}} & \cdots &  \int_{\Omega}{B^x_{1,p}B^y_{1,p}}{B^x_{N_x,p}B^y_{N_y,p}} \\    \int_{\Omega}{B^x_{2,p}B^y_{1,p}}{B^x_{1,p}B^y_{1,p}} & \int_{\Omega}{B^x_{2,p}B^y_{1,p}}{B^x_{2,p}B^y_{1,p}} & \cdots &  \int_{\Omega}{B^x_{2,p}B^y_{1,p}}{B^x_{N_x,p}B^y_{N_y,p}} \\       \vdots & \vdots & \vdots &  \vdots \\        \int_{\Omega}{B^x_{N_x,p}B^y_{N_y,p}}{B^x_{1,p}B^y_{1,p}} & \int_{\Omega}{B^x_{N_x,p}B^y_{N_y,p}}{B^x_{2,p}B^y_{1,p}} & \cdots &  \int_{\Omega}{B^x_{N_x,p}B^y_{N_y,p}}{B^x_{N_x,p}B^y_{N_y,p}} \\  \end{bmatrix}  \begin{bmatrix}  {\color{red}u_{1,1}} \\ {\color{red}u_{2,1}} \\ \vdots \\ {\color{red}u_{N_x,N_y}}\\  \end{bmatrix}  \)  \( =    \begin{bmatrix} \int_{\Omega} BITMAP(x,y) {\color{blue}B^x_1(x)*B^y_1(y)}  \\ \int_{\Omega} BITMAP(x,y) {\color{blue}B^x_1(x)*B^y_2(y)}  \\ \vdots \\ \int_{\Omega} BITMAP(x,y) {\color{blue}B^x_{N_x}(x)*B^y_{N_y}(y)}  \\  \end{bmatrix}  \) gdzie  \( \Omega \) nie jest już kwadratem tylko naszym ``pofałdowanym'' obszarem. Wyprowadzenie tego układu równań przebiega w taki sam sposób jak opisano to w rozdziale pierwszym dla zadaniam projekcji bitmapy, jedyną różnicą jest fakt iż naszą projekcję wykonujemy teraz na "pofałdowany" obszar. Na przykład chcemy obliczyć i zwizualizować jak wyglądać będzie bitmapa nałożona na pofałdowany materiał (np. na zasłonę). Oczywiście dla każdej całki po obszarze  \( \Omega \) możemy dokonać zmiany zmiennych na obszar kwadratowy, i wtedy w całce znajdzie się jacobian tej transformacji  \( \int_{\Omega} {\color{blue}B^x_{i}(x)B^y_{j}(y)}  \color{red}{B^x_{k}(x)B^y_{l}(y)} dxdy = \int_{[0,1]^2} \color{blue}{B^x_{i}(x)B^y_{j}(y)}  \color{red}{B^x_{k}(x)B^y_{l}(y)}  |Jac(x,y)|  dxdy \) Chcielibyśmy użyć do faktoryzacji naszej macierzy solwera zmiennokierunkowego, jednakże nie jest to możliwe, ponieważ nie potrafimy rozdzielić zmiennych w Jacobianie, nie wiemy jak przetworzyć  \( |Jac(x,y)|=|Jac_x(x)|*|Jac_y(y)| \). Na szczęście możemy zastosować następujący algorytm iteracyjny z preconditionerem


Wybór solwera w zależności od rodzaju problemu

Metoda elementów skończonych służyć może do rozwiązania wielu problemów inżynerskich. Ogólny schemat działania metody jest podobny, niezależnie od rozważanego problemu. W naszym przypadku dla uproszczenia wybieramy problem projekcji bitmapy. Podstawowe etapy metody elementów skończonych wymienione są poniżej. Ostatnim etapem jest wybór algorytmu służącego do rozwiązania wygenerowanego układu równań. Algorytm ten zależy od struktury macierzy, która pośrednio zależy od rozważanego problemu inżynierskiego.

Dla problemów dla których macierze są symetryczne i dodatnio określone, często używane są algorytmy iteracyjne bazujące na metodach Krylova, na przykład algorytm gradientów sprzężonych. Istnieją również algorytmy iteracyjne dla szerszej klasy problemów, np. algorytm GMRES. Algorytmy solwerów iteracyjnych opisane zostały są w bardzo obszerny sposób w podręczniku Józefa Saada [1]. Liczba iteracji dla solwerów iteracyjnych zależy od rodzaju rozwiązywanego problemu, od struktury i własności macierzy po dyskretyzacji. W szczególności na zbieżność solwera typu gradientów sprzężonych wpływa współczynnik uwarunkowania macierzy, czyli iloczyn modułu z największej wartości własnej macierzy i modułu z największej wartości własnej macierzy odwrotnej. Jeśli współczynnik uwarunkowania macierzy jest za duży, wówczas tworzone są tak zwane preconditionery, czyli macierze przez które przemnaża się dodatkowo nasz układ równań w taki sposób żeby rozwiązanie było takie samo, ale współczynnik uwarunkowanie macierzy był lepszy. Niestety problem doboru preconditionera jest zaleźny od rozwiązywanego problemu (nie ma uniwersalnych zawsze skutecznych algorytmów).

Solwery dokładne z koleji potafią rozwiązać każdy problem, potrafią sfaktoryzować każdą macierz, jednak ich koszt jest zazwyczaj większy od kosztu solwerów iteracyjnych. Dla jednorodnych siatek dwuwymiarowych koszt ten jest rzędu  \( {\cal O } ( N^{1.5 }) \). Dla jednorodnych siatek trójwymiarowych koszt ten jest rzędu  \( {\cal O }(N^2)  \). Wyjątkiem jest algorytm solwera zmienno-kierunkowego, którego koszt jest liniowy  \( {\cal O}(N) \). Algorytm solwera zmienno-kierunkowego może być zastosowany wtedy, kiedy macierz ma strukturę produktu Kroneckera. Dzieje się tak na przykład w problemie projekcji, lub podczas symulacji niestacjonarnych za pomocą metody explicite. Algorytm solwera wielo-frontalnego, jest używany dla problemów dla których macierze nie są symetryczne lub nie są dodatnio określone. Algorytm solwera wielo-frontalnego jest szczególną implementacją algorytmu eliminacji Gaussa lub LU faktoryzacji. Wersja iteracyjna algorytmu zmienno-kierunkowego, opisana w tym podręczniku używana jest do symulacji explicite na nieregularnych geometriach.


Implementacja w MATLABie algorytmu solwera zmienno-kierunkowego dla problemu projekcji bitmapy

W module tym przedstawiamy kod w MATLABie obliczający L2 projekcję bitmapy, stosując izogeometryczną metodę elementów skończonych. W szczególności kod ten posiada implementacje algorytmu solwera zmienno-kierunkowego. Wykonanie kodu\ów możliwe jest również w darmowym środowisku Octave.

W celu uruchomienia kodów, zapisujemy je w katalogu roboczym Octave. Ustawiamy zmienne ze ścieżką do pliku wejściowego w formacie tif  \( filename = 'C://Users/Maciej/Dropbox/bitmapa.tif' \) następnie podajemy ilość elementów siatki w kierunku osi x i y, oraz stopnie funkcji B-spline w tych kierunkach  \( nx=4 \)  \( ny=4 \)  \( px=2 \)  \( py=2 \) Następnie uruchamiamy pierwszą procedure  \( bitmap\_splitting(filename,nx,ny,px,py) \). Kod po zbudowaniu układu równań i rozwiązaniu go rysuje projekcje bitmapy w otwartym okienku.


Stabilizacja metody elementów skończonych

Podczas wykonywania obliczeń metodą elementów skończonych wykonujemy następujące czynności

W rozdziale tym skoncentrujemy się na zagadnieniu stabilności metody elementów skończonych. Najważniejsze twierdzenie matematyczne pozwalające badać stabilność metody elementów skończonych zostało niezależnie zaproponowane przez prof. Ivo Babuśke, prof. Franco Brezzi, oraz prof. Olga Ładyżenska [1] [2]. Odkryli oni mniej więcej w tym samym czasie, niezależnie od siebie, równoważne warunki, zwane obecnie warunkiem inf-sup ("inf-sup condition"). Warunek inf-sup jest do dzisiaj używany przez naukowców do badania stabilności metody elementów skończonych. Warunek ten zdefiniować można w abstrakcyjnych nieskończenie wymiarowych przestrzeniach matematycznych  \(  \inf_{u \in U } \sup_{v \in V } \frac{b_h(u,v)}{\|u\| \|v\| } = \gamma > 0  \) lub w przestrzeniach skończenie wymiarowych, wynikających z naszego przybliżenia rozwiązania za pomocą bazy funkcji B-spline, używanej do aproksymacji i testowani rozwiązania  \(  \inf_{u \in U } \sup_{v \in V } \frac{b_h(u,v)}{\|u\| \|v\| } = \gamma > 0  \) Może zdażyć się że nawet jeśli matematycy udowodnili spełnienie warunku inf-sup na przestrzeniach nieskończenie wymiarowych, to w momencie gdy zaczniemy rozwiązywać nasz problem w przestrzeniach skończenie wymiarowych, to warunek inf-sup nie będzie już spełniony. Na przykład, jeśli wybierzemy taką skończenie wymiarową przestrzeń testującą, że supremum z warunku inf-sup nie będzie spełnione,  \(  \inf_{u \in U } \sup_{v_h \in V_h } \frac{b_h(u,v_h)}{\|u\| \|v_h\| } = \gamma > 0  \) to wówczas nawet gdyby przestrzeń aproksymacyjna była nieskończenie wymiarowa, to i tak nasza symulacja będzie niestabilna. Problem którego dotyczy warunek inf-sup polega na tym, iż wybierając nasze skończenie wymiarowe zbiory (tak zwane bazy) funkcji do aproksymacji rozwiązania oraz do testowania naszych równań, nie mamy pewności czy rozwiązanie uzyskane za ich pomocą na komputerze będzie dostatecznie dokładne. Żeby to sprawdzić, należy zastosować bardzo zaawansowane narzędzia matematyczne, takie jak warunek inf-sup. Dla dużej klasy problemów rozwiązywanych na komputerach za pomocą metody elementów skończonych warunek ten jest spełniony, i uzyskujemy całkiem dokładne rozwiązania. Ale jest też dość duża klasa problemów dla których warunek inf-sup nie jest spełniony. Warunek ten napisać można w sposób następujący. Wybieramy wersję sformułowaną przez prof. Ivo Babuśkę (wszystkie wersje tego warunku opisane są w pracy Leszka Demkowicza, ICES Report 0608, 2006 "Babuśka <=> Brezzi?" ) [3]:  \(  \inf_{u_h \in U_h } \sup_{v_h \in V_h } \frac{b_h(u_h,v_h)}{\|u_h\| \|v_h\| } = \gamma_h > 0  \) Jeśli warunek ten jest spełniony, będzie dało się rozwiązać układ równań wygenerowany za pomocą metody elementów skończonych i rozwiązanie to będzie całkiem dobrym przybliżeniem idealnego rozwiązania  \(  u \approx u_h = \sum_{i,j=1,...,6} u_{i,j} B^x_{i,2}(x)B^y_{j,2 }(x)  \). Jeśli warunek ten nie jest spełniony, wówczas albo wygenerowany układ równań nie będzie możliwy do rozwiązania (na przykład algorytm eliminacji Gaussa przewróci się ponieważ na przekątnej macierzy powstanie numeryczne zero (bardzo mała liczba przez którą nie będzie się dało podzielić wiersza w macierzy), albo uzyskane rozwiązanie układu równań będzie niepoprawnym rozwiązaniem naszego problemu, np. powstaną oscylacje numeryczne). W takiej sytuacji konieczna jest stabilizacja naszego problemu, która polega na

Wszystkie te metody stabilizacji umożliwiają matematyczne udowodnienie spełnienia warunku inf-sup. Dowody spełniania tego warunku są dość zaawansowane matematycznie, tak więc ograniczymy się jedynie do podania kilku metod stabilizacji (SUPG, minimalizacji reziduum oraz metoda DG) które gwarantują spełnianie tego warunku, ale nie podamy wyjaśnienia dlaczego ten warunek jest spełniony, ponieważ wymagałoby to wprowadzenia zaawansowanych narzędzi matematycznych. Metoda minimalizacji reziduum leży również u podstaw metody Discontinuous Petrov-Galerkin (DPG) cieszącej się dużym powodzeniem metody stabilizacji adaptacyjnych obliczeń elementami skończonymi, zaproponowanej przez prof. Leszka Demkowicza. W metodzie DPG przestrzenie aproksymacyjne i testujące są połamane, w celu umożliwienia lokalnej statycznej kondensacji na poszczególnych elementach skończonych [4] [5] [6].


Stabilizacja równań adwekcji-dyfuzji za pomocą metody minimizalizacji reziduum

Rozważmy dla ustalenia uwagi nasz problem adwekcji-dyfuzji (obliczenia propagacji zanieczyszczeń na danym obszarze za pomocą dyfuzji oraz wiatru)  \(  b(u,v)=l(v)  \\ b(u,v) = \int_{\Omega} \beta_x(x,y) \frac{\partial u(x,y)}{\partial x} dxdy \int_{\Omega } \beta_y(x,y) \frac{\partial u(x,y)}{\partial y } dxdy \\ \int_{\Omega} \epsilon \frac{\partial u(x,y)}{\partial x }  \frac{\partial v(x,y)}{\partial x } dxdy  +\int_{\Omega} \epsilon  \frac{\partial u(x,y)}{\partial y }  \frac{\partial v(x,y)}{\partial y } dxdy \\ l(v) = \int_{\Omega} f(x,y) v(x,y) dxdy  \) Oznaczamy przez  \(  U   \) zbiór funkcji w którym zawiera się rozwiązanie problemu adwekcji-dyfuzji  \(  u \in U   \). Następnie  przez  \( V   \) oznaczmy zbiór funkcji które używamy do testowania w naszym sformułowaniu wariacyjnym (zwanym też słabym). W rozdziale tym opiszemy za pomocą trochę abstrakcyjnego języka matematycznego jak wyprowadza się metodę minimalizacji reziduum [1]. Nasz oryginalny problem zapisać można tak: szukamy funkcji  \(  u \in U   \) takiej że  \(  b(u,v)=l(v)   \) dla wszystkich funkcji testujących  \(  v\in V    \). Matematycznie można oznaczyć lewą stronę  \(  Bu  \) a prawą stronę  \( l   \) wprowadzając pewne operatory matematyczne (matematycy powiedzą że  \(  B : U \rightarrow V'   \) oraz  \( \langle Bu, v \rangle = b(u,v)    \). Wówczas nasz problem sprowadza się do znalezienia pola skalarnego  \( u   \) takiego że  \(  Bu-l=0   \). Wyrażenie  \(  r=Bu-l   \) nazywa się reziduum. Jeśli mamy jedynie rozwiązanie przybliżone, na przykład oznaczone  \( u_h    \) wówczas nasze reziduum będzie wyrażało błąd, im większe będzie reziduum tym błąd rozwiązania przybliżonego będzie większy   \(  r_h=Bu_h-l   \). W jaki sposób zmierzyć błąd rozwiązania, czyli wielkość reziduum? Żeby zmierzyć jak duże jest reziduum musimy wprowadzić jakąś normę (czyli miarę wielkości)  \(  r_h = \| Bu_h-l \|   \). Mogę teraz powiedzieć że szukamy takiego rozwiązania naszego problemu  \(  u_h   \) żeby reziduum (czyli błąd) był jak najmniejszy. Matematycznie zapisuje się ten warunek w postaci minimalizacji normy  \(  u_h = \displaystyle{argmin}_{w_h \in U } {1 \over 2} \| Bw_h - l \|_{V' }^2  \) Przed normą dopisuje się  \( 1\over 2    \) oraz podnosi się normę do kwadratu. Problem praktyczny jest taki że nie wiemy jak policzyć normę z różnicy  \(  \|Bw_h-l\|   \). Norma ta mierzy bowiem odległości w abstrakcyjnej przestrzeni matematycznej  \( V'   \) (matematycy powiedzą że przestrzeń ta jest przestrzenią dualną do przestrzeni funkcji testujących). Żeby rozwiązać ten problem, wykonujemy trick matematyczny polegający na rzutowaniu normy z przestrzeni  \( V'   \) do przestrzeni testującej  \(   V \). Wprowadza się operator przerzucający przestrzeń   \(  V  \) w przestrzeń  \(  V'   \). Operator ten nazywamy operatorem Riesza  \(  R_V \colon V \ni v \rightarrow (v,.) \in V'   \). Operator odwrotny natomiast przerzuca przestrzeń   \(  V'  \) z powrotem w przestrzeń  \(  V  \), czyli  \(  R_V^{-1} \colon V' \ni (v,.) \rightarrow v \in V   \). Tak więc jeśli użyjemy odwrotnego operatora Riesza, przerzucimy nasz problem do przestrzeni funkcji testujących  \(  V  \). Mamy więc  \(  u_h = \displaystyle{argmin}_{w_h \in U_h} {1 \over 2} \| {\color{red}{R_V^{-1}}} (Bw_h - l) \|_{\color{red}{V} }^2   \). Szukamy więc punktu w którym funkcja osiąga minimum. Taki punkt osiągany jest wtedy kiedy pochodne tej funkcji są równe zero. Ponieważ mamy tutaj funkcjonał, matematycy powiedzą że szukamy takiego  \(  u_h   \) w którym pochodne Gateaux są równe zero we wszystkich kierunkach  \(   \langle R_V^{-1} (Bu_h - l), R_V^{-1}(B\, w_h) \rangle_V = 0 \quad \forall \, w_h \in U_h  \) gdzie  \(  w_h   \) oznacza wszystkie możliwe "kierunki" ze skończenie wymiarowej przestrzeni funkcji  \(  U_h   \) (w której przy okazji szukamy rozwiązania naszego problemu adwekcji-dyfuzji). Formalnie rzecz ujmując, w tym miejscu musimy skonstruować skończenie wymiarową przestrzeń funkcji  \(  U_h   \) w której będziemy szukać naszego rozwiązania. Robimy to oczywiście w naszym podręczniku stosując funkcje bazowe B-spline, rozpinając je zgodnie z wektorami węzłów wzdłuż osi x oraz wzdłuż osi y. Wzdłuż osi  \(  x  \) wprowadzamy wektor węzłów [0 0 0 1 2 3 4 4 4], podobnie wzdłuż osi  \(  y  \)  wprowadzamy wektor węzłów [0 0 0 1 2 3 4 4 4]. Uzyskaliśmy dwie bazy jednowymiarowych funkcji B-spline  \(  \{ B_{i,2}(x) \}_{i=1,...,6 }  \) oraz  \(  \{B_{j,2}(y)\}_{j=1,...,6 }  \). Następnie utworzymy z nich iloczyny tensorowe  \(  B_{i,j;2}(x,y)=B_{i,2}(x)B_{j,2}(y),i,j=1,...,6  \). Zauważmy że nasz obszar  \(  \Omega  \) rozpięty jest na kwadracie  \(  [0,1]^2  \), podobnie jak nasze 6*6=36 funkcji bazowych co wynika z definicji wektora węzłów [0 0 0 1 2 2 3 4 4 4]. Nasza przestrzeń aproksymacyjna jest więc rozpięta przez iloczyny tensorowe naszych jednowymiarowych funkcji B-spline  \(  U_h = gen \{ B_{i,j;2}(x,y)=B_{i,2}(x)B_{j,2}(y),i,j=1,...,6 \}  \). Jest więc ona przestrzenią 6*6=36 wymiarową, Mamy więc 36 kierunków (wielomianów bazowych B-spline) w których liczyć będziemy pochodne kierunkowe i będziemy je przyrównywać do zera. Oznaczamy  \(  r=R_V^{-1}(Bu_h-l)   \) i wówczas nasz problem minimalizacji reziduum zapisać można w postaci  \(  \langle r , R_V^{-1} (B\,  w_h ) \rangle = 0 \quad \forall \, w_h \in U_h   \) co jest równoznaczne  \(  \langle Bw_h, r  \rangle = 0 \quad \quad \forall w_h \in U_h.   \) Dorzucamy teraz drugie równanie, korzystając z definicji reziduum  \(  r=Bu_h-l   \), przemnażam definicje reziduum przez funkcje testujące z przestrzeni funkcji testujących i dostaje równanie:  \(  (r,v)_V=\langle B u_h-l,v \rangle \quad \forall v\in V.   \) Mam więc układ równań: znajdź reziduum w nieskończenie wymiarowej przestrzeni funkcji testujących \(  r\in V   \) oraz rozwiązania w skończenie wymiarowej przestrzeni funkcji  \(  u_h \in U_h   \)  \(  (r,v)_V - \langle Bu_h-l ,v \rangle = 0 \quad  \forall v\in V \\ \langle Bw_h,r\rangle = 0 \quad  \forall w_h \in U_h    \) Gdybyśmy byli wstanie rozwiązać to równanie, to dostali byśmy najlepszą możliwą aproksymacje naszego problemu w przestrzei funkcji aproksymujących  \(   U_h  \). Niestety nie jest to możliwe, ponieważ nieskończona przestrzeń funkcji testujących generuje nam nieskończenie wiele równań które należało by rozwiązać. Musimy więc wybrać drugą inną bazę do przybliżenia przestrzeni testowej. Tutaj pojawia się pierwsza istotna różnica metody minimalizacji reziduum z tradycyjną metodą elementów skończonych. Otóż w metodzie minimalizacji reziduum mamy dwie różne przestrzenie, jedną do aproksymacji rozwiązania, drugą do testowania.Oczywiście przestrzeń testującą również rozpinamy za pomocą funkcji B-spline. Na przykład możemy użyć takiego samego patcha elementów jak dla przestrzeni aproksymującej (ale nie jest to wymagane!), albo podnieść stopień funkcji B-spline. Precyzujemy jak wyglądać będzie nasz patch elementów oraz nasze funkcje bazowe B-spline rozpięte na elementach, podając wektor węzłów wzdluż osi  \(  x  \) oraz wektor węzłów wzdłuż osi  \(  y  \). Odsyłamy tutaj do rozdziału trzeciego w którym opisany jest sposób definiowania funkcji bazowych za pomocą wektorów węzłów i wielomianów B-spline. Wzdłuż osi  \(  x  \) wprowadzamy wektor węzłów [0 0 0 0 11 2  2 3 3 4 4 4 4], podobnie wzdłuż osi  \(  y  \)  wprowadzamy wektor węzłów [0 0 0 0 1 1 2 2 3 3 4 4 4 4]. Generalnie przestrzenie testujące muszą być większe niż przestrzeń aproksymacyjna. Możemy uzyskać ten efekt zwiększając liczbę elementów lub podnosząc stopień funkcji B-spline, lub redukując ciągłość bazy funkcji B-spline, lub mieszając te trzy podejścia razem. Uzyskaliśmy dwie bazy jednowymiarowych funkcji B-spline  \(  \{ \tilde{B}_{k,3}(x) \}_{k=1,...,10 }  \) oraz  \(  \{\tilde{B}_{l,3}(y)\}_{l=1,...,10 }  \). Następnie utworzymy z nich iloczyny tensorowe  \(  \tilde{B}_{k,l;3}(x,y)=\tilde{B}_{k,3}(x)B_{l,3}(y),k,l=1,...,10  \). Nasza przestrzeń testujące to  \(  V_h = gen \{ \tilde{B}_{k,l;3}(x,y)=\tilde{B}_{k,3}(x)B_{l,3}(y),i,j=1,...,10 \}  \). Mając dyskretną przestrzeń testującą, uzyskujemy wreszcie nasz dyskretny (czyli skończenie wymiarowy) układ równań który będziemy chcieli rozwiązać. Szukamy  \(   ( r_m, u_h )_{ V_m \times U_h }  \)  \(  (r_m,v_m)_{V_m} - \langle B u_h-l,v_m \rangle = 0 \quad  \forall v\in V_m \\\langle Bw_h,r_m\rangle = 0 \quad \forall w_h \in U_h   \) Pojawia się tutaj tak zwany iloczyn skalarny (wewnętrzny) w dyskretnej przestrzeni testującej  \(  (*,*)_{V_m}   \). Żeby odgadnąć jaki iloczyn skalarny użyć w naszym problemie, musimy popatrzeć na nasze oryginalne równanie zapisane w formie słabej  \(  b(u,v)=l(v)  \\ b(u,v) = \int_{\Omega} \beta_x(x,y) \frac{\partial u(x,y)}{\partial x} v(x,y) dxdy+ \int_{\Omega } \beta_y(x,y) \frac{\partial u(x,y)}{\partial y }v(x,y)  dxdy +\\ \int_{\Omega}\epsilon \frac{\partial u(x,y)}{\partial x }  \frac{\partial v(x,y)}{\partial x } dxdy  +\int_{\Omega} \epsilon \frac{\partial u(x,y)}{\partial y }  \frac{\partial v(x,y)}{\partial y } dxdy \\ l(v) = \int_{\Omega} f(x,y) v(x,y) dxdy  \) i zobaczyć jakiej formy są funkcje testujące   \(  v   \) w formie  \(  b(u,v)   \). Widzimy że mamy tutaj doczynienia z pochodnymi cząstkowymi  \(   \frac{\partial v(x,y)}{\partial x }   \),  \(  \frac{\partial v(x,y)}{\partial y }    \) oraz z samą funkcją  \(  v(x,y)   \). Nasz produkt wewnętrzny powinien więc zawierać pochodne i wartości funkcji  \(  (u,v)_{V_m} = \int_{\Omega} u(x,y) v(x,y) dxdy + \int_{\Omega} \frac{\partial u(x,y)}{\partial x }  \frac{\partial u(y,y) }{\partial x } dxdy  + \int_{\Omega} \frac{\partial u(x,y)}{\partial y }  \frac{\partial u(y,y)}{\partial y } dxdy \) Podsumowując, w metodzie minimalizacji reziduum, musimy zdefiniować osobną przestrzeń aproksymacyjną, oraz osobną (większą) przestrzeń testującą, oraz wybrać iloczyn wewnętrzny przestrzeni testującej. Dostajemy wówczas układ równań w którym niewiadome to nasze rozwiązanie  \(  u   \) oraz dodatkowo reziduum  \(   r  \). To jak dobrze działać będzie metoda minimalizacja reziduum zależy w dużej mierze od naszego wyboru przestrzeni testującej oraz iloczynu wewnętrznego. Jeśli iloczyn wewnętrzny będzie dostatecznie dokładny, oraz przestrzeń testująca będzie dostatecznie duża, wszystko będzie idealnie działać, i dostaniemy najlepsze możliwe do uzyskania rozwiązanie w przestrzeni aproksymacyjnej  \(  U_h   \) (ale nie lepsze niż pozwala na to przestrzeń aproksymacyjna). Matematyczne uzasadnienie jest takie, że problem minimalizacji reziduum z nieskończenie wymiarową przestrzenią testową realizuje warunek stabilności inf-sup  ze stałą równą 1. Jeśli przestrzeń testującą zawęzimy do przestrzeni skończenie wymiarowej, to warunek inf-sup może nie być już idealnie realizowany w tej przestrzeni. Zwiększanie przestrzeni testującej będzie nam poprawiać realizowalność warunku stabilności inf-sup. Nasz układ równań do rozwiązania w metodzie minimalizacji reziduum wyglada następująco  \( \left(  \begin{matrix} G & B \\ B^T & 0 \\ \end{matrix} \right) \left( \begin{matrix} r \\ u \end{matrix} \right)=  \left( \begin{matrix} f \\ 0 \end{matrix} \right)  \) Zobaczmy jak wygląda nasz układ równań na przykładzie konkretnych przestrzeni aproksymacyjnej i testującej.


Stabilizacja równań adwekcji-dyfuzji za pomocą metody Streamline Upwind Petrov-Galerkin (SUPG)

W rozdziale tym opiszemy metodę stabilizacji równań adwekcji-dyfuzji zwaną Streamling Upwind Petrov-Galerkin (SUPG) wprowadzoną przez prof. T. J. R. Hughesa. Metoda ta opisana jest na przykład w pracy [1]. Przypomnijmy najpierw problem adwekcji-dyfuzji, problem modelowy Erikksona-Johnsona, opisany na przykład w pracy [2]. Problem zdefiniowany jest na obszarze kwadratowym  \(  \Omega = [0,1]^2  \) w następujący sposób: Szukamy funkcji \(  u  \) takiej że  \(  a(u,v)=l(v) \forall v  \) gdzie  \(  a(u,v) =\int_{\Omega} \beta_x(x,y) \frac{\partial u(x,y) }{\partial x } dxdy + \int_{\Omega} \beta_y(x,y) \frac{\partial u(x,y)}{\partial y } dxdy  \\ +\int_{\Omega} \epsilon \frac{\partial u(x,y) }{\partial x}  \frac{\partial v(x,y)}{\partial x } dxdy +\int_{\Omega} \epsilon \frac{\partial u(x,y)}{\partial y }  \frac{\partial v(x,y)}{\partial y } dxdy  \)  \(  l(v) = \int_{\partial \Omega } f(x,y) v dxdy  \)  \(  f(x,y)=sin(\pi y)(1-x)  \) jest rozszerzeniem warunku brzegowego Dirichleta na cały obszar, natomiast  \(  \beta = (1,0)  \) reprezentuje wiatr wiejący z lewej strony na prawą, natomiast   \(  \epsilon = 10^{-6} \) oznacza współczynnik dyfuzji. Metoda stabilizacji SUPG (Streamline Upwind Petrov-Galerkin) modyfikuje nasze równanie dodając pewne człony które nie zmieniają "sensu" równania, natomiast wymuszają spełnienie warunku inf-sup  \(  a(u,v) +{ \color{red}{\sum_K (R(u)+f,\tau \beta\cdot \nabla v)_K } }=l(v)+ {\color{blue}{\sum_K (f,\tau \beta\cdot \nabla v)_K } } \quad \forall v\in V  \) gdzie  \(  R(u)=\beta \cdot \nabla u + \epsilon \Delta u-f =\frac{\partial u}{\partial x }+\epsilon \Delta u-f  \) reprezentuje reziduum naszego rozwiązania, czyli błąd pomiędzy lewą i prawą stroną, wynikający z dyskretyzacji naszego problemu. Innymi słowy, na poziomie ciągłym, w abstrakcyjnych nieskończenie wymiarowych przestrzeniach funkcyjnych (które teoretycznie pozwalają na dokładne przybliżenie rozwiązania) reziduum wynosi zero (w abstrakcyjnych przestrzeniach nieskończenie wymiarowych nie popełniamy błędu). Jednakże, ponieważ używamy siatki obliczeniowej i skończonej liczby funkcji aproksymacyjnych, reziduum na poziomie dyskretnym zmierzy nam błąd metody.  \(  \tau^{-1}=\left(\frac{\beta_x}{h_x} + \frac{\beta_y}{h_y} \right) + 3\epsilon \frac{1}{h_x^2+h_y^2}  \)  \(  \epsilon=10^{-6}  \)  \(  \beta = (1,0)  \)  \(  h_x,h_y  \) to rozmiary elementu (nasze całki dzielimy na poszczególne elementy i na każdym elemencie liczymy całkę zmodyfikowaną z pomocą wzoru zawierającego średnicę elementu), oraz  \(  (u,v)_K = \int_K uvdx  \) oznacza iloczyn skalarny nad elementem  \(  K  \).


Stabilizacja równań Stokesa za za pomocą metody minimalizacji reziduum

Rozważmy problem Stokesa, który wymaga dodatkowej stabilizacji. Zapiszmy go w postaci pojedynczego funkcjonału  \(  B(({\bf u},p),({\bf v},q)) = L({\bf v })   \) gdzie  \(  B(({\bf u},p),({\bf v},q)) = \left( \begin{matrix} a({\bf u}, {\bf v}) + b(p, {\bf v}) \\ b(q, {\bf u} ) \\ \end{matrix} \right)  \)  \(   L({\bf v })  = \left( \begin{matrix} f({\bf v} \\ 0 \\ \end{matrix} \right)  \) oraz  \(  a({\bf u}, {\bf v }) =\int_{\Omega} \frac{\partial u_1}{\partial x}\frac{\partial v_1}{\partial x}dxdy + \int_{\Omega} \frac{\partial u_1}{\partial y}\frac{\partial v_1}{\partial y}dxdy + \int_{\Omega} \frac{\partial u_2}{\partial x}\frac{\partial v_2}{\partial x}dxdy + \int_{\Omega} \frac{\partial u_2}{\partial y}\frac{\partial v_2}{\partial y }dxdy \)  \(  b(p, {\bf v }) = \int_{\Omega}p  \frac{\partial v_1}{\partial x}  dxdy+ \int_{\Omega} p \frac{\partial v_2}{\partial y }  dxdy   \) Oznaczamy przez  \(  {\bf U} \times P   \) przestrzeń  funkcji w którym poszukujemy rozwiązanie problemu Stokesa   \(  ({\bf u},p) \in {\bf U } \times P  \). Następnie oznaczamy przez  \( {\bf V } \times Q   \) przestrzeń funkcji których używamy do testowania w naszym sformułowaniu wariacyjnym (zwanym też słabym). W rozdziale tym opiszemy za pomocą trochę abstrakcyjnego języka matematycznego jak wyprowadza się metodę minimalizacji reziduum [1]. Nasz oryginalny problem zapisać można tak: szukamy funkcji  \(  ({\bf u},p) \in {\bf U } \times P   \) takich że  \( B( ({\bf u},p),({\bf v},q) ) = L({\bf v })   \) dla wszystkich funkcji testujących  \(  ({\bf v },q) \in V\times Q    \). Matematycznie można oznaczyć lewą stronę  \(  B(u,p)  \) a prawą stronę  \( L  \) wprowadzając pewne operatory matematyczne (matematycy powiedzą że  \(  B : ({\bf U}\times P) \rightarrow (V\times Q)'   \) oraz  \( \langle B({\bf u},p), ({\bf v},q) \rangle =B(({\bf u},p),({\bf v },q))    \). Wówczas nasz problem sprowadza się do znalezienia  \( ({\bf u},p)   \) takiego że  \(  B({\bf u },p)-l=0   \). Wyrażenie to nazywa się reziduum  \(  r=B({\bf u },p)-l   \). Jeśli mamy jedynie rozwiązanie przybliżone, na przykład oznaczone  \( ({\bf u_h },p_h)    \) wówczas nasze reziduum będzie wyrażało błąd, im większe będzie reziduum tym błąd rozwiązania przybliżonego będzie większy   \(  r_h=B({\bf u_h },p_h)-l   \).W jaki sposób zmierzyć błąd rozwiązania, czyli wielkość reziduum? Żeby zmierzyć jak duże jest reziduum musimy wprowadzić jakąś normę (czyli miarę wielkości)  \(  r_h = \| B({\bf u_h },p_h)-l \|   \). Mogę teraz powiedzieć że szukamy takiego rozwiązania naszego problemu  \(  ({\bf u_h },p_h)   \) żeby reziduum (czyli błąd) był jak najmniejszy. Matematycznie zapisuje się ten warunek w postaci minimalizacji normy  \(  ({\bf u_h },p_h) = \displaystyle{argmin}_{({\bf w_h },s_h) \in U\times P } {1 \over 2} \| B({\bf w_h},s_h) - l \|_{({\bf V},Q)' }^2  \) Przed normą dopisuje się  \( 1\over 2    \) oraz podnosi się normę do kwadratu. Problem praktyczny jest taki że nie wiemy jak policzyć normę z różnicy  \(  \|B({\bf w_h },s_h)-l\|   \). Normę tą mierzy bowiem odległości w abstrakcyjnej przestrzeni matematycznej  \( (V\times Q)'   \) (matematycy powiedzą że przestrzeń ta jest przestrzenią dualną do przestrzeni funkcji testujących). Żeby rozwiązać ten problem, wykonujemy trick matematyczny polegający na rzutowaniu normy z przestrzeni  \( (V\times Q)'   \) do przestrzeni testującej  \(   V\times Q \). Wprowadza się operator przerzucający przestrzeń   \(  (V\times Q)  \) w przestrzeń  \(  (V\times Q) '   \). Operator ten nazywamy operatorem Riesza  \(  R_{(V,Q)} \colon (V\times Q) \ni  (v,q) \rightarrow ( ( v,q),.) \in (V\times Q)'   \). Operator odwrotny natomiast przerzuca przestrzeń   \(  (V\times Q)'  \) z powrotem w przestrzeń  \(  V\times Q  \), czyli  \(  R_{(V,Q)}^{-1} \colon V' \ni (v,.) \rightarrow v \in V   \). Tak więc jeśli użyjemy odwrotnego operatora Riesza, przerzucimy nasz problem do przestrzeni funkcji testujących  \(  V\times Q  \). Mamy więc  \(  ({\bf u_h},p_h) = \displaystyle{argmin}_{({\bf w_h },s_h) \in (U_h\times P_h)} {1 \over 2} \| {R_{(V,Q)}^{-1}} (B({\bf w_h},s_h) - l) \|_{V\times P }^2   \). Szukamy więc punktu w którym funkcja osiąga minimum. Taki punkt osiągany jest wtedy kiedy pochodne tej funkcji są równe zero. Ponieważ mamy tutaj funkcjonał, matematycy powiedzą że szukamy takiego  \(  ({\bf u_h },p_h)   \) w którym pochodne Gateaux są równe zero we wszystkich kierunkach  \(   \langle R_{(V,Q) }^{-1} (B({\bf u_h },p_h) - l), R_{(V,Q)}^{-1 }(B,({\bf w_h },s_h) ) \rangle_V = 0 \quad \forall ({\bf w_h },s_h) \in U_h \times P_h  \) gdzie  \(  ({\bf w_h},s_h)   \) oznacza wszystkie możliwe "kierunki" ze skończenie wymiarowej przestrzeni funkcji  \(  U_h \times P_h  \) (w której przy okazji szukamy rozwiązania naszego problemu Stokesa). Formalnie rzecz ujmując, w tym miejscu musimy skonstruować skończenie wymiarową przestrzeń funkcji  \(  U_h\times P_h   \) w której będziemy szukać naszego rozwiązania. Robimy to oczywiście w naszym podręczniku stosując funkcje bazowe B-spline, rozpinając je zgodnie z wektorami węzłów wzdłuż osi x oraz wzdłuż osi y. Wzdłuż osi  \(  x  \) wprowadzamy wektor węzłów [0 0 0 1 2 3 4 4 4], podobnie wzdłuż osi  \(  y  \)  wprowadzamy wektor węzłów [0 0 0 1 2 3 4 4 4]. Uzyskaliśmy dwie bazy jednowymiarowych funkcji B-spline  \(  \{ B_{i,2}(x) \}_{i=1,...,6 }  \) oraz  \(  \{B_{j,2}(y)\}_{j=1,...,6 }  \). Następnie utworzymy z nich iloczyny tensorowe  \(  B_{i,j;2}(x,y)=B_{i,2}(x)B_{j,2}(y),i,j=1,...,6  \). Zauważmy że nasz obszar  \(  \Omega  \) rozpięty jest na kwadracie  \(  [0,1]^2  \), podobnie jak nasze 6*6=36 funkcji bazowych co wynika z definicji wektora węzłów [0 0 0 1 2 2 3 4 4 4]. Nasza przestrzeń aproksymacyjna jest więc rozpięta przez iloczyny tensorowe naszych jednowymiarowych funkcji B-spline. Teraz mamy natomiast dwie współrzędne prędkości oraz jedną wartość skalarną ciśnienia, dostajemy więc  \(  U_h \times Q_h = gen \{ B_{i,j;2}(x,y)=B_{i,2}(x)B_{j,2}(y),i,j=\\=1,...,6, B_{i,j;2}(x,y)=B_{i,2}(x)B_{j,2}(y),i,j=1,...,6, B_{i,j;2}(x,y)=B_{i,2}(x)B_{j,2}(y),i,j=1,...,6 \}  \). Oznaczamy  \(  r=R_{(V,Q)}^{-1}(B({\bf u_h },p_h)-l)   \) i wówczas nasz problem minimalizacji reziduum zapisać można w postaci  \(  \langle r , R_{(V,Q)}^{-1} (B\, ({\bf w_h },s_h) ) \rangle = 0 \quad \forall \, (w_h,s_h ) \in U_h\times P_h   \) co jest równoznaczne  \(  \langle B({\bf w_h },s_h), r  \rangle = 0 \quad \quad \forall (w_h,s_h ) \in U_h \times P_h.   \) Dorzucamy teraz drugie równanie, korzystając z definicji reziduum  \(  r=B({\bf u_h },p_h)-l   \), przemnażam definicje reziduum przez funkcje testujące z przestrzeni funkcji testujących i dostaje równanie:  \(  (r,v)_V=\langle B({\bf u_h },p_h)-l,(v,q) \rangle \quad \forall( v,q )\in V\times Q.   \) Mam więc układ równań: znajdź reziduum w nieskończenie wymiarowej przestrzeni funkcji testujących \(  r\in V\times Q   \) oraz rozwiązania w skończenie wymiarowej przestrzeni funkcji  \(  {\bf u_h },p_h) \in U_h\times P_h   \)  \(  (r,v)_V - \langle B({\bf u_h },p_h)-l ,(v,q) \rangle = 0 \quad  \forall (v,q)  \in V\times Q \\ \langle B({\bf w_h },s_h),r\rangle = 0 \quad  \forall (w_h,s_h) \in U_h\times P_h    \) Gdybyśmy byli wstanie rozwiązać to równanie, to dostali byśmy najlepszą możliwą aproksymacje naszego problemu w przestrzei funkcji aproksymujących  \(   U_h\times P_h  \). Niestety nie jest to możliwe, ponieważ nieskończona przestrzeń funkcji testujących generuje nam nieskończenie wiele równań które należało by rozwiązać. Musimy więc wybrać drugą inną bazę do przybliżenia przestrzeni testowej. Tutaj pojawia się pierwsza istotna różnica metody minimalizacji reziduum z tradycyjną metodą elementów skończonych. Otóz w metodzie minimalizacji reziduum mamy dwie różne przestrzenie, jedną do aproksymacji rozwiązania, drugą do testowania. Oczywiście przestrzeń testującą również rozpinamy za pomocą funkcji B-spline. Na przykład możemy użyć takiego samego patcha elementów jak dla przestrzeni aproksymującej (ale nie jest to wymagane!), ale podnieść stopień funkcji B-spline. Precyzujemy jak wyglądać będzie nasz patch elementów oraz nasze funkcje bazowe B-spline rozpięte na elementach, podając wektor węzłów wzdluż osi  \(  x  \) oraz wektor węzłów wzdłuż osi  \(  y  \). Odsyłamy tutaj do rozdziału trzeciego w którym opisany jest sposób definiowania funkcji bazowych za pomocą wektorów węzłów i wielomianów B-spline. Wzdłuż osi  \(  x  \) wprowadzamy wektor węzłów [0 0 0 1 2 3 4 4 4], podobnie wzdłuż osi  \(  y  \)  wprowadzamy wektor węzłów [0 0 0 1 2 3 4 4 4]. Używamy więc do aproksymacji przestrzeni kwadratowych B-spline'ów o ciągłości C1. Do testowania zwiększymy ich stopień, oraz pozostawimy ciągłość C1. Wzdłuż osi  \(  x  \) wprowadzamy wektor węzłów [0 0 0 0 1 1 2 2 3 3 4 4 4 4], podobnie wzdłuż osi  \(  y  \)  wprowadzamy wektor węzłów [0 0 0 0 1 1 2 2 3 3 4 4 4 4]. Generalnie przestrzenie testujące muszą być większe niż przestrzeń aproksymacyjna. Możemy uzyskać ten efekt zwiększając liczbę elementów lub podnosząc stopień funkcji B-spline, lub redukując ciągłość bazy funkcji B-spline, lub mieszając te trzy podejścia razem. Przestrzeń aproksymacyjna powinna być podprzestrzenią przestrzeni testującej. Uzyskaliśmy dwie bazy jednowymiarowych funkcji B-spline stosowanych do testowania  \(  \{ \tilde{B} _{k,3}(x) \}_{k=1,...,10 }  \) oraz  \(  \{\tilde{B}_{l,3}(y) \}_{l=1,...,7 }  \). Następnie utworzymy z nich iloczyny tensorowe  \(  \tilde{B}_{k,l;3}(x,y)={\tilde{B}}_{k,3}(x){\tilde{B}}_{l,3}(y),k,l=1,...,10  \). Nasza przestrzeń testujące, uwzględniająca dwie współrzędne funkcji testującej dla pola prędkości oraz jedną współrzedną funkcji testującej dla ciśnienia, to  \(  V_h\times Q_h = gen \{ \tilde{B}_{k,3}(x)\tilde{B}_{l,3}(y),i,j=1,...,10; \tilde{B}_{k,3}(x)\tilde{B}_{l,3}(y),i,j=1,...,10; \tilde{B}_{k,3}(x)B_{l,3}(y),i,j=1,...,10 \}  \). Mając dyskretną przestrzeń testującą, uzyskujemy wreszcie nasz dyskretny (czyli skończenie wymiarowy) układ równań który będziemy chcieli rozwiązać, Szukamy  \(   ( r_m, ({\bf u_h },p_h) )_{ V_m \times U_h \times P_h }  \)  \(  (r_m,({\bf v_m }, q_h))_{V_m \times Q_h } - \langle B ({\bf u_h },p_h)-l,({\bf v_m }, q_m) \rangle = 0 \quad  \forall ({\bf v_m },q_m) \in V_m \times Q_m \\ \langle B({\bf w_h }, q_h),r_m\rangle = 0 \quad \forall ({\bf w_h },q_h) \in U_h\times P_h   \) Pojawia się tutaj tak zwany iloczyn skalarny (wewnętrzny) w dyskretnej przestrzeni testującej  \(  (*,*)_{V_m \times Q_M }   \). Żeby odgadnąć jaki iloczyn skalarny użyć w naszym problemie, musimy popatrzeć na nasze oryginalne równanie zapisane w formie słabej  \(  B( ({\bf u}p),({\bf v}, q))  = \left( \begin{matrix} a({\bf u}, {\bf v}) + b(p, {\bf v}) \\ b(q, {\bf u } ) \\ \end{matrix} \right)  \) i zobaczyć w jakiej formie są funkcje testujące  \(  ({\bf v },q)   \) w jej składowych  \(  a({\bf u}, {\bf v }) =\int_{\Omega} \frac{\partial u_1}{\partial x}\frac{\partial v_1}{\partial x}dxdy + \int_{\Omega} \frac{\partial u_1}{\partial y}\frac{\partial v_1}{\partial y}dxdy + \int_{\Omega} \frac{\partial u_2}{\partial x}\frac{\partial v_2}{\partial x}dxdy + \int_{\Omega} \frac{\partial u_2}{\partial y}\frac{\partial v_2}{\partial y }dxdy \)  \(  b(p, {\bf v }) = \int_{\Omega}p  \frac{\partial v_1}{\partial x}  dxdy+ \int_{\Omega} p \frac{\partial v_2}{\partial y }  dxdy   \) Nasz produkt wewnętrzny powinien więc zawierać pochodne funkcji  \(  v_1,v_2   \) oraz wartości funkcji  \(  q  \).  \(  ( ({\bf u },p), ({\bf v },q))_{V_m\times Q_m} =\\= \int_{\Omega} p(x,y) q(x,y) dxdy + \int_{\Omega} \frac{\partial u_1(x,y)}{\partial x }  \frac{\partial v_1(x,y) }{\partial x }dxdy  + \int_{\Omega} \frac{\partial u_1(x,y)}{\partial y }  \frac{\partial v_1(x,y)}{\partial y }  + \int_{\Omega} \frac{\partial u_2(x,y)}{\partial x }  \frac{\partial v_2(x,y)}{\partial x }dxdy + \int_{\Omega} \frac{\partial u_2(x,y)}{\partial y }  \frac{\partial v_2(x,y)}{\partial y }dxdy \) Podsumowując, w metodzie minimalizacji reziduum, musimy zdefiniować osobną przestrzeń aproksymacyjną, oraz osobną (większą) przestrzeń testującą, oraz wybrać iloczyn wewnętrzny przestrzeni testującej. Dostajemy wówczas układ równań w którym niewiadome to nasze rozwiązanie  \(  ({\bf u },p)   \) oraz dodatkowo reziduum  \(   r  \). To jak dobrze działać będzie metoda minimalizacja reziduum zależy w dużej mierze od naszego wyboru przestrzeni testującej oraz iloczynu wewnętrznego. Jeśli iloczyn wewnętrzny będzie dostatecznie dokładny, oraz przestrzeń testująca będzie dostatecznie duża, wszystko będzie idealnie działać, i dostaniemy najlepsze możliwe do uzyskania rozwiązanie w przestrzeni aproksymacyjnej  \(  U_h   \) (ale nie lepsze niż pozwala na to przestrzeń aproksymacyjna). Matematyczne uzasadnienie jest takie, że problem minimalizacji reziduum z nieskończenie wymiarową przestrzenią testową realizuje warunek stabilności inf-sup  ze stałą równą 1. Jeśli przestrzeń testującą zawęzimy do przestrzeni skończenie wymiarowej, to warunek inf-sup może nie być już idealnie realizowany w tej przestrzeni. Zwiększanie przestrzeni testującej będzie nam poprawiać realizowalność warunku stabilności inf-sup. Nasz układ równań do rozwiązania metodzie minimalizacji reziduum wyglada następująco  \( \left(  \begin{matrix} G & B \\ B^T & 0 \\ \end{matrix} \right) \left( \begin{matrix} r \\ u \end{matrix} \right)=  \left( \begin{matrix} f \\ 0 \end{matrix} \right)  \) Zobaczmy jak wygląda nasz układ równań na przykładzie konkretnych przestrzeni aproksymacyjnej i testującej.


Stabilizacja równań Stokesa za za pomocą metody Discontinuous Galerkin (DG)

Jedną z możliwych metod stabilizacji równań Stokesa jest metoda DG (Discontinuous Galerkin). W metodzie tej funkcje bazowe używane do aproksymacji i testowania rozwiązania "rozcinane" są nożyczkami na granicy poszczególnych elementów. Tak więc jeśli chcemy używać wielomianów B-spline, możemy je dalej używać, ale nad każdym elementem zostaną one rozcięte na lokalne kawałki. W rozdziale tym opieramy się na sformułowaniu DG równań Stokesa na podstawie książki [1]  Używamy sformułowania wyprowadzonego w poprzednim rozdziale  \(   a({\bf u},{\bf v}) + b(p,{\bf v }) = f({\bf v})  \quad \forall {\bf v}  \)  \(  b(q,{\bf u } ) = 0 \quad \forall q   \)  \(  a({\bf u}, {\bf v}) =\int_{\Omega} \nabla {\bf u} : \nabla {\bf v }dxdy = \int_{\Omega} \frac{\partial u_1}{\partial x}\frac{\partial v_1}{\partial x}dxdy + \int_{\Omega} \frac{\partial u_1}{\partial y}\frac{\partial v_1}{\partial y}dxdy + \int_{\Omega} \frac{\partial u_2}{\partial x}\frac{\partial v_2}{\partial x}dxdy + \int_{\Omega} \frac{\partial u_2}{\partial y }\frac{\partial v_2}{\partial y }dxdy \)  \(  b(p, {\bf v}) =  \int_{\Omega}  p div {\bf v}  dxdy= \int_{\Omega}p  \frac{\partial v_1}{\partial x}  dxdy+ \int_{\Omega} p \frac{\partial v_2}{\partial y }  dxdy   \)  \(  f({\bf v} )= \int_{\Omega} \frac{\partial h(x,y)}{\partial x}\frac{\partial v_1(x,y)}{\partial x } dxdy +\int_{\Omega} \frac{\partial h(x,y)}{\partial y}\frac{\partial v_2(x,y)}{\partial y} dxdy +\int_{\partial \Omega} \frac{\partial h(x,y)}{\partial n } v_1(x,y) dS   \) W przypadku sformułowania dla metody DG konieczne jest wprowadzenie dodatkowych członów opisujących skoki funkcji bazowych  \(  [\psi(x)]=\psi|_{T_1}(x)-\psi|_{T_2}(x)   \) gdzie  \(  T_1   \) i  \(  T_2   \) oznacza dwa sąsiednie elementy, i obliczamy tutaj różnce z wartości "pociętej" funkcji B-spline oznaczonej  \(  \psi  \) na elemencie z jednej strony i z drugiej strony. Dodatkowo wprowadza się średnią z wartości rozciętych funkcji bazowych na krawędziach dwóch sąsiednich elementów  \(  \{\psi\} = \frac{\psi|_{T_1}(x)+\psi|_{T_2}(x)}{2}  \). Nasz funkcjonał  \(  a({\bf u },{\bf v })  \) należy zmodyfikować dla metody DG w następujący sposób  \(  a_{DG}({\bf u }, {\bf v }) = \sum_{K \in \Omega_h } \int_K  \left( \frac{\partial u_1 }{\partial x } \frac{\partial v_1}{\partial x } +  \frac{\partial u_1 }{\partial y } \frac{\partial v_1 }{\partial y } + \frac{\partial u_2 }{\partial x } \frac{\partial v_1}{\partial x } +  \frac{\partial u_2 }{\partial y } \frac{\partial v_1 }{\partial y } \right)     \)  \(  + \sum_{F\in F_h} \int_F \frac{\eta}{h_F} \left( [\frac{\partial u_1}{\partial x} ] [\frac{\partial v_1}{\partial x}]  + [\frac{\partial u_1}{\partial y}] [\frac{\partial v_1}{\partial y }]  + [\frac{\partial u_2}{\partial x} ] [\frac{\partial v_2}{\partial x}]  + [\frac{\partial u_2}{\partial y}] [\frac{\partial v_2}{\partial y }]\right)     \)  \( -\sum_{F\in F_h} \int_F \left( \{\frac{\partial u_1}{\partial n_1} \}  [\frac{\partial v_1}{\partial x}] + \{ \frac{\partial u_1}{\partial n_2} \}  [\frac{\partial v_1}{\partial y } ]  + \{\frac{\partial u_2}{\partial n_1} \}  [\frac{\partial v_2}{\partial x}] + \{ \frac{\partial u_2}{\partial n_2} \}  [\frac{\partial v_2}{\partial y } ]\right)     \)  \( - \sum_{F\in F_h} \int_F \left( [\frac{\partial u_1}{\partial x} ]  \{ \frac{\partial v_1}{\partial n_1} \}  + [\frac{\partial u_1}{\partial y} ]  \{\frac{\partial v_1}{\partial n_2 } \} + [\frac{\partial u_2}{\partial x} ]  \{ \frac{\partial v_2}{\partial n_1} \}  + [\frac{\partial u_2}{\partial y} ]  \{\frac{\partial v_2}{\partial n_2 } \} \right)    \) W powyższych wzorach, całki liczone są element po elemencie. Tak więc  \(  \Omega_h  \) oznacza zbiór wszystkich elementów z patcha na którym rozpięte są nasze funkcje B-spline,  \(  K \in \Omega_h  \) oznacza pojedynczy element wybrany z rodziny elementów (tak więc sumujemy nasze całki elementowe po wszystkich elementach z patcha),  \(  F_h  \) oznacza zbiór wszystkich krawędzi pomiędzy elementami patcha,  \(  F\in F_h  \) oznacza pojedynczą krawędź wybraną z tej rodziny (tak więc sumujemy nasze całki krawędziowe po wszystkich krawędziach z patcha),  \(  h_F  \) oznacza dugość krawędzi  \(  F  \), natomiast  \(  \eta  \) oznacza parametr którego wartość należy dobrać eksperymentalnie. Niestety wadą tej metody stabilizacji jest fakt, iż musimy uruchomić kilka obliczeń i znaleźć taką wartość parametru  \(  \eta  \) dla której symulacja da poprawne wyniki. Zazwyczaj eksperymentuje się z wielkościami różniącymi się o rząd wielkości np. 0.1, 1, 10, 100, 1000, 10000 etc. Symbol  \(  {\bf n }=(n_1,n_2)   \) oznacza wersor prostopadły do krawędzi  \(  F  \). W naszym przypadku kiedy rozważamy funkcje bazowe B-spline rozpięte na regularnym patchu elementów w 2D, przyjmujemy  \(  {\bf n }=(1,0)  \) lub  \(  {\bf n }=(0,1)   \). Z kolei nasz funkcjonał  \(  b(p ,{\bf v })  \) należy zmodyfikować dla metody DG w następujący sposób  \(  b_{DG}(p, {\bf v})= - \sum_{K\in \Omega_h } \int_K \left ( p \frac{\partial v_1}{\partial x}+ p \frac{\partial v_2}{\partial y } \right)    \)  \( + \sum_{F \in F_h } \int_F\left(  [v_1] n_1 p+ [v_2] n_2 p \right)  \) Znaczenie symboli w całkach jest podobne jak w przypadku funkcjonału  \(   a({\bf u },{\bf v })  \). Nasz stabilizowany problem Stokes'a wygląda teraz następująco:  \(   a_{DG}({\bf u},{\bf v}) + b_{DG}(p,{\bf v }) = f({\bf v })  \quad \forall {\bf v}  \)  \(  b_{DG}(q,{\bf u } ) + {\color{red} {S_{DG}(p,q) }}= 0 \quad \forall q   \) Zgodnie z książką Daniele Antoni Di Pietro, Alexandre Ern, Mathematical Aspects of Discontinuous Galerkin Methods, w celu ustabilizowania równań Stokesa należy dodać tutaj człon  \(   {\color{red}{S_{DG}(p,q)}} = \sum_{F\in F_h^{internal} } h_F \int_F [p][q] \) w której   \(  F_h^{internal}  \) oznacza wszystkie krawędzie wewnętrzne, tak więc sumujemy tutaj całki policzone po wszystkich krawędziach znajdujących się wewnątrz patcha elementów (omijamy krawędzie położone na brzegu). Podobnie jak poprzednio,  \(  h_F  \) oznacza długość krawędzi. W celu rozwiązania równań Stokesa stabilizowanych za pomocą metody DG, generujemy w podobny sposób układ równań, uwzględniając powyższe modyfikacje. Możliwe jest również rozważanie różnych modyfikacji. Założymy następujące fakty o funkcjach aproksymujących i testujących. Załóżmy że funkcje testujące będą kawałkami ciągłe, w sensie że nad poszczególnymi przedziałami wektora węzłów w analizie izogeometrycznej, lub nad poszczególnymi elementami skończonymi w sensie klasycznej metody elementów skończonych będą ciągłe, ale pomiędzy elemetami lub pomiędzy przedziałami wektora węzłów będą nieciągłe. Mamy więc "połamane" wielomiany testujące. Załóżmy też że  funkcje aprokszmaczjne są ciągłe. Wówczas wszystkie człony zawierające skoki i średnie z funkcji aproksymujące znikną, w szczególności dostaniemy zmodyfikowane wyrażenia  \(  a_{DG}({\bf u }, {\bf v }) = \sum_{K \in \Omega_h } \int_K  \left( \frac{\partial u_1 }{\partial x } \frac{\partial v_1}{\partial x } +  \frac{\partial u_1 }{\partial y } \frac{\partial v_1 }{\partial y } + \frac{\partial u_2 }{\partial x } \frac{\partial v_1}{\partial x } +  \frac{\partial u_2 }{\partial y } \frac{\partial v_1 }{\partial y } \right)     \)  \( -\sum_{F\in F_h} \int_F \left( \{\frac{\partial u_1}{\partial n_1} \}  [\frac{\partial v_1}{\partial x}] + \{ \frac{\partial u_1}{\partial n_2} \}  [\frac{\partial v_1}{\partial y } ]  + \{\frac{\partial u_2}{\partial n_1} \}  [\frac{\partial v_2}{\partial x}] + \{ \frac{\partial u_2}{\partial n_2} \}  [\frac{\partial v_2}{\partial y } ]\right)     \)  \(  b_{DG}(p, {\bf v})= - \sum_{K\in \Omega_h } \int_K \left ( p \frac{\partial v_1}{\partial x}+ p \frac{\partial v_2}{\partial y } \right)    \)  \( + \sum_{F \in F_h } \int_F\left(  [v_1] n_1 p+ [v_2] n_2 p \right)  \)  \(   {\color{red}{S_{DG}(p,q)}} = 0 \) Założymy następujące fakty o funkcjach aproksymujących i testujących. Załóżmy że funkcje aproksymujące będą kawałkami ciągłe, w sensie że nad poszczególnymi przedziałami wektora węzłów w analizie izogeometrycznej, lub nad poszczególnymi elementami skończonymi w sensie klasycznej metody elementów skończonych będą ciągłe, ale pomiędzy elemetami lub pomiędzy przedziałami wektora węzłów będą nieciągłe. Mamy więc "połamane" wielomiany aproksymacyjne. Załóżmy też że  funkcje testujące są ciągłe. Wówczas mamy  \(  a_{DG}({\bf u }, {\bf v }) = \sum_{K \in \Omega_h } \int_K  \left( \frac{\partial u_1 }{\partial x } \frac{\partial v_1}{\partial x } +  \frac{\partial u_1 }{\partial y } \frac{\partial v_1 }{\partial y } + \frac{\partial u_2 }{\partial x } \frac{\partial v_1}{\partial x } +  \frac{\partial u_2 }{\partial y } \frac{\partial v_1 }{\partial y } \right)     \)  \( - \sum_{F\in F_h} \int_F \left( [\frac{\partial u_1}{\partial x} ]  \{ \frac{\partial v_1}{\partial n_1} \}  + [\frac{\partial u_1}{\partial y} ]  \{\frac{\partial v_1}{\partial n_2 } \} + [\frac{\partial u_2}{\partial x} ]  \{ \frac{\partial v_2}{\partial n_1} \}  + [\frac{\partial u_2}{\partial y} ]  \{\frac{\partial v_2}{\partial n_2 } \} \right)    \)  \(  b_{DG}(p, {\bf v})= - \sum_{K\in \Omega_h } \int_K \left ( p \frac{\partial v_1}{\partial x}+ p \frac{\partial v_2}{\partial y } \right)    \)  \(   {\color{red}{S_{DG}(p,q)}} = 0 \) Jeśli do sformułowania DG wsadzimy ciągłe przestrzenie aproksymacyjne i testujące, dostaniemy wówczas takie samo oryginalne sformułowanie Stokesa bez stabilizacji. Różne warianty przestrzeni aproksymacyjnych i testujących mogą być pomocne podczas testowania różnych form stabilizacji oraz do samego debugowania kodu generującego macierz metody DG.


Implementacja w MATLABie problemu adwekcji-dyfuzji ze stabilizacją metodą SUPG

Poniżej przedstawiam kod MATLABa obliczający problem adwekcji-dyfuzji metodą elementów skończonych ze stabilizacją metodą Streamline Upwind Petrov-Galerkin (SUPG), dla modelowego problemu Erikksona-Johnsona. Przypomnijmy najpierw problem adwekcji-dyfuzji, problem modelowy Erikksona-Johnsona (opisany na przykład w dokumencie [1]), zdefiniowany na obszarze kwadratowym  \(  \Omega = [0,1]^2  \) w następujący sposób: Szukamy funkcji  \(  u  \) takiej że  \(  a(u,v)=l(v) \forall v  \) gdzie  \(  a(u,v) =\int_{\Omega} \beta_x(x,y) \frac{\partial u(x,y) }{\partial x } dxdy + \int_{\Omega} \beta_y(x,y) \frac{\partial u(x,y)}{\partial y } dxdy  \\ +\epsilon \int_{\Omega} \frac{\partial u(x,y) }{\partial x}  \frac{\partial v(x,y)}{\partial x } dxdy +\epsilon \int_{\Omega} \frac{\partial u(x,y)}{\partial y }  \frac{\partial v(x,y)}{\partial y } dxdy  \)  \(  l(v) = \int_{\partial \Omega } f(x,y) v dxdy  \)  \(  f(x,y)=sin(\pi y)(1-x)  \) jest rozszerzeniem warunku brzegowego Dirichleta na cały obszar, natomiast  \(  \beta = (1,0)  \) reprezentuje wiatr wiejący z lewej strony na prawą, natomiast  \(  \epsilon = 10^{-2} \) oznacza współczynnik dyfuzji. Wielkość  \( Pe=1/ \epsilon = 100 \) nazywa sie liczbą Pekleta, i definiuje ona wrażliwość problemu adwekcji-dyfuzji.

Kod można uruchomić w darmowym środowisku Octave.

W linii 848 podawany jest współczynnik dyfuzji  \(  \epsilon = 10 ^{-2} \). Jego odwrotnośc to tak zwana wartość Pekleta (Peclet number)  \(  Pe=\frac{1}{\epsilon}  \). W liniach 849 i 850 zaszyte są stałe metody SUPG. Następnie tworzony jest wektor węzłów (zawsze liczby naturalne liczone od zera)  \(  knot\_x = [0 \quad 0 \quad 0 \quad 1 \quad 2 \quad 2 \quad 2]  \) dla przestrzeni aproksymacyjnej, oraz wektor odpowiadających im punktów wzdłuź osi x, z przedziału od 0 do 1,  \(  points\_x = [0 \quad 0.5 \quad 1]  \) w których umieszczana będzie baza funkcji B-spline służaca do aproksymacji rozwiązania problemu Erikksona-Johnsona. Wektory węzłów dla przestrzeni aproksymującej i punkty na których rozpinamy wektory węzłów, definiujemy osobno dla osi x oraz dla osi y. Kod może zostać uruchomiony w darmowym środowisku Octave. Kod uuchamia się otwierając go w Octave oraz wpisując komendę  \( advection\_supg\_adapt \) Po chwili obliczeń kod otwiera dodatkowe okienko i rysuje w nim rozwiązanie numeryczne oraz dokładne. Kod oblicza błąd reziduum  \( Residual norm: L2 0.015808, H1 0.100417 \) iKod oblicza również normę L2 i H1 z rozwiązania  \( Error: L2 31.41 procent, H1 38.74 procent \) i porównuje do normy L2 i H1 z rozwiazania dokładnego.  \( Best possible: L2 2.06 procent, H1 28.73 procent \) Widać że w celu poprawienia dokładności rozwiązania, konieczne jest zagęszczanie siatki.


Implementacja w MATLABie problemu adwekcji-dyfuzji za stabilizacją metodą minimalizacji reziduum

Poniżej przedstawiam kod MATLABa obliczający problem adwekcji-dyfuzji metodą elementów skończonych ze stabilizacją metodą minimalizacji reziduum, dla modelowego problemu Erikksona-Johnsona. Przypomnijmy najpierw problem adwekcji-dyfuzji, problem modelowy Erikksona-Johnsona (opisany na przykład w dokumencie [1] zdefiniowany na obszarze kwadratowym  \(  \Omega = [0,1]^2  \) w następujący sposób: Szukamy funkcji  \(  u  \) takiej że  \(  a(u,v)=l(v) \forall v  \) gdzie  \(  a(u,v) =\int_{\Omega} \beta_x(x,y) \frac{\partial u(x,y) }{\partial x } dxdy + \int_{\Omega} \beta_y(x,y) \frac{\partial u(x,y)}{\partial y } dxdy  \\ +\epsilon \int_{\Omega} \frac{\partial u(x,y) }{\partial x}  \frac{\partial v(x,y)}{\partial x } dxdy +\epsilon \int_{\Omega} \frac{\partial u(x,y)}{\partial y }  \frac{\partial v(x,y)}{\partial y } dxdy  \)  \(  l(v) = \int_{\partial \Omega } f(x,y) v dxdy  \)  \(  f(x,y)=sin(\pi y)(1-x)  \)jest rozszerzeniem warunku brzegowego Dirichleta na cały obszar, natomiast  \(  \beta = (1,0)  \) reprezentuje wiatr wiejący z lewej strony na prawą, natomiast  \(  \epsilon = 10^{-2} \) oznacza współczynnik dyfuzji. Wielkość  \( Pe=1/ \epsilon = 100 \) nazywa sie liczbą Pekleta, i definiuje ona wrażliwość problemu adwekcji-dyfuzji. Kod można uruchomić w darmowym środowisku Octave.

W linii 830 podawany jest współczynnik dyfuzji  \(  \epsilon = 10 ^{-2} \). Jego odwrotnośc to tak zwana wartość Pecleta  \(  Pe=\frac{1}{\epsilon}  \). Następnie tworzony jest wektor węzłów (zawsze liczby naturalne liczone od zera)  \(  knot\_trial\_x = [0 \quad 0 \quad 0 \quad 1 \quad 2 \quad 2 \quad 2]  \) dla przestrzeni aproksymacyjnej, oraz wektor odpowiadających im punktów wzdłuź osi x, z przedziału od 0 do 1,  \(  points\_x = [0 \quad 0.5 \quad 1]  \) w których umieszczana będzie baza funkcji B-spline służaca do aproksymacji rozwiązania problemu Erikksona-Johnsona. Podobnie definiujemy wektor węzłów dla przestrzeni testującej  \(  knot\_test\_x = [0 \quad 0 \quad 0 \quad 0 \quad 1 \quad 2 \quad 2 \quad 2 \quad 2]  \). Zgodnie z ideą metody minimalizacji reziduum, funkcji testujących musi być więcej niż funkcji aproksymujących. W naszym przykładzie używamy funkcji B-spline stopnia kwadratowego o ciągłości C1 dla przestrzeni aproksymacyjnej (trial) oraz funkcji B-spline stopnia trzeciego o ciągłości C1 dla przestrzeni testującej (test). Wektory węzłów dla przestrzeni aproksymującej oraz testującej, i punkty na których rozpinamy wektory węzłów, definiujemy osobno dla osi x oraz dla osi y. Kod może zostać uruchomiony w darmowym środowisku Octave. Kod uuchamia się otwierając go w Octave oraz wpisując komendę  \( advection\_igrm\_adapt \) Po chwili obliczeń kod otwiera dodatkowe okienko i rysuje w nim rozwiązanie numeryczne oraz dokładne. W drugim oknie kod rysuje reziduum, obliczane dodatkowo przez metodę minimalizacji reziduum, Jest ono miarą lokalnego błędu rozwiązania. Kod oblicza błąd reziduum  \( Residual norm: L2 0.015808, H1 0.100417 \) iKod oblicza również normę L2 i H1 z rozwiązania  \( Error: L2 45.90 procent, H1 61.85 procent \) i porównuje do normy L2 i H1 z rozwiazania dokładnego.  \( Best possible: L2 2.02 procent, H1 14.20 procent \) Widać że w celu poprawienia dokładności rozwiązania, konieczne jest zwiększenie siatki.

Autorzy kodów w MATLABie: Marcin Łoś i Maciej Woźniak.


Rozdział 6. Przetwarzanie siatek obliczeniowych

Generacja i adaptacja siatek obliczeniowych

Celem obliczeń za pomocą metody elementów skończonych jest przeprowadzenie symulacji wybranego zjawiska fizycznego na zadanym obszarze. Ponadto, zadanie projekcji, takie jak to przedstawione w modułach wprowadzających w podręczniku, ma zastosowania w grafice komputerowej.

Każda z tych symulacji wymaga wygenerowania siatki obliczeniowej pokrywającej określony obszar elementami skończonymi. Klasyczne generatory siatek obliczeniowych generują najczęściej siatki zbudowane z elementów trójkątnych w dwóch wymiarach, oraz siatki zbudowane z elementów czworościennych w trzech wymiarach. Stosuje się również siatki zbudowane z elementów prostokątnych w dwóch wymiarach oraz sześciennych w trzech wymiarach, oraz dodatkowo elementy pryzmatyczne i piramidy. Teoretycznie możliwe jest również tworzenie elementów skończonych zbudowanych z dowolnych wieloboków w dwóch wymiarach lub wielościanów w trzech wymiarach. Siatki obliczeniowe zbudowane z dowolnych elementów powinny posiadać określone właściwości.

Jedną z często używanych metod generacji siatki obliczeniowej zbudowanej z elementów trójkątnych jest triangulacja Delauneya. Bazując na idei Delauneya aktualnie wciąż rozwija się całą szkołę generacji i adaptacji siatek obliczeniowych [1] [2]. Siatka obliczeniowa zbudowana za pomocą triangulacji Delauneya zbudowana jest z elementrów trójkątnych w dwóch wymiarach. Na każdym elemencie trójkątnym możemy rozpiąć okrąg (ponieważ możliwe jest rozpięcie okręgu na dowolyuch trzech punktach. Triangulacja Delauneya w dwóch wymiarach posiada następującą własność, okręgi opisane na trójątach triangulacji Delauneya nie zawierają punktów wierzchołków innych trójkątów.

Możliwe jest również skonstruowanie triangulacji Delauneya w trzech wymiarach. Wówczas siatka obliczeniowa zbudowana jest z elementów czworościennych. Na wierzchołkach elementu czworościennego możemy rozpiąć kulę (do rozpięcia kuli potrzebujemy bowiem czterech punktów). Triangulacja Delauneya w trzech wymiarach posiada z koleji następującą własność, kule opisane na czworościanach Delauneya nie będą zawierały punktów wierzchołków innych czworościanów.


Algorytm adaptacji siatek trójkątnych i czworościennych

W przypadku siatek trójkątnych lub czworościennych możliwe jest zagęszczanie siatek w celu zwiększenia dokładności aproksymacji funkcji rozpiętych na tych siatkach. Algorytm zagęszczania siatek nazywa się algorytmem adaptacji. Musi on zostać skonstruowany w taki sposób, żeby nie zepsuć właściwości oryginalnej siatki, na przykład żeby elementy nie stawały się coraz bardziej "wydłużone" w wyniku adaptacji, co powodowało by zaburzenie proporcji między krawędziami siatki i możliwość wygenerowania długich cienkich elementów dla których policzenie całki dawało by bardzo małe liczby, rzędu 0.000001 lub mniej co z koleji powodowało by błedy numeryczne faktoryzacji układu równań metody elementów skończonych. Algorytm adaptacji siatek trójkątnych zachowujący proporcje elementów zaproponowany został przez Marie Rivare [1]. Jest on opisany w pseudokodzie, oraz zilustrowany na rysunku


Algorytm h-adaptacji

Celem algorytmu h adaptacji jest zwiększenie dokładności aproksymacji na siatkach obliczeniowych poprzez połamanie wybranych elementów na mniejsze. Algorytm h adaptacji możliwy jest do zaimplementowania dopiero wtedy kiedy skonstruujemy takie funkcje bazowe, które będzie można rozpinać na elementach skończonych różnego rozmiaru. W przypadku siatek niestrukturalnych, zbudowanych z elementów trójkątnych, możliwe jest zastosowanie algorytmu Rivary opisanego w innym rozdziale. W przypadku siatek strukturalnych, zbudowanych z elementów prostokątnych, na których rozpięto funkcje bazowe zgodnie z zadanymi wektorami węzłów, h adaptacja jest możliwa wtedy jeśli pomiędzy wszystkimi łamanymi elementami wsadzimy  \( C^0 \) separatory. W rozdziale "Aproksymacja z wiszącymi węzłami" opisany został sposób łączenia ze sobą funkcji bazowych rozpiętych na sąsiadujących połamanych elementach różnego rozmiaru. Algorytm h-adaptacji może zostać zaimplementowany w formie algorytmu a priori lub a posteriori, oraz stosując adaptacje izotropowe lub anizotropowe. Algorytm adaptacyjny może używać jednej siatki obliczeniowej, tak zwanej siatki rzadkiej, lub dwóch siatek obliczeniowych rzadkiej i gęstej. W naszej klasyfikacji przez algorytm adaptacyjny a priori, rozumiemy algorytm który podejmuje decyzję przed skonstruowaniem i rozwiązaniem problemu na siatce gęstej, po rozwiązaniu problemu na aktualnej siatce rzadkiej. Z tego względu określenie "a priori" niejako "z góry" rozumiemy tutaj bez dodatkowych obliczeń związanych na przykład z rozwiązywaniem problemu na dodatkowej siatce gęstej. Przez algorytm adaptacyjny a posteriori rozumiemy algorytm który podejmuje decyzje o adaptacje po wykonaniu pewnych dodatkowych obliczeń (nie tylko rozwiązaniu problemu na siatce rzadkiej) na przykład po wygenerowaniu siatki gęstej i rozwiązywaniu problemu na siatce gęstej.

Jak powiedział kiedyś prof. Zienkiewicz, jeden z twórców metody elementów skończonych, wystarczającą dokładność w wielu zagadnieniach inżynierskich dostaniemy stosując wielomiany kwadratowe i h-adaptację. Zakładamy więc że siatka początkowa to posklejane grupy elementów opisanych wektorami węzłów [0 0 0 1 1 1] i [0 0 0  1 1 1]. Algorytm h-adaptacji anioztropowej a priori w ogólnej postaci zapisać można w sposób następujący:

Poprzez błąd w przykładowym zadaniu projekcji bitampy, rozumiemy tutaj błąd w normie  \( H^1 \) reprezentujący różnicę pomiędzy aproksymacją ciągłą np. odlewu terenu którego wysokość w różnych punktacj opisana jest za pomocą odcieni pikseli bitmapy. Jeśli konieczne jest połamanie danego elementu (wykonanie h-adaptacji dla tego elementu), mamy do wyboru trzy scenariusze. Pierwsza możliwość to połamanie elementu na cztery mniejsze elementy. Druga możliwość to połamanie elementu na dwa nowe elementy w kierunku osi  \( x \). Trzecia możliwość to połamanie danego elementu na dwa mniejsze elementy  w kierunku osi  \( y \). W algorytmie adaptacyjnym zazwyczaj nie wykonuje się adaptacji na wszystkich elementach siatki, ale jedynie na tych elementach na których błąd jest większy niż 33 procent błedu maksymalnego (liczonego po wszystkich elementach). Jest to oczywiście arbitrarnie przyjęta wartość, dobrana na podstawie doświadczeń numerycznych przez prof. Leszka Demkowicza. W zaproponowanej wersji algorytmu próbujemy najpierw wykonać adaptacje w obydwu kierunkach (połamać element w obydwu kierunkach), a następnie (jeśli nie jest to wskazane) próbujemy połamać element w jednym z dwóch kierunków. Jest to oczywiście pzykładowa wersja algorytmu, możliwe są różne modyfikacje. Najbardziej zaawansowaną wersją algorytmu adaptacyjnego jest algorytm hp adaptacji opisany w innym module. Generowanie nowych funkcji bazowych polega na generowaniu nowych lokalnych wektorów węzłów dla połamanych elementów. Na przykład, jeśli mamy element opisany wektorem węzłów [0 0 0 1 1 1] w kierunku osi  \( x \) oraz wektorem węzłów [0 0 0 1 1 1] w kierunku osi  \( y \), i połamiemy ten element na cztery nowe elementy, wówczas mamy wektor węzłów [0 0 0 0.5 0.5 1 1 1] w kierunku osi  \( x \) oraz wektor węzłów [0 0 0 0.5 0.5 1 1 1] w kierunku osi  \( y \). Jeśli połamiemy ten element na dwa nowe elementy w kierunku osi  \( x \), wówczas mamy wektor węzłów [0 0 0 0.5 0.5 1 1 1] w kierunku osi  \( x \) oraz wektor węzłów [0 0 0 0 1 1 1] w kierunku osi  \( y \). Jeśli połamiemy ten element na dwa nowe elementy w kierunku osi  \( y \), wówczas mamy wektor węzłów [0 0 0 1 1 1] w kierunku osi  \( x \) oraz wektor węzłów [0 0 0 0.5 0.5 1 1 1] w kierunku osi  \( y \). Scalając ze sobą wiele patchów (grup elementów) na których rozpięte są różne wielomiany B-spline, uzyskamy siatkę obliczeniową na której możliwe będą lokalne modyfikacje stopnia funkcji B-spline, na przykład stosując opisany algorytm p-adaptacji. Sposób scalania funkcji bazowych uzyskanych na dużych i małych połamanych elementach został opisany w rozdziale "Aproksymacja z wiszącymi węzłami". Algorytm automatycznej h-adaptacji aposteriori:

Mamy więc trzy możliwości adaptacji pojedynczego elementu:

W jaki sposób podejmowana jest decyzja o wyborze rodzaju adaptacji pojedynczego elementu? Decyzja podejmowana jest zgodnie z następującym algorytmem.

Innymi słowy, wybieramy taki rodzaj adaptacji dla elementu, który pozwala uzyskać największy spadek błędu najmniejszym kosztem. Wielkość ta reprezentowana jest przez prędkość spadku błędu, która rośnie wraz ze spadkiem błędu, ale maleje wraz z poniesionym kosztem (z liczbą funkcji dodaną do elemetu, ponieważ koszt obliczenia na danym elemencie zależy od liczby niewiadomych, które są współczynnikami funkcji bazowych).