Wygenerowanie układu równań linowych za pomocą obliczeń analitycznych

Następny etap to wygenerowanie układu równań liniowych oraz jego rozwiązanie. Oczywiście zależy nam na tym żeby wybrane algorytmy generacji oraz rozwiązywania układów równań były najszybsze możliwe. W pierwszej kolejności generujemy macierz zawierającą całki z iloczynów dwuwymiarowych funkcji B-spline. Możemy dla uproszczenia przyjąć, że wektory węzłów są równoodległe, oraz, że jednostką odległości wzdłuż danej osi układu współrzędnych jest odległość pomiędzy dwoma węzłami w wektorze węzłów. Zauważmy najpierw, że  \( \int B^x_{i,p}B^y_{j,p}B^x_{k,p}B^y_{l,p}dxdy = \int B^x_{i,p}B^x_{k,p}dx \int B^y_{j,p}B^y_{l,p}dy \) Innymi słowy dzięki temu, że nasze dwuwymiarowe B-spline'y powstają poprzez przemnożenie jednowymiarowych B-spline'ów, możemy rozdzielić nasze dwuwymiarowe całki na iloczyn dwóch jednowymiarowych całek, w których przemnażamy i całkujemy pary jednowymiarowych B-spline'ów. Musimy więc wygenerować następującą macierz

Zauważmy, że macierz ta ma następującą strukturę. W wierszach zmieniają się pierwsze B-spline'y w obu całkach pojedynczych (całce po $x$ i całce po $y$). Wiersze zmieniają się tak, że najpierw mamy kolejno B-spline'y  \( B^x_{1,p},B^x_{2,p},...,B^x_{N_x,p} \) dla ustalonego  \( B^y_{1,p} \). Następnie powtarzamy wszystkie  \( B^x_{1,p},B^x_{2,p},...,B^x_{N_x,p} \) dla ustalonego  \( B^y_{2,p} \), i tak dalej, występują bloki wszystkich kolejnych B-spline'ów względem  \( x \) dla kolejnych ustalonych B-spline'ów  \( B^y_{j,p} \) aż do ostatniego bloku, w którym występują kolejno  \( B^x_{1,p},B^x_{2,p},...,B^x_{N_x,p} \) dla ustalonego ostatniego  \( B^y_{N_y,p} \) W kolumnach sytuacja jest podobna, dotyczy ona natomiast drugich B-spline'ów w każdej całce pojedynczej. Kolumny zmieniają się więc tak, że najpierw mamy kolejno B-spline'y  \( B^y_{1,p},B^y_{2,p},...,B^y_{N_y,p} \) dla ustalonego  \( B^x_{1,p} \). Następnie powtarzamy wszystkie  \( B^y_{1,p},B^y_{2,p},...,B^y_{N_y,p} \) dla ustalonego  \( B^x_{2,p} \), i tak dalej, występują bloki wszystkich kolejnych B-spline'ów względem  \( y \) dla kolejnych ustalonych B-spline'ów  \( B^x_{i,p} \) aż do ostatniego bloku, w którym występują kolejno  \( B^y_{1,p},B^y_{2,p},...,B^y_{N_y,p} \) dla ustalonego ostatniego  \( B^x_{N_x,p} \) Macierz, która ma taką strukturę jest iloczynem Kroneckera dwóch mniejszych macierzy, jednej, w której występują same wyrazy z pierwszych całek pojedynczych (pary B-spline'ów względem  \( x \)), i drugiej, w której występują same wyrazy z drugich całek pojedynczych (pary B-spline'ów względem  \( y \)). Matematyczny zapis takiego iloczynu Kroneckera to symbol  \( \otimes \) pomiędzy dwoma wspomnianymi macierzami. Symbol "=" z przodu oznacza iż iloczyn Kroneckea tych macierzy daje naszą dużą macierz.  \( 	=\begin{bmatrix}    \int{B^x_{1,p}B^x_{1,p}}dx  & \int{B^x_{1,p}B^x_{2,p}}dx  & \cdots &  \int{B^x_{1,p}B^x_{N_x,p}}dx \\   \int{B^x_{2,p}B^x_{1,p}}dx &  \int{B^x_{2,p}B^x_{2,p}}dx & \cdots &  \int{B^x_{2,p}B^x_{N_x,p}}dx\\       \vdots & \vdots & \vdots &  \vdots \\ \int{B^x_{N_x,p}B^x_{1,p}}dx  & \int{B^x_{N_x,p}B^x_{2,p}}dx  & \cdots &  \int{B^x_{N_x,p}B^x_{N_x,p}}dx \\   \end{bmatrix} \otimes \\ \begin{bmatrix} \int{B^y_{1,p}B^y_{1,p}}dy &  \int{B^y_{1,p}B^y_{1,p}}dy & \cdots & \int{B^y_{1,p}B^y_{N_y,p}}dy\\    \int{B^y_{1,p}B^y_{1,p}}dy &  \int{B^y_{1,p}B^y_{1,p}}dy & \cdots & \int{B^y_{1,p}B^y_{N_y,p}}dy \\        \vdots & \vdots & \vdots &  \vdots \\  \int{B^y_{N_y,p}B^y_{1,p}}dy &  \int{B^y_{N_y,p}B^y_{1,p}}dy & \cdots & \int{B^y_{N_y,p}B^y_{N_y,p}}dy \\  \end{bmatrix}  \) Zapis  \( \otimes \) oznacza, iż przemnażamy przez siebie odpowiednie wyrazy dwóch macierzy tak żeby dostać oryginalną macierz ( 1 ).

Musimy teraz obliczyć całki z iloczynów par jednowymiarowych B-spline'ów, przedstawionych na Rys. 1. Zauważmy najpierw, że jeśli przemnożymy przez siebie dwa jednowymiarowe B-spline'y, których wykresy nie nachodzą na siebie (matematycy powiedzą "które nie mają wspólnego suportu"), na przykład  \( N_{1;2} \) oraz  \( B_{4;2} \) z Rys. 1, wówczas całka  \( \int_{[t_0,t_6]} B_{1;2}(x)B_{4;2}(x)dx = 0 \) Dzieje się tak dlatego ponieważ na każdym przedziale przedstawionym na Rys. 1 jeden z B-spline'ów jest równy zeru, czyli  \( \int_{[t_0,t_6]} B_{1;2}(x)B_{4;2}(x)dx = \\ \int_{[t_0,t_1]} B_{1;2}(x)B_{4;2}(x)dx + \int_{[t_1,t_2]} B_{1;2}(x)B_{4;2}(x)dx + \int_{[t_2,t_3]} B_{1;2}(x)B_{4;2}(x)dx +\\ \int_{[t_3,t_4]} B_{1;2}(x)B_{4;2}(x)dx + \int_{[t_4,t_5]} B_{1;2}(x)B_{4;2}(x)dx + \int_{[t_5,t_6]} B_{1;2}(x)B_{4;2}(x)dx =\\ \int_{[t_0,t_1]} B_{1;2}(x)*0dx + \int_{[t_1,t_2]} B_{1;2}(x)*0dx + \int_{[t_2,t_3]} B_{1;2}(x)*0dx + \\ \int_{[t_3,t_4]} 0*B_{4;2}(x)dx + \int_{[t_4,t_5]} 0*B_{4;2}(x)dx + \int_{[t_5,t_6]} 0*B_{4;2}(x)dx = \\ 0+0+0+0+0+0=0   \) Następnie, możemy przyjąć jako jednostkę długości średnicę elementu, wówczas każdy przedział  \( [t_i,t_{i+1}] \) będzie miał średnicę równą jeden. Następnie musimy wprowadzić wzory na poszczególne segmenty B-spline'a (przedstawione na prawym panelu Rys. 1 ). Segmenty te oznaczamy  \( B_1,B_2,B_3 \).  \( B_1 (x)=\frac{1}{2} x^2  \\ B_2 (x)=-x^2+x+\frac{1}{2} \\ B_3 (x)=\frac{1}{2}(1-x)^2  \) Ponieważ jedynie całki z nachodzących na siebie B-spline'ów dają niezerowe wartości, nad każdym przedziałem  \( [t_i,t_{i+1}] \) musimy tak naprawdę obliczyć następujące całki, występujące w macierzy elementowej, w których nachodzą na siebie poszczególne segmenty B-spline'ów. Ponieważ podane wzory na segmenty obowiązują nad przedziałem 0,1, a całka nie zmienia się podczas przesunięcia (podobnie jak objętość pod stołem nie zmieni się jeśli przesuniemy stół na inne miejsce na płaskiej podłodze), możemy dla uproszczenia policzyć nasze całki właśnie w przedziale 0,1.  \(  \begin{bmatrix} \int_0^1 B_1(x)B_1(x)dx  & \int_0^1 B_1(x)B_2(x)dx  & \int_0^1 B_1(x)B_3(x)dx \\ \int_0^1 B_2(x)B_1(x)dx  & \int_0^1 B_2(x)B_2(x)dx & \int_0^1 B_2(x)B_3(x)dx   \\ \int_0^1 B_3(x)B_1(x)dx  & \int_0^1 B_3(x)B_2(x)dx  & \int_0^1 B_3(x)B_3(x)dx   \\ \end{bmatrix}  \) Innymi słowy mamy tylko 9 możliwości nachodzenia na siebie poszczególnych segmentów. Ponieważ segmenty  \( B_1 \) i  \( B_3  \) są symetryczne, dodatkowo mamy symetrię macierzy, czyli tak naprawdę musimy policzyć sześć całek  \(  \begin{bmatrix} \int_0^1 B_1(x)B_1(x)dx  & \int_0^1 B_1(x)B_2(x)dx  & \int_0^1 B_1(x)B_3(x)dx  \\ == & \int_0^1 B_2(x)B_2(x)dx  & \int_0^1 B_2(x)B_3(x)dx  \\ ==  & == & \int_0^1 B_3(x)B_3(x)dx \\  \end{bmatrix}  \) Z symetrii segmentów wynika również iż segment  \( B_3 \) ma kształt odwróconego segmentu  \( B_1 \), i dlatego  \( \int_0^1 B_1(x)B_1(x)dx  =\int_0^1 B_3(x)B_3(x)dx  \), innymi słowy odwrócenie funkcji nie zmienia wartości całki (podobnie jak przekręcenie stołu o 180 stopni nie zmienia objętości znajdującej się pod stołem), oraz  \( \int_0^1 B_2(x)B_3(x)dx = \int_0^1 B_1(x)B_2(x)dx  \), tak naprawdę musimy więc policzyć jedynie cztery całki  \( 	\begin{bmatrix} \int_0^1 B_1(x)B_1(x)dx  & \int_0^1 B_1(x)B_2(x)dx  & \int_0^1 B_1(x)B_3(x)dx   \\ == & \int_0^1 B_2(x)B_2(x)dx  & ==   \\ ==  & == & ==   \\ \end{bmatrix}  \) Obliczamy jednowymiarowe całki z wielomianów  \( \int_0^1{B_1(x)B_1(x)dx} = \int_0^1 (\frac{1}{2} x^2)(\frac{1}{2} x^2)dx = \int_0^1 \frac{1}{4}x^4)dx = \frac{1}{4} (\frac{x^5}{5})|^1_0=\frac{1}{20}  \)  \( \int_0^1{B_2(x)B_2(x)dx} = \int_0^1 (-x^2+x+\frac{1}{2})(-x^2+x+\frac{1}{2}) dx= \int_0^1 (x^4-2x^3+x+\frac{1}{4}) dx = \\ (\frac{x^5}{5})|^1_0-2(\frac{x^4}{4})^1_0+(\frac{x^2}{2})^1_0+(\frac{1}{4}x)|^1_0=\frac{1}{5}+\frac{1}{4}=\frac{9}{20} \)  \( \int_0^1{B_1(x)B_2(x)dx} = \int_0^1 (\frac{1}{2} x^2)(-x^2+x+\frac{1}{2})dx = \int_0^1 (-\frac{x^4}{2}+\frac{x^3}{2}+\frac{x^2}{4})dx = \\ (-\frac{x^5}{10})|^1_0+(\frac{x^4}{8})|^1_0 +(\frac{x^3}{12})|^1_0=-\frac{1}{10}+\frac{1}{8}+\frac{1}{12}=\frac{13}{120}  \)  \( \int_0^1{B_1(x)B_3(x)dx} = \int_0^1 (\frac{1}{2} x^2)(\frac{1}{2}(1-x)^2)dx = \int_0^1 (\frac{x^4}{4}-\frac{x^3}{2}+\frac{x^2}{4})dx = \\ (\frac{x^5}{20})|^1_0 -(\frac{x^4}{8})|^1_0 +(\frac{x^3}{12})|^1_0=\frac{1}{20}-\frac{1}{8}+\frac{1}{12}=\frac{1}{120}  \) Uwzględniając symetrie obliczonych całek, wpisujemy je w stosowne miejsca w naszej matrycy i dostajemy  \( 	\begin{bmatrix} \int_0^1{B_1(x)B_1(x)dx}  & \int_0^1{B_1(x)B_2(x)dx } & \int_0^1{B_1(x)B_3(x)dx}   \\ \int_0^1{B_2(x)B_1(x)dx}  & \int_0^1{B_2(x)B_2(x)dx } & \int_0^1{B_2(x)B_3(x)dx }  \\ \int_0^1{B_3(x)B_1(x)dx}  & \int_0^1{B_3(x)B_2(x)dx}  & \int_0^1{B_3(x)B_3(x)dx }  \\ \end{bmatrix} = \begin{bmatrix} \frac{1}{20} & \frac{13}{120} & \frac{1}{120} \\ \frac{13}{120} & \frac{9}{20} & \frac{13}{120} \\ \frac{1}{120} & \frac{13}{120} & \frac{1}{20} \\ \end{bmatrix}  \) Sumując teraz wygenerowane w ten sposób kawałki macierzy dostaniemy dwie macierze tworzące nasz układ równań w postaci produktu Kroneckera macierzy. Sumowanie przeprowadzamy w następujący sposób:  \( 	\begin{bmatrix} \frac{1}{20} & \frac{13}{120} & \frac{1}{120} & \cdots \\ \frac{13}{120} & \frac{9}{20} +\color{red}{\frac{1}{20}}& \frac{13}{120} + \color{red}{\frac{13}{120}} & \color{red}{\frac{1}{120}} & \cdots \\ \frac{1}{120} & \frac{13}{120} +\color{red}{\frac{13}{120}} & \frac{1}{20} +\color{red}{\frac{9}{20}} +\color{blue}{\frac{1}{20}} &  \color{red}{\frac{13}{120}} +\color{blue}{\frac{13}{120}}  & \color{blue}{\frac{1}{120}} & \cdots \\ \cdots & \color{red}{\frac{1}{120}} & \color{red}{\frac{13}{120}} +\color{blue}{\frac{13}{120}}  & \color{red}{\frac{1}{20}}+\color{blue}{\frac{9}{20}}  +\color{green}{\frac{1}{20}} & \color{blue}{\frac{13}{120}} +\color{green}{\frac{13}{120}} & \color{green}{\frac{1}{120}} & \cdots \\ \vdots & \vdots & \vdots &  \vdots &     \vdots & \vdots & \vdots \\ & \cdots & \color{green}{\frac{1}{120}} & \color{blue}{\frac{13}{120}} +\color{green}{\frac{13}{120}} & \color{red}{\frac{1}{20}}+\color{blue}{\frac{9}{20}} +\color{green}{\frac{1}{20} } & \color{red}{\frac{13}{120}}+\color{blue}{\frac{13}{120}}  & \color{red}{\frac{1}{120}} \\ & & \cdots &	\color{blue}{\frac{1}{120}}  & \color{red}{\frac{13}{120}}+\color{blue}{\frac{13}{120}} & \frac{1}{20} +\color{red}{\frac{9}{20}} +\color{blue}{\frac{1}{20}} & \frac{13}{120} +\color{red}{\frac{13}{120}} & \frac{1}{120} \\ & & & \cdots &  \color{red}{\frac{1}{120}} & \frac{13}{120} +\color{red}{\frac{13}{120}} & \frac{9}{20} +\color{red}{\frac{1}{20}} & \frac{13}{120}  \\ & & & & \cdots &  \frac{1}{120} & \frac{13}{120} & \frac{1}{20}  \\ \end{bmatrix}  \) Poza wypisanymi wyrazami macierzy umieszczonymi na pięciu przekątnych, pozostałe wyrazy macierzy są równe 0. Innymi słowy naszą matrycę przesuwamy wzdłuż przekątnej od lewego górnego rogu macierzy do prawego dolnego rogu, i sumujemy nakładające się na siebie wyrazy. W ten sposób uzyskamy dwie macierze, które tworzą za pomocą tak zwanego produktu Kroneckera  naszą macierz ( 1 ). Kolejny problem do rozwiązania to policzenie całek prawej strony. Całka prawej strony to próbkowanie bitmapy przemnożonej przez poszczególne funkcje testujące (B-spline'y używane do uśredniania bitmapy w obszarze na którym są określone). Używamy od teraz nowej notacji, w której wprowadzamy dwuwymiarową funkcję B-spline, funkcję zmiennych   \( (x,y) \), będącą iloczynem dwóch jednowymiarowych funkcji B-spline  \( B_{k,l;2}(x,y)=B^x_{k;2}(x)B^y_{l;2}(y) \). Zauważmy że przemnożenie bitmapy przez B-spline'a  \( v(x,y)=B_{k,l;2}(x,y) \) oznacza iż całkę tą musimy policzyć jedynie po dziewięciu elementach na których określony jest B-spline  \( B_{k,l;2}(x,y) \). Musimy rozwiązać teraz dwa problemy. Pierwszy problem to jaki wzór ma dziewięć fragmentów naszego B-spline'a na tych dziewięciu elementach, na których jest on określony. Drugi problem, to jak policzyć całkę z bitmapy przemnożonej przez wielomian. Zauważmy że jednowymiarowe B-spline'y składają się z trzech segmentów opisanych zgodnie z ( 1 ). W związku z tym, nasz dwuwymiarowy B-spline testujący na dziewięciu elementach na których jest określony składa się z odpowiednich iloczynów tych segmentów.  \(  B_1(x)B_1(y), B_2(x)B_1(y), B_3(x)B_1(y) \\B_1(x)B_2(y), B_2(x)B_2(y), B_3(x)B_2(y) \\  B_1(x)B_3(y), B_2(x)B_3(y), B_3(x)B_3(y)  \). Jest to zilustrowane na Rys. 2.

Tak więc problem liczenia całki bitmapy przemnożonej przez funkcję testującą B-spline sprowadza się do problemu policzenia dziewięciu całek. Na przykład, jeśli chcemy przeliczyć całkę dla B-spline'a testującego  \( B_{k,l;2}(x,y)  \), wówczas musimy policzyć następujące dziewięć całek:  \( \int^1_0 \int^1_0 B_1(x)B_1(y) BITMAP((k-1+x)*maxx/N_x,(l-1+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_2(x)B_1(y) BITMAP((k+x)*maxx/N_x,(l-1+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_3(x)B_1(y) BITMAP((k+1+x)*maxx/N_x,(l-1+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_1(x)B_2(y) BITMAP((k-1+x)*maxx/N_x,(l+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_2(x)B_2(y) BITMAP((k+x)*maxx/N_x,(l+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_3(x)B_2(y) BITMAP((k+1+x)*maxx/N_x,(l+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_1(x)B_3(y)BITMAP((k-1+x)*maxx/N_x,(l+1+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_2(x)B_3(y)BITMAP((k+x)*maxx/N_x,(l+1+y)*maxy/N_y) dxdy, \\ \int^1_0 \int^1_0 B_3(x)B_3(y)BITMAP((k+1+x)*maxx/N_x,(l+1+y)*maxy/N_y) dxdy.  \) W powyższych wzorach wyrażenia typu  \(  (k-1+x)*maxx/N_x \) oznaczają przeliczanie współrzędnych B-spline'a, którego całkujemy na indeksy pikseli bitmapy, na których on się rozpościera. Zmienne x i y zmieniają się od 0 do 1. W obrębie jednego elementu, mamy  \( maxx/N_x \) pikseli w kierunku  \( x \) oraz  \( maxy/N_y \) pikseli w kierunku  \( y \) , gdzie  \( maxx,maxy \)  oznacza rozmiar bitmapy (całkowitą liczbę pikseli w kierunku  \( x \) i  \( y \)) natomiast  \( N_x,N_y \) oznacza liczbę elementów siatki w kierunku  \( x \) i  \( y \). Tak więc  \( x*maxx/N_x \) zmienia się od 0 do  \( maxx/N_x \), czyli obejmuje liczbę pikseli bitmapy w kierunku  \( x \) na pojedyńczym elemencie, natomiast  \( y*maxy/N_y \) zmienia się od 0 do liczby pikseli bitmapy w kierunku  \( y \)  na pojedynczym elemencie. Dodatkowo przesuwamy nasz zakres tak żeby przeglądać piksele odpowiednich elementów, na których rozpięty jest nasz B-spline testujący. Na przykład wyrażenie  \( (k-1+x)*maxx/N_x \) oznacza że przesuwamy się po pikselach elementu numer  \( k \) w kierunku  \( x \),  \( (k+x)*maxx/N_x \) oznacza, że przesuwamy się po pikselach elementu numer  \( k+1 \) w kierunku  \( x \),  \( (k+1+x)*maxx/N_x \) oznacza, że przesuwamy się po pikselach elementu numer  \( k+2 \) w kierunku  \( x \). Podobnie  \( (l-1+y)*maxy/N_y \) oznacza, że przesuwamy się po pikselach elementu numer  \( l \) w kierunku  \( y \),  \( (l+y)*maxy/N_y \) oznacza, że przesuwamy się po pikselach elementu numer  \( l+1 \) w kierunku  \( y \), oraz  \( (l+1+y)*maxy/N_y \) oznacza, że przesuwamy się po pikselach elementu numer  \( +2l \) w kierunku  \( y \). Jak więc policzyć takie całki po pikselach? Musimy rozbić całkę na sumę całek po poszczególnych pikselach, na przykład dla pierwszej całki  \( \int^1_0 \int^1_0 B_1(x)B_1(y) BITMAP((k-1+x)*maxx/N_x,(l-1+y)*maxy/N_y) dxdy = \\ \color{red}{\sum_{i=1,maxx/N_x;j=1,maxy/N_y}}BITMAP((k-1)*maxx/N_x+i,(l-1)*maxy/N_y+j) \\ \color{blue}{\int_{(i-1,i)*\frac{1}{maxx/N_x}}} B_1(x)dx \color{blue}{ \int_{(j-1,j)*\frac{1}{maxy/N_y}}} B_1(y)dy  \) gdzie suma zaznaczona na czerwono rozpościera się po wszystkich pikselach z pojedynczego elementu (jest ich  \( maxx/N_x * maxy/N_y \)), bitmapa jest próbkowana w punktach pikseli rozpostartych na danym elemencie, a całki zaznaczone na niebiesko, liczone są z funkcji B-spline po pojedynczych pikselach. Wystarczy teraz, że policzymy pojedyncze całki z trzech segmentów  \(  \int B_1(x) dx = \int \frac{1}{2} x^2 dx = \frac{1}{2}\frac{x^3}{3}, \\ \int B_2(x) dx = \int  (-x^2+x+\frac{1}{2}) dx = -\frac{x^3}{3}+\frac{x^2}{2}+\frac{1}{2}x, \\ \int B_3(x) dx = \int \frac{1}{2}(1-x)^2 dx = \int(1-2x+x^2)=x-x^2+\frac{x^3}{3}  \) i wstawimy je do wzorów na całki, na przykład  \( \color{blue}{ \int_{(i-1,i)*\frac{1}{maxx/N_x}} B_1(x)dx} =  \left(\frac{1}{2}\frac{x^3}{3}\right)|^{ \frac{i}{maxx/N_x} }_{\frac{i-1}{maxx/N_x} }  =  \frac{1}{2} \left( \frac{ \left( \frac{i-1}{maxx/N_x} \right)^3- \left( \frac{i}{maxx/N_x}\right)^3 }{3} \right) \\  \color{blue}{\int_{(j-1,j)*\frac{1}{maxy/N_y}}B_1(y)dy } =    \left( \frac{1}{2}\frac{x^3}{3} \right)|^{ \frac{j }{ maxy/N_y} }_{ \frac{j-1}{maxy/N_y} }=  \frac{1}{2} \left( \frac{ \left( \frac{j-1} { maxy/N_y } \right)^3-\left(\frac{j}{maxy /N_y }\right)^3}{3}\right) \) Liczenie całek funkcji testujących z bitmapy zrobiło się trochę skomplikowane. Podsumujmy, musimy policzyć wiele całek z bitmapy, przemnożone przez różne dwuwymiarowe B-spliny, tak zwane funkcje testujące. Każdą z tych całek liczymy tak że rozbijamy ją na całkę po dziewięciu elementach na których rozpięty jest B-spline testujący, i sumujemy te wynikowe dziewięć całek, wpisując otrzymaną liczbę do jednego wiersza wektora prawej strony.  Z kolei żeby policzyć całkę po każdym z tych dziewięciu elementów na których rozpięty jest nasz B-spline testujący, całki te rozbijamy na poszczególne piksele które leża na tym elemencie, próbkujemy naszą bitmapę po tych pikselach i liczymy całkę z naszych segmentów B-spline'ów po wszystkich pikselach. Wszystko to razem sumujemy (suma po dziewięciu elementach na których leży B-spline testujący, suma po pikselach elementów).


Klasyczna i izogeometryczna metoda elementów skończonych

Rozdział 1. Wstęp do metody elementów skończonych

Rozwiązanie układu równań linowych


Interpretacja rozwiązania

W wyniku rozwiązania układu równań liniowych otrzymujemy wektor współczynników  \(    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} \\   \) Pamiętamy teraz że nasze rozwiązanie (projekcja bitmapy) reprezentowana jest przez kombinacje liniową dwuwymiarowych funkcji B-spline  \(   u(x,y) = \sum_{i=1}^{N_x} \sum_{j=1}^{N_y} u_{i,j} B^x_{i}(x)  B^y_{j}(y)   \) W celu uzyskania wartości rozwiązania w punkcje  \(  (x,y)  \) należącym do obszaru  \(  \Omega  \), na którym zdefiniowana jest nasza bitmapa, nie musimy oczywiście sumować wartości wszystkich funkcji B-spline. Musimy zlokalizować element w którym leży punkt  \(  (x,y)  \) oraz wszystkie  \(  (p+1)^2  \) funkcje B-spline określone na tym elemencie. Pozostałe funkcje B-spline są równe zero w tym miejscu. W naszym przypadku,  \(  \Omega = [1,maxx]\times[1,maxy]  \), oraz mamy równo rozłożone  \(  N_x  \) funkcji B-spline stopnia  \(  p  \) wzdłuż osi  \(  x  \) oraz  \(  N_y  \) funkcji B-spline stopnia  \( p  \) wzdłuż osi \(  y  \). Mamy więc  \( N_x-p  \) elementów wzdłuż osi  \(  x  \) oraz  \(  N_y-p  \) elementów wzdłuż osi  \(  y  \). Każdy element ma rozmiar  \(  [maxx / (N_x-p)]\times [maxy / (N_y-p)]  \). Żeby dostać więc numer elementu wzdłuż osi  \(  x  \), wykonujemy dzielenie  \(  ne_x = int (\frac{x} {maxx/(N_x-p) })  \), i analogicznie żeby obliczyć numer elementu wzdluż osi  \(  y  \), wykonujemy dzielenie  \(  ne_z=int(\frac{y}{maxy/(N_y-p) } )  \), gdzie int oznacza wartość całkowitą (zaokrąglenie w dół). Wówczas, na naszym elemencie  \(  [ne_x,ne_y]  \) określone są funkcje  \(  \{B^x_{i,p}(x)B^y_{j,p}\}_{ i=ne_x-p+1,ne_x+p-1,j=ne_y-p+1,ne_y-p-1 }  \). W celu obliczenia wartości rozwiązania w punkcie  \(  (x,y)  \) wystarczy więc, że obliczymy  \(  u(x,y) = \sum_{ i=ne_x-p+1,ne_x+p-1,j=ne_y-p+1,ne_y-p-1 } u_{i,j} B^x_{i}(x)  B^y_{j}(y)   \)


Implementacja w MATLABie problemu projekcji bitmapy

W module tym przedstawiamy dwa kody MATLABa obliczające izogeometryczną L2 projekcję bitmapy. Kody używają izogeometrycznej metody elementów skończonych, w której to bitmapa aproksymowana jest za pomocą kombinacji liniowej wielomianów B-spline. 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ą procedurę  \( bitmap\_param(filename,nx,ny,px,py) \). Kod po zbudowaniu układu równań i rozwiązaniu go rysuje projekcje bitmapy w otwartym okienku. Pierwszy kod oblicza izogeometryczną L2 projekcję bitmapy, generuje wartości funkcji B-spline zgodnie z rekurencyjnej definicji. Podczas całkowania, oblicza on kwadratury Gaussa na  \( (Ne_x+px)(Ne_y+py) \) elementach, próbkując wartości funkcji B-spline. Woła on funkcje rekurencyjne wiele razy dla wielu punktów kwadratury Gaussa. Przez to jest on bardzo wolny. Nawet dla 4*4=16 elementów działa ona stosunkowo długo, a zwiększenie liczby elementów zwiększa czas działania programu tak, jak funkcja kwadratowa liczby elementów. Dlatego zaproponowaliśmy drugą procedurę  \( bitmap\_fast(filename,nx,ny,px,py) \), w której w tablicy umieszczono przeliczone wcześniej wartości funkcji B-spline, w kierunku osi poziomej i pionowej. Zamiast liczyć je na  \( (Ne_x+px)(Ne_y+py) \) elementach, czytamy ich wartości z tablicy. Operacja ta przyspiesza działanie kodu o 2 rzędy wielkości. Żeby bardziej przyspieszyć kod konieczne jest zastąpienie MATLABowej procedury backslash (\ stosowanej do rozwiązania wygenerowanego układu równań) solwerem zmienno-kierunkowym.


Rozdział 2. Dyskretyzacja za pomocą różnych funkcji bazowych

Liniowe funkcje bazowe

Zarówno liniowe funkcje bazowe jak i funkcje bazowe wyższego rzędu opisać można w nowoczesny sposób za pomocą konwencji przyjętej w izogeometrycznej metodzie elementów skończonych, odwołującej się do funkcji B-spline oraz notacji tak zwanego wektora węzłów (po angielsku knot-vector). Rozważmy jednowymiarowy obszar  \( [0,N] \)  podzielony na  \( N \)  przedziałów zwanych elementami skończonymi. Przyjmijmy dla uproszczenia naszej prezentacji że mamy pięć takich przedziałów, czyli  \( N=5 \). Wprowadzamy notacje wektora węzłów, który stanowi sekwencję nie malejących współrzędnych punktów. Z reguły dla uproszczenia przyjmuje się punkty o współrzednych będących liczbami całkowitymi. Na przykład wektor węzłów  \( [0 \quad 0 \quad 1 \quad 2 \quad 3 \quad 4 \quad 5 \quad 5] \) definiuje nam liniowe funkcje bazowe na przedziale  \( [0,5] \) podzielonym na pięć elementów  \( [0,1], [1,2], [2,3], [3,4] i [4,5] \). W celu zilustrowania funkcji bazowych wynikających z różnych wektorów węzłów polecamy załączony kod MATLABa. Jak interpretować zapis wektora węzłów? W pierwszej kolejności określić musimy stopień i ciągłość funkcji bazowych. Robimy to w sposób następujący. Stopień funkcji bazowych równy jest liczbie powtórzeń pierwszego (lub ostatniego) punktu w wektorze węzłów, minus jeden. Innymi słowy, patrzymy ile razy powtórzony jest pierwszy i ostatni punkt w wektorze węzłów. Jeśli punkt ten powtórzony jest dwa razy (u nas 0 0 oraz 5 5) oznacza to, że stopień funkcji bazowych wynosi jeden (dwa minus jeden), czyli wprowadzamy liniowe funkcje bazowe. Jakie są wzory na liniowe funkcje bazowe określone za pomocą wektora węzłów. Określa to tak zwana reguła Cox-de-Boor'a, której oryginalną formułę przyczatam poniżej [1]:  \(  B_{i,0}(\xi)=1 \textrm{ jeśli } \xi_i \leq \xi \leq \xi_{i+1} \textrm{ lub 0 w pozostałych przypadkach }  \)  \( B_{i,p}(\xi)=\frac{\xi-\xi_i}{\xi_{i+p}-\xi_i}B_{i,p-1}(\xi)+\frac{\xi_{i+p+1}-\xi}{\xi_{i+p+1}-\xi_{i+1}}B_{i+1,p-1}(\xi)  \) Jak należy rozumieć tą regułę? Wektor  \( N \) węzłów koduje nam  \( N+p \) funkcji B-spline, gdzie  \( p \) oznacza stopień funkcji B-spline (w naszym przypadku rozważamy liniowe funkcje B-spline, więc  \( p=1 \)). Funkcje B-spline oznaczane są  \( B_{i,p} \), gdzie  \( i=1,...N+p \) oznacza numer (indeks) funkcji B-spline, natomiast   \( p \) oznacza stopień funkcji B-spline.


Funkcje bazowe wyższego stopnia rzędu Ck w 1D


Wielomiany Lagrange’a

W rozdziale tym wyprowadzimy funkcje bazowe zwane klasycznymi wielomianami Lagrange'a. Wielomiany Lagrange'a pierwszego stopnia są równoważne funkcjom B-spline pierwszego stopnia, mają dokładnie takie same wzory. Wielomiany Lagrange'a drugiego stopnia można wyprowadzić z ogólnego wzoru na B-spline'y stosując wektor węzłów generujący B-spline'y drugiego stopnia, w którym powtórzono wszystkie wewnętrze węzły  \( p \) razy, czyli  \( [0 \quad 0 \quad 0 \quad 1 \quad 1 \quad 2 \quad 2 \quad 3 \quad 3 \quad 4 \quad 4 \quad 5 \quad 5 \quad 5] \) Tak zdefiniowany wektor węzłów wygeneruje nam funkcje bazowe równoważne wielomianom Lagrange'a drugiego stopnia (wielomianom kwadratowym) używanym w tradycyjnej metodzie elementów skończonych. W celu zilustrowania funkcji bazowych wynikających z różnych wektorów węzłów polecamy załączony kod MATLABa. Jak wyglądają takie funkcje bazowe? Musimy wygenerować krok po kroku wszystkie wielomiany zerowego stopnia, oraz wielomiany pierwszego i drugiego stopnia, używając wzoru Cox-de-Boor'a. Zacznijmy od wielomianów zerowego stopnia. Mamy teraz  \( \xi_1=\xi_2=\xi_3=0, \xi_4=\xi_5=1, \xi_6=\xi_7=2,  \xi_8=\xi_9=3, \xi_{10}=\xi_{11}=4 \textrm{ oraz } \xi_{12}=\xi_{13}=\xi_{14}=5 \)  \( B_{1,0}=1\textrm{ dla }x\in[\xi_1,\xi_2=[0,0]=\{0\} \), 0 w pozostałych punktach,  \( B_{2,0}=1\textrm{ dla }x\in[\xi_2,\xi_3]=[0,0]=\{0\} \), 0 w pozostałych punktach,  \( B_{3,0}=1\textrm{ dla }x\in[\xi_3,\xi_4]=[0,1] \), 0 w pozostałych punktach,  \( B_{4,0}=1\textrm{ dla }x\in[\xi_4,\xi_5]=[1,1]=\{1\} \), 0 w pozostałych punktach,  \( B_{5,0}=1\textrm{ dla }x\in[\xi_5,\xi_6]=[1,2] \), 0 w pozostałych punktach,  \( B_{6,0}=1\textrm{ dla }x\in[\xi_6,\xi_7]=[2,2]=\{2\} \), 0 w pozostałych punktach,  \( B_{7,0}=1\textrm{ dla }x\in[\xi_7,\xi_8]=[2,3] \), 0 w pozostałych punktach,  \( B_{8,0}=1\textrm{ dla }x\in[\xi_8,\xi_9]=[3,3]=\{3\} \), 0 w pozostałych punktach,  \( B_{9,0}=1\textrm{ dla }x\in[\xi_{10},\xi_{11}]=[3,4] \), 0 w pozostałych punktach,  \( B_{10,0}=1\textrm{ dla }x\in[\xi_{11},\xi_{12}]=[4,4]=\{4\} \), 0 w pozostałych punktach,  \( B_{11,0}=1\textrm{ dla }x\in[\xi_{12},\xi_{13}]=[4,5] \), 0 w pozostałych punktach,  \( B_{12,0}=1\textrm{ dla }x\in[\xi_{13},\xi_{14}]=[5,5]=\{5\} \), 0 w pozostałych punktach,  \( B_{13,0}=1\textrm{ dla }x\in[\xi_{14},\xi_{15}]=[5,5]=\{5\} \), 0 w pozostałych punktach. Podobnie, musimy wygenerować funkcje bazowe pierwszego stopnia dla nowego wektora węzłów. Przypominamy sobie wzór dla  \( p=1 \)  \( B_{i,1}(\xi)=\frac{\xi-\xi_i}{\xi_{i+1}-\xi_i}B_{i,0}(\xi)+\frac{\xi_{i+2}-\xi}{\xi_{i+2}-\xi_{i+1}}B_{i+1,0}(\xi)  \), do którego wstawiamy kolejne węzły:  \( B_{1,1}(\xi)=\frac{\xi-\xi_1}{\xi_{2}-\xi_1}B_{1,0}(\xi)+\frac{\xi_{3}-\xi}{\xi_{3}-\xi_{2}}B_{2,0}(\xi)={\color{red}{\frac{\xi-0}{0-0}B_{1,0}(\xi)+\frac{0-\xi}{0-0}B_{2,0}(\xi)}} =0  \),  \( B_{2,1}(\xi)=\frac{\xi-\xi_2}{\xi_{3}-\xi_2}B_{2,0}(\xi)+\frac{\xi_{4}-\xi}{\xi_{4}-\xi_{3}}B_{3,0}(\xi)={\color{red}{\frac{\xi-0}{0-0}B_{2,0}(\xi)}}+\frac{1-\xi}{1-0}B_{3,0}(\xi)=1-\xi \textrm{ dla } \xi\in[0,1] \),  \( B_{3,1}(\xi)=\frac{\xi-\xi_3}{\xi_{4}-\xi_3}B_{3,0}(\xi)+\frac{\xi_{5}-\xi}{\xi_{5}-\xi_{4}}B_{4,0}(\xi) = \frac{\xi-0}{1-0}B_{3,0}(\xi)+{\color{red}{\frac{1-\xi}{1-1}B_{4,0}(\xi)}} = \xi \textrm{ dla } \xi\in[0,1] \),  \( B_{4,1}(\xi)=\frac{\xi-\xi_4}{\xi_{5}-\xi_4}B_{4,0}(\xi)+\frac{\xi_{6}-\xi}{\xi_{6}-\xi_{5}}B_{5,0}(\xi) = {\color{red}{\frac{\xi-1}{1-1}B_{4,0}(\xi)}}+\frac{2-\xi}{2-1}B_{5,0}(\xi) =2-\xi \textrm{ dla } \xi\in[1,2] \),  \( B_{5,1}(\xi)=\frac{\xi-\xi_5}{\xi_{6}-\xi_5}B_{5,0}(\xi)+\frac{\xi_{7}-\xi}{\xi_{7}-\xi_{6}}B_{6,0}(\xi) = \frac{\xi-1}{2-1}B_{5,0}(\xi)+{\color{red}{\frac{2-\xi}{2-2}B_{6,0}(\xi)}} =\xi-1 \textrm{ dla } \xi\in[1,2] \),  \( B_{6,1}(\xi)=\frac{\xi-\xi_6}{\xi_{7}-\xi_{6}}B_{6,0}(\xi)+\frac{\xi_{8}-\xi}{\xi_{8}-\xi_{7}}B_{7,0}(\xi) = {\color{red}{\frac{\xi-2}{2-2}B_{6,0}(\xi)}}+\frac{3-\xi}{3-2}B_{7,0}(\xi) =3-\xi \textrm{ dla } \xi\in[2,3] \),  \( B_{7,1}(\xi)=\frac{\xi-\xi_7}{\xi_{8}-\xi_7}B_{7,0}(\xi)+\frac{\xi_{9}-\xi}{\xi_{9}-\xi_{8}}B_{8,0}(\xi) = \frac{\xi-2}{3-2}B_{7,0}(\xi)+{\color{red}{\frac{3-\xi}{3-3}B_{9,0}(\xi)}} =\xi-2 \textrm{ dla } \xi\in[2,3] \),  \( B_{8,1}(\xi)=\frac{\xi-\xi_8}{\xi_{9}-\xi_8}B_{8,0}(\xi)+\frac{\xi_{10}-\xi}{\xi_{10}-\xi_{9}}B_{9,0}(\xi) = {\color{red}{\frac{\xi-3}{3-3}B_{8,0}(\xi)}}+\frac{4-\xi}{4-3}B_{9,0}(\xi) =4-\xi \textrm{ dla } \xi\in[3,4] \),  \( B_{9,1}(\xi)=\frac{\xi-\xi_9}{\xi_{10}-\xi_9}B_{9,0}(\xi)+\frac{\xi_{11}-\xi}{\xi_{11}-\xi_{10}}B_{10,0}(\xi) = \frac{\xi-4}{4-3}B_{9,0}(\xi)+{\color{red}{\frac{4-\xi}{4-4}B_{10,0}(\xi)}} = \xi-3 \textrm{ dla } \xi\in[3,4] \),  \( B_{10,1}(\xi)=\frac{\xi-\xi_{10}}{\xi_{11}-\xi_{10}}B_{10,0}(\xi)+\frac{\xi_{12}-\xi}{\xi_{12}-\xi_{11}}B_{11,0}(\xi) ={\color{red}{\frac{\xi-4}{4-4}B_{10,0}(\xi)}}+\frac{5-\xi}{5-4}B_{11,0}(\xi) 5-\xi \textrm{ dla } \xi\in[4,5] \),  \( B_{11,1}(\xi)=\frac{\xi-\xi_{11}}{\xi_{12}-\xi_{11}}B_{11,0}(\xi)+\frac{\xi_{13}-\xi}{\xi_{13}-\xi_{12}}B_{12,0}(\xi) =\frac{\xi-4}{5-4}B_{11,0}(\xi)+{\color{red}{\frac{5-\xi}{5-5}B_{12,0}(\xi)}} =\xi-4 \textrm{ dla } \xi\in[4,5] \),  \( B_{12,1}(\xi)=\frac{\xi-\xi_{12}}{\xi_{13}-\xi_{12}}B_{12,0}(\xi)+\frac{\xi_{14}-\xi}{\xi_{14}-\xi_{13}}B_{13,0}(\xi) ={\color{red}{\frac{\xi-5}{5-5}B_{11,0}(\xi)+\frac{5-\xi}{5-5}B_{12,0}(\xi)}}=0 \). Możemy teraz wygenerować wszystkie funkcje B-spline drugiego stopnia ponownie używając wzoru  dla  \( p=2 \), przy założeniu że kolejne węzły wsadzane do mianownika muszą być różne, a jeśli nie są różne, wówczas dany człon zamieniamy na zero. Człony które znikają zaznaczamy ponownie na czerwono. W końcowym etapie wyprowadzenia wstawiamy obliczone przed chwilą wzory na  \( B_{1,1}(\xi)=0 \),  \( B_{2,1}=1-\xi \textrm{ dla } \xi\in[0,1] \),  \( B_{3,1}=\xi \textrm{ dla } \xi\in[0,1] \),  \( B_{4,1}=2-\xi \textrm{ dla } \xi\in[1,2] \),  \( B_{5,1}=\xi-1 \textrm{ dla } \xi\in[1,2] \),  \( B_{6,1}=3-\xi \textrm{ dla } \xi\in[2,3] \),  \( B_{7,1}=\xi-2 \textrm{ dla } \xi\in[2,3] \),  \( B_{8,1}=4-\xi \textrm{ dla } \xi\in[3,4] \),  \( B_{9,1}=\xi-3 \textrm{ dla } \xi\in[3,4] \),  \( B_{10,1}=5-\xi \textrm{ dla } \xi\in[4,5] \),  \( B_{11,1}=\xi-4 \textrm{ dla } \xi\in[4,5] \), \( B_{12,1}=0 \). Uzyskujemy  \( B_{1,2}(\xi)=\frac{\xi-\xi_1}{\xi_{3}-\xi_1}B_{1,1}(\xi)+\frac{\xi_{4}-\xi}{\xi_{4}-\xi_{2}}B_{2,1}(\xi) ={\color{red}{\frac{\xi-0}{0-0}B_{1,1}(\xi)}}+\frac{1-\xi}{1-0}B_{2,1}(\xi) = (1-\xi)^2 \textrm{ dla } \xi\in[0,1]  \).  \( B_{2,2}(\xi)=\frac{\xi-\xi_2}{\xi_{4}-\xi_2}B_{2,1}(\xi)+\frac{\xi_{5}-\xi}{\xi_{5}-\xi_{3}}B_{3,1}(\xi) = \frac{\xi-0}{1-0}B_{2,1}(\xi)+\frac{1-\xi}{1-0}B_{3,1}(\xi) = \\ = \frac{\xi-0}{1-0}\left[1-\xi \textrm{ dla } \xi\in[0,1]\right]+\frac{1-\xi}{1-0}\left[ \xi \textrm{ dla } \xi\in[0,1] \right] = \xi (1-\xi) + (1-\xi)\xi \textrm{ dla } \xi\in[0,1] =2 \xi(1-\xi) \textrm{ dla } \xi\in[0,1] \).  \( B_{3,2}(\xi)=\frac{\xi-\xi_3}{\xi_{5}-\xi_3}B_{3,1}(\xi)+\frac{\xi_{6}-\xi}{\xi_{6}-\xi_{4}}B_{4,1}(\xi) =\frac{\xi-0}{1-0}B_{3,1}(\xi)+\frac{2-\xi}{2-1}B_{4,1}(\xi) = \\ =\frac{\xi-0}{1-0}\left[\xi \textrm{ dla } \xi\in[0,1]\right]+\frac{1-\xi}{1-0}\left[2-\xi \textrm{ dla } \xi\in[1,2] \right] = \xi^2 \textrm{ dla } \xi\in[0,1]+ (1-\xi)(2-\xi) \textrm{ dla } \xi\in[1,2]  \).  \( B_{4,2}(\xi)= \frac{\xi-\xi_4}{\xi_{6}-\xi_4}B_{4,1}(\xi)+\frac{\xi_{7}-\xi}{\xi_{7}-\xi_{5}}B_{5,1}(\xi) = \frac{\xi-1}{2-1}B_{4,1}(\xi)+\frac{2-\xi}{2-1}B_{5,1}(\xi) = \\ = \frac{\xi-1}{2-1}\left[ 2-\xi \textrm{ dla } \xi\in[1,2] \right]+\frac{2-\xi}{2-1}\left[ \xi-1 \textrm{ dla } \xi\in[1,2] \right] = \\ = (\xi-1)(2-\xi)+(2-\xi)(\xi-1) \textrm{ dla } \xi\in[1,2] =2(\xi-1)(2-\xi) \textrm{ dla } \xi\in[1,2] \).  \( B_{5,2}(\xi)=\frac{\xi-\xi_5}{\xi_{7}-\xi_5}B_{5,1}(\xi)+\frac{\xi_{8}-\xi}{\xi_{8}-\xi_{6}}B_{6,1}(\xi) = \frac{\xi-1}{2-1}B_{5,1}(\xi)+\frac{3-\xi}{3-2}B_{6,1}(\xi) = \\ =\frac{\xi-1}{2-1}\left[ \xi-1 \textrm{ dla } \xi\in[1,2] \right]+\frac{3-\xi}{3-2}\left[ 3-\xi \textrm{ dla } \xi\in[2,3] \right] = (\xi-1)^2  \textrm{ dla } \xi\in[1,2] + (3-\xi)^2 \textrm{ dla } \xi\in[2,3]  \).  \( B_{6,2}(\xi)=\frac{\xi-\xi_6}{\xi_{8}-\xi_6}B_{6,1}(\xi)+\frac{\xi_{9}-\xi}{\xi_{9}-\xi_{7}}B_{7,1}(\xi) =\frac{\xi-2}{3-2}B_{6,1}(\xi)+\frac{3-\xi}{3-2}B_{7,1}(\xi) = \\ =\frac{\xi-2}{3-2}\left[ 3-\xi \textrm{ dla } \xi\in[2,3] \right]+\frac{3-\xi}{3-2}\left[ \xi-2 \textrm{ dla } \xi\in[2,3] \right] = \\ = (\xi-2)(3-\xi)+(3-\xi)(\xi-2) \textrm{ dla } \xi\in[2,3] =2(\xi-2)(3-\xi) \textrm{ dla } \xi\in[2,3] \).  \( B_{7,2}(\xi)=\frac{\xi-\xi_7}{\xi_{9}-\xi_7}B_{7,1}(\xi)+\frac{\xi_{10}-\xi}{\xi_{10}-\xi_{8}}B_{8,1}(\xi) = \frac{\xi-2}{3-2}B_{7,1}(\xi)+\frac{4-\xi}{4-3}B_{8,1}(\xi) = \\ =\frac{\xi-2}{3-2}\left[ \xi-2 \textrm{ dla } \xi\in[2,3] \right]+\frac{4-\xi}{4-3}\left[ 4-\xi \textrm{ dla } \xi\in[3,4] \right] = (\xi-2)^2  \textrm{ dla } \xi\in[2,3] +(4-\xi)^2 \textrm{ dla } \xi\in[3,4]  \).  \( B_{8,2}(\xi)=\frac{\xi-\xi_8}{\xi_{10}-\xi_8}B_{8,1}(\xi)+\frac{\xi_{11}-\xi}{\xi_{11}-\xi_{9}}B_{9,1}(\xi) =\frac{\xi-3}{4-3}B_{8,1}(\xi)+\frac{4-\xi}{4-3}B_{9,1}(\xi) = \\ =\frac{\xi-3}{4-3}\left[ 4-\xi \textrm{ dla } \xi\in[3,4] \right]+ \frac{4-\xi}{4-3}\left[ \xi-3 \textrm{ dla } \xi\in[3,4] \right] = \\ = (\xi-3)(4-\xi)+(4-\xi)(\xi-3) \textrm{ dla } \xi\in[3,4] =2(\xi-3)(4-\xi) \textrm{ dla } \xi\in[3,4] \).  \( B_{9,2}(\xi)=\frac{\xi-\xi_9}{\xi_{11}-\xi_9}B_{9,1}(\xi)+\frac{\xi_{12}-\xi}{\xi_{12}-\xi_{10}}B_{10,1}(\xi) = \frac{\xi-3}{4-3}B_{9,1}(\xi)+\frac{6-\xi}{5-4}B_{9,1}(\xi) = \\ =\frac{\xi-3}{4-3}\left[ \xi-3 \textrm{ dla } \xi\in[3,4] \right]+\frac{5-\xi}{5-4}\left[ 5-\xi \textrm{ dla } \xi\in[4,5] \right] = (\xi-3)^2  \textrm{ dla } \xi\in[3,4] +(5-\xi)^2 \textrm{ dla } \xi\in[4,5]  \).  \( B_{10,2}(\xi)=\frac{\xi-\xi_{10}}{\xi_{12}-\xi_{10}}B_{10,1}(\xi)+\frac{\xi_{13}-\xi}{\xi_{13}-\xi_{11}}B_{11,1}(\xi) = \frac{\xi-4}{5-4}B_{10,1}(\xi)+\frac{5-\xi}{5-4}B_{11,1}(\xi) = \\ = \frac{\xi-4}{5-4}\left[ 5-\xi \textrm{ dla } \xi\in[4,5] \right]+ \frac{5-\xi}{5-4}\left[ \xi-4 \textrm{ dla } \xi\in[4,5] \right] = \\ = (\xi-4)(5-\xi)+(5-\xi)(\xi-4) \textrm{ dla } \xi\in[4,5] =2(\xi-4)(5-\xi) \textrm{ dla } \xi\in[4,5] \).  \( B_{11,2}(\xi)=\frac{\xi-\xi_{11}}{\xi_{12}-\xi_{11}}B_{11,1}(\xi)+\frac{\xi_{14}-\xi}{\xi_{14}-\xi_{12}}B_{12,1}(\xi) = \frac{\xi-4}{5-4}B_{11,1}(\xi)+{\color{red}{\frac{8-\xi}{5-5}B_{11,1}(\xi)}} = \\ = \frac{\xi-4}{5-4}\left[ \xi-4 \textrm{ dla } \xi\in[4,5] \right]= (\xi-4)^2  \textrm{ dla } \xi\in[4,5] \). Wyprowadziliśmy właśnie funkcje bazowe rozpięte na wektorze węzłów z powtórzonymi węzłami pomiędzy każdym elementem. Można udowodnić matematycznie że  wyprowadzone tutaj funkcje  \( B_{1,2},...,B_{11,2} \) stanowią bazę równoważną bazie tak zwanych wielomianów Lagrange'a. Można więc używać notacji wektora węzłów żeby wyprowadzać funkcje bazowe B-spline używane w izogeometrycznej metodzie elementów skończonych, lub w celu wyprowadzenia bazy równoważnej bazie Lagrange'a używanej w klasycznej metodzie elementów skończonych.


Ulepszona analiza izogeometryczna w 1D

W rozdziale tym przyjrzymy się jakie macierze generują opisane powyżej funkcje bazowe B-spline, i oszacujemy koszt faktoryzacji wynikającego z nich układu równań. W ten sposób możemy wskazać funkcje bazowe, które dają szybsze obliczenia za pomocą izogeometrycznej metody elementów skończonych. Ustalmy dla uproszczenia że rozwiązujemy problem projekcji jednowymiarowej. Szczegóły na temat obliczeń dwuwymiarowej projekcji zostały opisane w rozdziale pierwszym. Mamy wówczas następujące macierze zwane macierzami masowymi. Załóżmy, że chcemy rozwiązać jednowymiarowy problem projekcji.  \( u(x) \approx BITMAP(x)  \) Szczegóły na temat obliczeń dwuwymiarowej projekcji zostały opisane w rozdziale pierwszym. Tutaj, dla uproszczenia załóżmy, że rozwiązujemy jednowymiarowy problem projekcji, nasza bitmapa (którą aproksymujemy w rozdziale pierwszym) jest teraz jednowymiarową płaską bitmapą. Nasza jednowymiarowa bitmapa jest aproksymowana przez kombinację liniową funkcji B-spline  \( u = \sum_{i=1,...,N_x} u_{i} B^x_{i;p }(x)  \) Załóżmy, że mamy 16 elementów skończonych (16 przedziałów). Oczywiście nie oznacza to że  \( N_x=16  \), ponieważ  \( N_x  \) to liczba funkcji bazowych. To, ile będzie funkcji bazowych, zależy od tego jak wyglądać będzie nasz wektor węzłów, innymi słowy od tego jakiego stopnia będą nasze funkcje bazowe, oraz czy i w których miejscach powtórzymy węzły pomiędzy elementami. W celu wygenerowania układu równań z naszą macierzą do problemu projekcji, przemnażamy nasze równanie przez poszczególne B-spliny, które służą do uśredniania naszej relacji  \( u(x)=BITMAP(x) \) zgodnie z rozkładem opisanym przez B-spline'y, i całkujemy żeby policzyć średnią  \( \int{\color{red}{u(x)}}B^x_{1;p}(x)dx=\int BITMAP(x)B^x_{1;p }(x)dx \\ \int{\color{red}{u(x)}}B^x_{2;p}(x)dx=\int BITMAP(x)B^x_{2;p}(x)dx \\ \vdots \\ \int{\color{red}{u(x)}}B^x_{N_x-1;p}(x)dx=\int BITMAP(x)B^x_{N_x-1;p}(x)dx \\\int{\color{red}{u(x)}}B^x_{N_x;p}(x)dx=\int BITMAP(x)B^x_{N_x;p }(x)dx  \) Wstawiamy naszą aproksymację w miejsce  \( {\color{red}{u(x)}}=\sum_{i=1,...,N_x} u_{i} B^x_{i;p }(x) \)  i dostajemy układ równań:  \( \int{\color{red}{\sum_{i=1,...,N_x} u_{i} B^x_{i;p}(x)} }B^x_{1;p}(x)dx=\int BITMAP(x)B^x_{1;p}(x) dx \\ \int{\color{red}{\sum_{i=1,...,N_x} u_{i} B^x_{i;p}(x)} }B^x_{2;p}(x)dx=\int BITMAP(x)B^x_{2;p}(x) dx \\ \vdots \\ \int{\color{red}{\sum_{i=1,...,N_x} u_{i} B^x_{i;p}(x)} }B^x_{N_x-1;p}(x)dx=\int BITMAP(x)B^x_{N_x-1;p}(x) dx  \\ \int{\color{red}{\sum_{i=1,...,N_x} u_{i} B^x_{i;p}(x)} }B^x_{N_x;p}(x)dx=\int BITMAP(x)B^x_{N_x;p}(x) dx  \) Wyciągamy sumę przed całkę  \( \sum_{i=1,...,N_x}\int{\color{red}{ u_{i} B^x_{i;p}(x)} }B^x_{1;p}(x)dx=\int BITMAP(x)B^x_{1;p}(x) dx \\ \sum_{i=1,...,N_x} \int{\color{red}{u_{i} B^x_{i;p}(x)} }B^x_{2;p}(x)dx=\int BITMAP(x)B^x_{2;p}(x) dx \\ \vdots \\ \sum_{i=1,...,N_x} \int{\color{red}{u_{i} B^x_{i;p}(x)} }B^x_{N_x-1;p}(x)dx=\int BITMAP(x)B^x_{N_x-1;p}(x) dx  \\ \sum_{i=1,...,N_x} \int{\color{red}{ u_{i} B^x_{i;p}(x) } }B^x_{N_x;p}(x)dx=\int BITMAP(x)B^x_{N_x;p}(x) dx  \) Możemy zapisać nasz układ równań w następującej postaci  \(  \begin{bmatrix}\int B^x_{1;p}(x)B^x_{1;p}(x)dx & \int B^x_{1;p}(x)B^x_{2;p}(x)dx & \cdots &  \int B^x_{1;p}(x)B^x_{N_x;p}(x)dx \\ \int B^x_{2;p}(x)B^x_{2;p}(x)dx & \int B^x_{2;p}(x)B^x_{2;p}(x)dx & \cdots &  \int B^x_{2;p}(x)B^x_{N_x;p}(x)dx \\ \vdots & \vdots & \vdots &  \vdots \\ \int B^x_{N_x-1;p}(x)B^x_{1;p}(x)dx & \int B^x_{N_x-1;p}(x)B^x_{2;p}(x)dx & \cdots &  \int B^x_{N_x-1;p}(x)B^x_{N_x;p}(x)dx \\ \int B^x_{N_x;p}(x)B^x_{N_x;p}(x)dx & \int B^x_{N_x;p}(x)B^x_{2;p}(x)dx & \cdots &  \int B^x_{N_x;p}(x)B^x_{N_x;p}(x)dx \\ \end{bmatrix} \begin{bmatrix}  u_{1} \\ u_{2} \\ \vdots \\ u_{N_x-1}\\ u_{N_x}\\ \end{bmatrix} =   \)  \(  \begin{bmatrix} \int BITMAP(x)B^x_1(x)dx \\ \int BITMAP(x)B^x_2(x)dx \\ \vdots \\ \int BITMAP(x)B^x_{N_x}-1(x)dx \\ \int BITMAP(x)B^x_{N_x}(x)dx \\ \end{bmatrix}  \)

Zauważmy, że układ równań jest rzadki ponieważ całki z iloczynów jednowymiarowych funkcji B-spline są niezerowe jedynie wtedy gdy funkcje bazowe występujące w całce nachodzą na siebie.

Całka to suma próbek wartości funkcji, które całkujemy. Nasza próbka to iloczyn dwóch funkcji  \( i \)-tej oraz  \( j \)-tej,  \( B^x_{i;p}(x)B^x_{j;p}(x) \). Jeśli więc dla danego punktu w którym próbkujemy jedna z funkcji, na przykład  \( i \) -ta jest równa zero, mamy więc  \( 0*B^x_{j;p}(x)=0 \). Podobnie jeśli druga funkcja  \( j \)-ta jest równa zero w miejscu próbkowania, mamy wówczas  \( B^x_{i;p}(x)*0=0 \).

Załóżmy teraz, że na naszych szesnastu elementach rozpinamy wektor węzłów [0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 16 16]. Uzyskujemy w ten sposób funkcje B-spline trzeciego stopnia, stosowane w izogeometrycznej metodzie elementów skończonych. Są one przedstawione na Rys. 2. Rysunek ten ilustruje również fakt, iż kolejne wiersze i kolumny w macierzy związane są z kolejnymi funkcjami B-spline, określonymi w wektorze węzłów. Zauważmy, że każda jednowymiarowa funkcja B-spline stopnia  \( p \) nachodzi na  \( 2p+1 \)  innych jednowymiarowych funkcji B-spline. Na przykład B-spline'y stopnia trzeciego  \( p=3 \) nachodzą na  \( 2*3+1=7 \) sąsiednich B-spline'ów. Niezerowe wartości w macierzy występują w tych wierszach i kolumnach, dla których odpowiadające im funkcje bazowe "nachodzą" na siebie (mają nie zerowe przecięcie dziedzin). Nasz układ równań jest więc rzadki, oraz zawiera on siedem przekątnych. W szczególności dla naszego przypadku szesnastu elementów skończonych mamy  \( N=16+3=19 \) funkcji bazowych B-spline trzeciego stopnia (w ogólności jest ich  \( N_e+p \)). Nasza macierz ma więc rozmiar  \( 19 \times 19 \) i zawiera 7 gęstych przekątnych. Jest to zilustrowane na Rys. 2.

Oszacujmy teraz koszt faktoryzacji macierzy dla bazy B-spline'ów trzeciego stopnia używanych w izogeometrycznej metodzie elementów skończonych, przedstawionej na Rys. 2. Uruchamiamy algorytm eliminacji Gaussa, który omija wszystkie zera w macierzy. Jest on opisany w rozdziale czwartym. Koszt ten równy jest kosztowi faktoryzacji pojedynczego wiersza na podmacierzy o rozmiarze  \( 4\times 4 \) przemnożony przez wiele wierszy macierzy, czyli 4 (koszt skalowania wiersza o czterech kolumnach) plus 3*3*2 (koszt odejmowania pierwszego wiersza od pozostałych, wraz z mnożeniem przez wartość z przekątnej). Operację tę powtarzamy dla 16 bloków macierzy (odpowiadających 16 elementom), a dla ostatniego bloku dokańczamy faktoryzację co ma koszt 2*2*2 (koszt odejmowania drugiego wiersza od pozostałych, wraz z mnożeniem przez wartość z przekątnej) + 2*1 (koszt odejmowania trzeciego wiersza od czwartego, wraz z mnożeniem przez wartość z przekątnej) + 1 (skalowanie ostatniego wiersza).Koszt całkowity to 4*16+3*3*2*16+2*2*2+2*1+1=64+18*16+8+2+1=363.

Rozważmy teraz przypadek, w którym powtarzamy węzły pomiędzy poszczególnymi elementami skończonymi. W ogólności jeśli chcemy uzyskać funkcje bazowe równoważne wielomianom Lagrange'a używanym w klasycznej metodzie elementów skończonych, musimy powtórzyć węzły  \( p-1 \) razy, gdzie  \( p \) to stopień bazy B-spline'ów. Jeśli więc stosujemy B-spliny trzeciego stopnia, musimy powtórzyć węzły 2 razy: [0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 14 14 14 15 15 15 16 16 16 16]

Jak wygląda macierz naszego układu równań? Każda funkcja bazowa trzeciego stopnia rozpościera się na czterech przedziałach (używa pięciu węzłów, począwszy od 0 0 0 0 1 do 15 16 16 16 16. Ile mamy takich podprzedziałów w naszym wektorze węzłów? Jest ich 16*3+1=49. Nasza macierz ma więc wymiar  \( 49 \times 49 \), i jest przedstawiona na Rys. 3. Różnica macierzy właśnie uzyskanej dla bazy równoważnej wielomianom Lagrange'a w stosunku do macierzy uzyskanej wcześniej dla funkcji B-spline jest następująca. Macierz dla funkcji B-spline jest mniejsza, ale posiada gęstsze przekątne. Macierz uzyskana dla bazy równoważnej bazie Lagrange'a jest większa, ale za to posiada rzadsze przekątne.

Oszacujmy teraz koszt faktoryzacji macierzy dla bazy równoważnej bazie Lagrange'a używanej w standardowej metodzie elementów skończonych, przedstawionej na Rys. 3. Koszt ten równy jest kosztowi faktoryzacji pojedynczej podmacierzy o rozmiarze  \( 49 \times 49 \) przez pełny algorytm eliminacji Gaussa, czyli 4+3+2 (koszt skalowania pierwszego, drugiego, trzeciego i czwartego wiersza od przekątnej do końca wiersza) plus 3*3*2 (koszt odejmowania pierwszego wiersza od pozostałych, wraz z mnożeniem przez wartość z przekątnej) plus 2*2*2 (koszt odejmowania drugiego wiersza od pozostałych, wraz z mnożeniem przez wartość z przekątnej) + 1*2 (koszt odejmowania trzeciego wiersza od czwartego, wraz z mnożeniem przez wartość z przekątnej). Koszt całkowity dla jednego bloku to 4+3+2+3*3*2+2*2*2+2*1=9+18+8+2=36. Mamy 16 elementów, więc koszt całkowity to 36*16=576.

Zobaczmy teraz co stanie się jeśli pomieszamy wielomiany Lagrange'a z funkcjami B-spline. Wprowadźmy wektor węzłów [0 0 0 0 1 2 3 4 4 4 5 6 7 8 8 8 9 10 11 12 12 12 13 14 15 16 16 16 16], w którym powtórzyliśmy węzły na grupach co cztery elementy. Uzyskamy wtedy funkcje bazowe i macierz przedstawioną na Rys. 4. W tym przypadku mamy 25 funkcji bazowych. Nasza macierz ma więc rozmiar  \( 25\times 25 \), i posiada gęste bloki na przekątnych o rozmiarze 7 na 7. Bloki te poprzedzielane są separatorami. Jaki jest koszt faktoryzacji w tym przypadku? Koszt ten równy jest kosztowi faktoryzacji pojedynczego wiersza na podmacierzy o rozmiarze  \( 4\times 4 \) przemnożony przez wiersze macierzy w pojedynczym bloku, czyli 4 (koszt skalowania wiersza o czterech kolumnach) plus 3*3*2 (koszt odejmowania pierwszego wiersza od pozostałych, wraz z mnożeniem przez wartość z przekątnej). Operacje tą powtarzamy dla 4 podmacierzy w bloku (odpowiadających 4 elementom), i całość powtarzamy 4 razy (ponieważ mamy 4 bloki w macierzy). Całkowity koszt wynosi więc (4+3*3*2*4)*4=76*4=304.

Podsumujmy. Klasyczna metoda elementów skończonych dla 16. elementów i bazy równoważnej bazie Lagrange'a trzeciego stopnia wymaga 576 operacji zmienno-przecinkowych żeby sfaktoryzować macierz (mnożeń i dodawań / odejmowań). Izogeometryczna metoda elementów skończonych dla 16 elementów bazy równoważnej bazie B-spline'ów trzeciego stopnia wymaga 363 operacji zmienno-przecinkowych. Hybryda wymaga 304 operacji zmienno-przecinkowych.

Widzimy więc że nawet w prostym jednowymiarowym przykładzie hybryda pomiędzy bazą funkcji B-spline oraz bazą równoważną bazie Lagrange'a wygrywa w sensie kosztu obliczeniowego z czystą bazą B-spline'ów oraz z czystą bazą równoważną bazie Lagrange'a. Różnica ta będzie dużo większa (do dwóch rzędów wielkości) dla problemów dwuwymiarowych oraz trójwymiarowych.

Obliczenia za pomocą metody elementów skończonych, w których używa się baz mieszanych, w których stosuje się powtarzanie węzłów w wektorze węzłów nazywane są ulepszoną analizą izogeometryczną (ang. refined isogeometric analysis) [1] Zauważmy również, iż wsadzając  \( C^0 \) separatory (redukując ciągłość na granicach elementów) niejako zwiększamy moc aproksymacyjną używanej bazy funkcji. Tak więc dodawanie nowych funkcji i redukowanie ciągłości zwiększa dokładność aproksymacji.


Uogólnienie funkcji bazowych poprzez iloczyn tensorowy na 2D

W poprzednich rozdziałach zdefiniowaliśmy funkcje bazowe na przedziale jednowymiarowym. Pokażemy teraz w jaki sposób definicje tą można uogólnić na problemy dwuwymiarowe. Rozważmy najpierw siatkę prostokątną, na której rozpinamy  \( N_x \times N_y=16 \times 16 \) elementów, której jeden brzeg jest równoległy do osi  \( x \), a drugi brzeg jest równoległy do osi  \( y \). Mamy więc  \( N_x=16 \) elementów wzdłuż osi  \( x \) i  \( N_y=16 \) elementów wzdłuż osi  \( y \).

Rozważmy problem aproksymacji bitmapy opisany szczegółowo w rozdziale pierwszym. Definiujemy następnie wektor węzłów wzdłuż osi  \( x \) [0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 16 16] oraz wektor węzłów wzdłuż osi  \( y \) [0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 16 16]. Wektory te definiują funkcje bazowe B-spline trzeciego stopnia o ciągłości dwa, co oznacza, że jeśli policzymy projekcje bitmapy przy użyciu takich funkcji B-spline, nasza aproksymacja będzie bardzo smukła, i możemy policzyć pierwszą i drugą pochodną w każdym punkcje bitmapy. To tak jakbyśmy zrobili bardzo smukły plastikowy odlew naszej bitmapy.

Nasz wektor węzłów wzdłuż osi  \( x \) definiuje następujące funkcje bazowe  \( B^x_{1;3}(x),...,B^x_{19;3}(x) \), których wzór może zostać wyprowadzony zgodnie ze wzorem Cox-de Boora, ale wyprowadzając B-spline trzeciego stopnia, które rozpościerają się na czterech kolejnych przedziałach. W szczególności będzie ich  \( N_x+p=N_x+3=16+3=19 \). Podobnie wyprowadzamy funkcje B-spline dla wektora węzłów wzdłuż osi  \( y \), i dostajemy 19 B-spline'ów trzeciego stopnia  \( B^y_{1;3}(x),...,B^y_{19;3}(x) \) zgodnie z formułą . Następnie nasze dwuwymiarowe funkcje bazowe uzyskujemy przemnażając przez siebie wybraną jedną funkcję bazową z pierwszego zbioru, rozpiętego wzdłuż osi  \( x \), oraz jedną funkcję bazową z drugiego zbioru, rozpiętego wzdłuż osi  \( y \). Możemy je uporządkować wierszami (równoległymi do osi  \( x \)).  \( B^x_{1;3}B^y_{1,3}, B^x_{2;3}B^y_{1,3}, ..., B^x_{N_x-1;3}B^y_{1,3}, B^x_{N_x;3}B^y_{1,3} \),  \( B^x_{1;3}B^y_{2,3}, B^x_{2;3}B^y_{2,3}, ..., B^x_{N_x-1;3}B^y_{2,3}, B^x_{N_x;3}B^y_{2,3} \),  \( \cdots \),  \( B^x_{1;3}B^y_{N_y-1,3}, B^x_{2;3}B^y_{N_y-1,3}, ..., B^x_{N_x-1;3}B^y_{N_y-1,3}, B^x_{N_x;3}B^y_{N_y-1,3} \),  \( B^x_{1;3}B^y_{N_y,3}, B^x_{2;3}B^y_{N_y,3}, ..., B^x_{N_x;3}B^y_{N_y,3}, B^x_{N_x;3}B^y_{N_y,3} \). W ten sposób uzyskujemy bazę dwuwymiarowych funkcji bazowych  \(  \{B_{i,j;3}(x,y)\}_{i=1,...,N_x,j=1,...,N_y}=\{B^x_{i;3}(x)B^y_{j;3}\}_{i=1,...,N_x,j=1,...,N_y}=\{B^x_{i;3}(x)B^y_{j;3}\}_{i=1,...,19,j=1,...,19 } \) rozpiętych na prostokątnej siatce obliczeniowej zbudowanej z 16*16=256 elementów. Są one przedstawione na Rys. 1.

Możemy rozpiąć na nich aproksymację bitmapy  \( u(x,y) = \sum_{i=1,j=1}^{16,16} u_{i,j} B^x_{i}(x)  B^y_{j}(y) \) obliczając współczynniki  \( u_{i,j} \) w sposób opisany w rozdziale pierwszym. W szczególności będziemy musieli wygenerować całki z iloczynów par funkcji B-spline  \( \int_{\Omega} {\color{red}{B^x_{i}(x)  B^y_{j}(y)}} {\color{blue}{B^x_{k}(x)B^y_{l}(y)}} dx dy \) dla  \( i=1,..,19, j=1,...,19 \). W przypadku funkcji bazowych rozpiętych na płaskiej siatce, całki te możemy rozbić na iloczyn dwóch całek z par B-spline'ów w poszczególnych kierunkach.  \( \int_{\Omega_x} {\color{red}{B^x_{i}(x)B^y_{j}(y)}} {\color{blue}{B^x_{k}(x)B^y_{l}(y)}} dx dy =\int_{\Omega_y} {\color{red}{B^x_{i}(x)}} {\color{blue}{B^x_{k}(x)}} dx {\color{red}{ B^y_{j}(y)}} {\color{blue}{B^y_{l}(y)}} dy \) gdzie  \( \Omega \) to nasza siatka prostokątna,  \( \Omega_x \) to brzeg siatki prostokątnej wzdłuż osi  \( x \),  \( \Omega_y \) to brzeg siatki prostokątnej wzdłuż osi  \( y \).

Zajmijmy się teraz ciekawym przypadkiem, w którym nasza siatka obliczeniowa nie jest regularnym prostokątem, tylko rozpięta jest na przykład na rozmaitości, tak jak pokazano na Rys. 2.

W przypadku tym mamy funkcje B-spline liniowe (pierwszego stopnia) w kierunku osi  \( x \) oraz funkcje B-spline drugiego stopnia w kierunku osi  \( y \). Ilustracją może być tutaj wykres przedstawiony na Rys. 2 to  \( y(y-1)(1-x) \). Wówczas definiujemy nasze wektory węzłów w taki sam sposób, definiujemy jednowymiarowe funkcje bazowe w taki sam sposób, i następnie musimy określić transformacje z siatki prostokątnej na siatkę rozpiętą na brzegu rozmaitości. Wówczas całki, które musimy wygenerować muszą zostać policzone po brzegu rozmaitości  \( \Omega_S \) i musimy inaczej nazwać nasze osie  \( \hat{x} \) i  \( \hat{y} \) rozpięte na brzegu rozmaitości.  \( \int_{\Omega_S} {\color{red}B^{\hat{x}}_{i;3}(\hat{x})  B^{\hat{y}}_{j;3}(\hat{y})} {\color{blue}B^{\hat{x}}_{k;3}(\hat{x})B^{\hat{y}}_{l;3}(\hat{y})} d\hat{x} d\hat{y}  \) i musimy zmienić zmienne w całkach z prostokąta rozpiętego na brzegu rozmaitości na prostokącie  \( \int_{\Omega_S} {\color{red}{B^x_{i}(\hat{x})  B^y_{j}(\hat{y})}} {\color{blue}{B^x_{k}(\hat{x})B^y_{l}(\hat{y})}} d\hat{x} d\hat{y} = \int_{\Omega} {\color{red}{B^x_{i}(x)  B^y_{j}(y)}} {\color{blue}{B^x_{k}(x)B^y_{l}(y)}} |Jac Map(x,y)|dx dy  \) i tutaj pojawia się jakobian odwzorowania  \( Map(x,y) \), które przerzuca płaski prostokąt na prostokąt rozpięty na brzegu rozmaitości. Zauważmy, że żeby zastosować algorytm szybkiego solwera zmienno-kierunkowego, jakobian tej mapy musi zostać rozdzielony na iloczyn jakobianów będących funkcją  \( x \) i funkcją  \( y \), czyli  \( Jac Map(x,y)=Jac Map^x(x)Jac Map^y(y) \). W przeciwnym wypadku nie rozdzielimy całek  \( \int_{\Omega_S} {\color{red}B^x_{i}(\hat{x})  B^y_{j}(\hat{y})} {\color{blue}B^x_{k}(\hat{x})B^y_{l}(\hat{y})} d\hat{x} d\hat{y} = \int_{\Omega} {\color{red}B^x_{i}(x)  B^y_{j}(y)} {\color{blue}B^x_{k}(x)B^y_{l}(y)} |Jac Map(x,y)|dx dy =\\= \int_{\Omega} {\color{red}B^x_{i}(x)  } {\color{blue}B^x_{k}(x)}|Jac Map^x(x)|dx \int_{\Omega} {\color{red}B^y_{j}(y)} {\color{blue}B^y_{l}(y)} |Jac Map^y(y)| dy \). Nie jest to zawsze możliwe, i wówczas zamiast solwera zmienno-kierunkowego należy stosować solwer wielo-frontalny lub iteracyjny.


Uogólnienie funkcji bazowych poprzez iloczyn tensorowy na 3D

Pokażemy teraz w jaki sposób definicje funkcji bazowych można uogólnić na problemy trójwymiarowe. Rozważmy najpierw siatkę prostopadłościenną, na której rozpinamy  \( N_x \times N_y \times N_z=16 \times 16 \times 16 \) elementów, której jeden brzeg jest równoległy do osi  \( x \), drugi brzeg jest równoległy do osi  \( y \), a trzeci brzeg jest równoległy do osi  \( z \). Mamy więc  \( N_x=16 \) elementów wzdłuż osi  \( x \) i  \( N_y=16 \) elementów wzdłuż osi \( y \) oraz  \( N_z=16 \) elementów wzdłuż osi  \( z \). Rozważmy ponownie problem aproksymacji bitmapy opisany szczegółowo w rozdziale pierwszym. Tym razem jednak musimy skupić się na trójwymiarowej bitmapie. Bitmapy takie są generowane na przykład podczas tomografii komputerowych głowy .

Definiujemy następnie wektor węzłów wzdłuż osi  \( x \) [0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 16 16] oraz identyczny wektor węzłów wzdłuż osi  \( y \) [0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 16 16] i wzdłuż osi  \( z \) [0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 16 16]. Wektory te definiują funkcje bazowe B-spline trzeciego stopnia o ciągłości dwa. Nasze wektory węzłów definiują następujące funkcje bazowe  \( B^x_{1;3}(x),...,B^x_{19;3}(x)\textrm{ wzdłuż osi }x \),  \( B^y_{1;3}(y),...,B^y_{19;3}(y)\textrm{ wzdłuż osi }y \), oraz  \( B^z_{1;3}(z),...,B^z_{19;3}(z)\textrm{ wzdłuż osi }z \), Wzory tych jednowymiarowych funkcji B-spline mogą zostać wyprowadzone zgodnie z formułą Cox-de Boor'a. Jest ich 19 w każdym kierunku, ponieważ  \( N_x+p=N_y+p=N_z+p=16+3=19 \). Następnie nasze trójwymiarowe funkcje bazowe uzyskujemy przemnażając przez siebie wybraną jedną funkcję bazową z pierwszego zbioru, rozpiętego wzdłuż osi  \( x \), jedną funkcję bazową z drugiego zbioru, rozpiętego wzdłuż osi  \( y \), oraz jedną funkcję bazową z drugiego zbioru, rozpiętego wzdłuż osi  \( z \). Możemy je uporządkować poziomami (równolegle do płaszczyzny  \( OXY \)) i wierszami (równoległymi do osi  \( x \)).  \( B^x_{1;3}B^y_{1,3}B^z_{1,3}, B^x_{2;3}B^y_{1,3}B^z_{1,3}, ..., B^x_{N_x-1;3}B^y_{1,3}B^z_{1,3}, B^x_{N_x;3}B^y_{1,3}B^z_{1,3} \),  \( B^x_{1;3}B^y_{2,3}B^z_{1,3}, B^x_{2;3}B^y_{2,3}B^z_{1,3}, ..., B^x_{N_x-1;3}B^y_{2,3}B^z_{1,3}, B^x_{N_x;3}B^y_{2,3}B^z_{1,3} \),  \( \cdots \),  \( B^x_{1;3}B^y_{N_y-1,3}B^z_{1,3}, B^x_{2;3}B^y_{N_y-1,3}B^z_{1,3}, ..., B^x_{N_x-1;3}B^y_{N_y-1,3}B^z_{1,3}, B^x_{N_x;3}B^y_{N_y-1,3}B^z_{1,3} \),  \( B^x_{1;3}B^y_{N_y,3}B^z_{1,3}, B^x_{2;3}B^y_{N_y,3}B^z_{1,3}, ..., B^x_{N_x;3}B^y_{N_y,3}B^z_{1,3}, B^x_{N_x;3}B^y_{N_y,3}B^z_{1,3} \),  \( \cdots \),  \( B^x_{1;3}B^y_{1,3}B^z_{2,3}, B^x_{2;3}B^y_{1,3}B^z_{2,3}, ..., B^x_{N_x-1;3}B^y_{1,3}B^z_{2,3}, B^x_{N_x;3}B^y_{1,3}B^z_{2,3} \),  \( B^x_{1;3}B^y_{2,3}B^z_{2,3}, B^x_{2;3}B^y_{2,3}B^z_{2,3}, ..., B^x_{N_x-1;3}B^y_{2,3}B^z_{2,3}, B^x_{N_x;3}B^y_{2,3}B^z_{2,3} \),  \( \cdots \),  \( B^x_{1;3}B^y_{N_y-1,3}B^z_{2,3}, B^x_{2;3}B^y_{N_y-1,3}B^z_{2,3}, ..., B^x_{N_x-1;3}B^y_{N_y-1,3}B^z_{2,3}, B^x_{N_x;3}B^y_{N_y-1,3}B^z_{2,3} \),  \( B^x_{1;3}B^y_{N_y,3}B^z_{2,3}, B^x_{2;3}B^y_{N_y,3}B^z_{2,3}, ...,B^x_{N_x;3}B^y_{N_y,3}B^z_{2,3}, B^x_{N_x;3}B^y_{N_y,3}B^z_{2,3} \),  \( \cdots \),  \( B^x_{1;3}B^y_{1,3}B^z_{N_z,3}, B^x_{2;3}B^y_{1,3}B^z_{N_z,3}, ..., B^x_{N_x-1;3}B^y_{1,3}B^z_{N_z,3},B^x_{N_x;3}B^y_{1,3}B^z_{N_z,3} \),  \( B^x_{1;3}B^y_{2,3}B^z_{N_z,3}, B^x_{2;3}B^y_{2,3}B^z_{N_z,3}, ..., B^x_{N_x-1;3}B^y_{2,3}B^z_{N_z,3}, B^x_{N_x;3}B^y_{2,3}B^z_{N_z,3} \),  \( \cdots \),  \( B^x_{1;3}B^y_{N_y-1,3}B^z_{N_z,3}, B^x_{2;3}B^y_{N_y-1,3}B^z_{N_z,3}, ..., B^x_{N_x-1;3}B^y_{N_y-1,3}B^z_{N_z,3}, B^x_{N_x;3}B^y_{N_y-1,3}B^z_{N_z,3} \),  \( B^x_{1;3}B^y_{N_y,3}B^z_{N_z,3}, B^x_{2;3}B^y_{N_y,3}B^z_{N_z,3}, ..., B^x_{N_x;3}B^y_{N_y,3}B^z_{N_z,3}, B^x_{N_x;3}B^y_{N_y,3}B^z_{N_z,3} \). W ten sposób uzyskujemy bazę trójwymiarowych funkcji bazowych  \( \{B_{i,j,k;3}(x,y,z)\}_{i=1,...,N_x,j-1,...,N_y,k=1,...,N_z}=\{B^x_{i;3}(x)B^y_{j;3}\}_{i=1,...,N_x,j-1,...,N_y} =\\=\{B^x_{i;3}(x)B^y_{j;3}\}_{i=1,...,19,j-1,...,19}, \{B^x_{i;3}(x)B^z_{j;3}\}_{i=1,...,19,j-1,...,19,k=1,...,19 }  \) rozpiętych na prostopadłościennej siatce obliczeniowej zbudowanej z 16*16*16=4096 elementów. Możemy rozpiąć na nich aproksymację trójwymiarowej bitmapy  \( u(x,y,z) = \sum_{i=1,j=1,k=1}^{16,16,16} u_{i,j,k} B^x_{i}(x)  B^y_{j}(y) B^y_{k}(z) \) obliczając współczynniki  \( u_{i,j,k} \) w sposób opisany w rozdziale pierwszym. W szczególności będziemy musieli wygenerować całki z iloczynów trójek funkcji B-spline  \( \int_{\Omega} {\color{red}B^x_{i}(x)  B^y_{j}(y) B^z_{k}(z)} {\color{blue}B^x_{l}(x)B^y_{m}(y)B^z_{n}(z)} dx dy dz \) dla  \( i=1,..,19, j=1,...,19, k=1,...,19 \). W przypadku funkcji bazowych rozpiętych na płaskiej siatce, całki te możemy rozbić na iloczyn trzech całek z trójek B-spline'ów w poszczególnych kierunkach.  \( \int_{\Omega_x} {\color{red}B^x_{i}(x)  B^y_{j}(y) B^y_{k}(z)} {\color{blue}B^x_{l}(x)B^y_{m}(y)B^z_{n}(z)} dx dy dz= \int_{\Omega_y} {\color{red}B^x_{i}(x)  } {\color{blue}B^x_{l}(x)} dx {\color{red}B^y_{j}(y)} {\color{blue}B^y_{m}(y)} dy {\color{red} B^z_{k}(z)} {\color{blue}B^z_{n}(z)} dz \) gdzie  \( \Omega \) to nasza siatka prostopadłościenna,  \( \Omega_x, \Omega_y, \Omega_z \) to osie rozpięte wzdłuż brzegów siatki  prostopadłościennej.

Zauważmy, że jeśli chcemy używać innych funkcji bazowych, na przykład bazy równoważnej wielomianom Lagrange'a, cała konstrukcja pozostaje niezmieniona, jedynie definiujemy inne wektory węzłów, które dają nam inną liczbę funkcji bazowych. Na przykład w celu wygenerowania bazy równoważnej wielomianom Lagrange'a, rozpinamy wektor węzłów [0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 14 14 14 15 15 15 16 16 16 16] wzdłuż osi  \( x \) oraz drugi wektor węzłów [0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 14 14 14 15 15 15 16 16 16 16] wzdłuż osi  \( y \) i trzeci wektor węzłów [0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 14 14 14 15 15 15 16 16 16 16] wzdłuż osi  \( z \). Następnie generujemy funkcje bazowe zgodnie ze wzorem , i przemnażamy je przez siebie (tworzymy iloczyn tensorowy). Możemy również na przykład wygenerować wielomiany B-spline trzeciego stopnia [0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 16 16] wzdłuż osi  \( x \), wielomiany równoważne wielomianom Lagrenge'a [0 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 14 14 14 15 15 15 16 16 16 16] wzdłuż osi  \( y \), oraz wielomiany "hybrydowe" [0 0 0 0 1 2 3 4 4 4 5 6 7 8 8 8 9 10 11 12 12 12 13 14 15 16 16 16 16]  wzdłuż osi  \( z \). Następnie podobnie, generujemy funkcje bazowe zgodnie ze wzorem , i przemnażamy je przez siebie (tworzymy iloczyn tensorowy). Możliwe jest również konstruowanie siatek, w których mieszane są grupy elementów (po angielsku "patches") różnego rozmiaru i o różnej liczbie elementów i tworzone wektory węzłów dla każdej grupy elementów oddzielnie. Przykład pokazany został na Rys. 2.


Siatki nieregularne

Podczas obliczeń metodą elementów skończonych stosuje się również siatki nieregularne zbudowane z elementów trójkątnych w dwóch wymiarach lub z elementów czworościennych w trzech wymiarach. Niestety metoda tworzenia funkcji bazowych poprzez wektory węzłów nie działa na siatkach trójkątnych lub siatkach czworościennych. Przed powstaniem analizy izogeometrycznej była to najczęściej stosowana wersja metody elementów skończonych.

Obecnie, obliczenia metodą elementów skończonych często integruje się z obiektami geometrycznymi tworzonymi w systemach CAD, i wówczas konieczność wygenerowania siatki elementów nieregularnych na obiektach geometrycznych opisanych już za pomocą funkcji B-spline i NURBS, wymaga dodatkowego narzutu pacy, oraz konieczności uzgadniania elementów na sąsiadujących obiektach geometrycznych. Na siatkach nieregularnych nie definiuję się funkcji B-spline o wyższej regularności, ze względu na fakt, iż funkcje B-spline zdefiniowane są na wielu sąsiadujących elementach prostokątnych. Na elementach trójkątnych i czworościennych definiuje się wielomiany rozpięte na elementach sąsiadujących z danym wierzchołkiem lub krawędzią. Przykładem takich funkcji są wielomiany Lagrange'a lub wielomiany hierarchiczne.

Metoda definiowania funkcji bazowych na takich elementach jest dobrze opisana w książkach prof. Leszka Demkowicza [1][2]

Na każdym elemencie trójkątnym zdefiniować można wielomiany pierwszego stopnia lub wielomiany wyższych stopni. Popularny sposób definiowana tych wielomianów przedstawiony jest na Rys. 1. Zaznaczony na nim trójkąt definiuję orientację trzech swoich krawędzi, co ma znaczenie przy formalnym definiowaniu funkcji bazowych i elementu skończonego.

W tym celu na dowolnym elemencie trójkątnym definiujemy dwie osie układu współrzędnych  \( \xi_1  \) wzdłuż jednego boku trójkąta oraz  \( \xi_2  \) wzdłuż drugiego boku trójkąta. Definiujemy trzy funkcje tworzące tak zwany barycentryczny układ współrzędnych rozpięty na trójkącie  \( \lambda_1(\xi_1,\xi_2)=1-\xi_1-\xi_2 \quad \lambda_2(\xi_1,\xi_2)=\xi_1 \quad \lambda_3(\xi_1,\xi_2)=\xi_2  \) osiągające maksimum równe 1 w poszczególnych wierzchołkach trójkąta, oraz 0 w pozostałych. Funkcja  \( \lambda_1 \) związana jest pierwszym wierzchołkiem w punkcie (0,0) w układzie współrzędnych rozpiętym przez osie  \( \xi_i \textrm{ i } \xi_2 \).

Funkcja  \( \lambda_2 \)  związana jest z drugim wierzchołkiem na osi  \( \xi_1 \). Funkcja  \( \lambda_3 \) związana jest z trzecim wierzchołkiem na osi  \( \xi_2 \). Funkcje te pozwalają nam definiować nasze funkcje bazowe, które oznaczamy  \( \psi_i \). Zaczynamy od funkcji bazowych związanych z wierzchołkami trójkąta  \( \psi_1(\xi_1,\xi_2)=\lambda_1(\xi_1,\xi_2)=1-\xi_1-\xi_2 \\ \psi_2(\xi_1,\xi_2)=\lambda_2(\xi_1,\xi_2)=\xi_1 \\ \psi_3(\xi_1,\xi_2)=\lambda_2(\xi_1,\xi_2)=\xi_2  \) Mamy więc oczywiście funkcję bazową  \( \psi_1 \) związaną z pierwszym wierzchołkiem w punkcie (0,0) w układzie współrzędnych rozpiętym przez osie  \( \xi_1 \textrm{ i } \xi_2 \), funkcję bazową  \( \psi_2 \) związaną z drugim wierzchołkiem na osi  \( \xi_1 \), oraz funkcje bazową  \( \psi_3 \) związaną z trzecim wierzchołkiem na osi  \( \xi_2 \)

Zbudowanie funkcji bazowych drugiego stopnia na trójkącie wymaga przemnażania przez siebie funkcji bazowych określonych na wierzchołkach. Budujemy funkcje  \( \psi_4(\xi_1,\xi_2)=\lambda_1(\xi_1,\xi_2)\lambda_2(\xi_1,\xi_2)=(1-\xi_1-\xi_2)\xi_1 \\ \psi_5(\xi_1,\xi_2)=\lambda_2(\xi_1,\xi_2)\lambda_3(\xi_1,\xi_2) =\xi_1\xi_2 \\ \psi_6(\xi_1,\xi_2)=\lambda_3(\xi_1,\xi_2)\lambda_1(\xi_1,\xi_2)= \xi_2(1-\xi_1-\xi_2)  \) Widać więc, że przemnożenie przez siebie dwóch funkcji wierzchołkowych związanych z pierwszym i drugim wierzchołkiem,  \( \lambda_1 \) i  \( \lambda_2 \) daje nam możliwość zdefiniowana funkcji kwadratowej rozpiętej na krawędzi pomiędzy tymi wierzchołkami.

Przemnożenie przez siebie dwóch funkcji wierzchołkowych związanych z drugim i trzecim wierzchołkiem,  \( \lambda_2 \) i  \( \lambda_3 \) daje nam możliwość zdefiniowana funkcji kwadratowej rozpiętej na krawędzi pomiędzy tymi wierzchołkami. Z kolei przemnożenie przez siebie funkcji wierzchołkowych związanych z pierwszym i trzecim wierzchołkiem,  \( \lambda_1 \), i  \( \lambda_3 \) daje nam możliwość zdefiniowana funkcji kwadratowej rozpiętej na trzeciej krawędzi trójkąta.

Funkcji bazowa nad wnętrzem elementu definiowana jest jako iloczyn wszystkich funkcji  \( \lambda_1 \),  \( \lambda_2 \),  \( \lambda_3 \).  \( \psi_7(\xi_1,\xi_2)=\lambda_1(\xi_1,\xi_2)\lambda_2(\xi_1,\xi_2)\lambda_3(\xi_1,\xi_2) \)

Jak to wygląda w trzech wymiarach, gdzie elementy mają kształt czworościenny? W przypadku trzech wymiarów musimy rozpiąć trzy osie układu współrzędnych  \( \xi_1, \xi_2, \xi_3 \) wzdłuż trzech krawędzi czworościanu. Następnie definiujemy analogiczne cztery funkcje  \( \lambda_1(\xi_1,\xi_2,\xi_3)=1-\xi_1-\xi_2 \xi_3 \\ \lambda_2(\xi_1,\xi_2,\xi_3)=\xi_1 \\ \lambda_3(\xi_1,\xi_2,\xi_3)=\xi_2 \\ \lambda_4(\xi_1,\xi_2,\xi_3)=\xi_3  \) osiągające maksimum równe 1 w poszczególnych wierzchołkach trójkąta, oraz 0 w pozostałych.

Mamy więc oczywiście funkcję bazową  \( \psi_1 \) związaną z pierwszym wierzchołkiem w punkcie (0,0,0) w układzie współrzędnych rozpietym przez osie  \( \xi_1, \xi_2, \xi_3 \), funkcję bazową  \( \psi_2 \) związaną z drugim wierzchołkiem na osi  \( \xi_1 \), funkcję bazową  \( \psi_3 \) związaną z trzecim wierzchołkiem na osi  \( \xi_2 \), oraz funkcję bazową  \( \psi_4 \) związaną z trzecim wierzchołkiem na osi  \( \xi_3 \).

Zbudowanie funkcji bazowych drugiego stopnia na czworościanie wymaga przemnażania przez siebie funkcji bazowych określonych na wierzchołkach. Budujemy funkcje krawędziowe przemnażając dwie funkcje wierzchołkowe  \( \psi_4(\xi_1,\xi_2,\xi_3)=\lambda_1(\xi_1,\xi_2,\xi_3)\lambda_2(\xi_1,\xi_2,\xi_3)=(1-\xi_1-\xi_2-\xi_3)\xi_1 \\ \psi_5(\xi_1,\xi_2,\xi_3)=\lambda_2(\xi_1,\xi_2,\xi_3)\lambda_3(\xi_1,\xi_2,\xi_3)=\xi_1\xi_2  \\ \psi_6(\xi_1,\xi_2,\xi_3)=\lambda_3(\xi_1,\xi_2,\xi_3)\lambda_4(\xi_1,\xi_2,\xi_3)=\xi_2\xi_3 \\ \psi_7(\xi_1,\xi_2,\xi_3)=\lambda_4(\xi_1,\xi_2,\xi_3)\lambda_1(\xi_1,\xi_2,\xi_3)=\xi_1(1-\xi_1-\xi_2-\xi_3)  \) Widać więc że przemnożenie przez siebie dwóch funkcji wierzchołkowych związanych z pierwszym i drugim wierzchołkiem,  \( \lambda_1 \) i  \( \lambda_2 \) daje nam możliwość zdefiniowana funkcji - wielomianu drugiego stopnia - rozpiętej na krawędzi pomiędzy tymi wierzchołkami (stopień wielomianu to suma wszystkich wykładników potęg niezerowego jednomianu).

Przemnożenie przez siebie dwóch funkcji wierzchołkowych związanych z drugim i trzecim wierzchołkiem,  \( \lambda_2 \) i  \( \lambda_3 \) daje nam możliwość zdefiniowania funkcji - wielomianu drugiego stopnia - rozpiętej na krawędzi pomiędzy tymi wierzchołkami. Przemnożenie przez siebie dwóch funkcji wierzchołkowych związanych z trzecim i czwartym wierzchołkiem,  \( \lambda_3 \)i  \( \lambda_4 \) daje nam możliwość zdefiniowana funkcji - wielomianu drugiego stopnia - rozpiętej na krawędzi pomiędzy tymi wierzchołkami. Z kolei przemnożenie przez siebie funkcji wierzchołkowych związanych z pierwszym i czwartym wierzchołkiem,  \( \lambda_1 \), i  \( \lambda_5 \) daje nam możliwość zdefiniowana funkcji - wielomianu drugiego stopnia - rozpiętej na trzeciej krawędzi trójkąta.

Funkcje bazowe na ścianach elementów uzyskuje się mnożąc odpowiednie trzy funkcje bazowe z wierzchołków otaczających ścianę  \( \psi_8(\xi_1,\xi_2,\xi_3)=\lambda_1(\xi_1,\xi_2,\xi_3)\lambda_2(\xi_1,\xi_2,\xi_3)\lambda_3(\xi_1,\xi_2,\xi_3)=(1-\xi_1-\xi_2-\xi_3)\xi_1\xi_2 \\ \psi_9(\xi_1,\xi_2,\xi_3)=\lambda_2(\xi_1,\xi_2,\xi_3)\lambda_3(\xi_1,\xi_2,\xi_3)\lambda_4(\xi_1,\xi_2,\xi_3)=\xi_1\xi_2 \xi_3 \\ \psi_{10}(\xi_1,\xi_2,\xi_3)=\lambda_1(\xi_1,\xi_2,\xi_3)\lambda_3(\xi_1,\xi_2,\xi_3)\lambda_4(\xi_1,\xi_2,\xi_3)=(1-\xi_1-\xi_2-\xi_3) \xi_2\xi_3  \\ \psi_{11}(\xi_1,\xi_2,\xi_3)=\lambda_1(\xi_1,\xi_2,\xi_3)\lambda_2(\xi_1,\xi_2,\xi_3)\lambda_4(\xi_1,\xi_2,\xi_3)=(1-\xi_1-\xi_2-\xi_3)\xi_1 \xi_3  \) Przemnożenie przez siebie trzech funkcji wierzchołkowych związanych z pierwszym, drugim i trzecim wierzchołkiem,  \( \lambda_1 \),  \( \lambda_3 \)  i  \( \lambda_3 \) daje nam możliwość zdefiniowana funkcji - wielomianu trzeciego stopnia   \( \psi_8 \) - rozpiętej na ścianie pomiędzy tymi wierzchołkami.

Podobnie przemnożenie przez siebie trzech funkcji wierzchołkowych związanych z drugim, trzecim i czwartym wierzchołkiem,  \( \lambda_1, \lambda_3, \lambda_4 \) daje nam możliwość zdefiniowana funkcji - wielomianu trzeciego stopnia  \( \psi_9 \)-  rozpiętej na ścianie pomiędzy tymi wierzchołkami. Przemnożenie przez siebie trzech funkcji wierzchołkowych związanych z pierwszym, trzecim i czwartym wierzchołkiem,  \( \lambda_1, \lambda_3, \lambda_4 \) daje nam możliwość zdefiniowana funkcji - wielomianu trzeciego stopnia  \( \psi_10 \) - rozpiętej na ścianie pomiędzy tymi wierzchołkami.

W końcu przemnożenie przez siebie trzech funkcji wierzchołkowych związanych z pierwszym, drugim i czwartym wierzchołkiem,  \( \lambda_1, \lambda_2, \lambda_4 \) daje nam możliwość zdefiniowana funkcji - wielomianu trzeciego stopnia   \( \psi_11 \)-  rozpiętej na ścianie pomiedzy tymi wierzchołkami. Ostatnią funkcją bazową do zdefiniowania jest funkcja związana z wnętrzem elementu. Można ją uzyskać przemnażając przez siebie cztery funkcje bazowe wierzchołkowe  \( \psi_{12}(\xi_1,\xi_2,\xi_3)=\lambda_1(\xi_1,\xi_2,\xi_3)\lambda_2(\xi_1,\xi_2,\xi_3)\lambda_3(\xi_1,\xi_2,\xi_3)\lambda_4(\xi_1,\xi_2,\xi_3)=(1-\xi_1-\xi_2-\xi_3)\xi_1 \xi_2 \xi_3 \)


Implementacja w MATLABie generacji funkcji bazowych na podstawie wektora węzłów

W module tym prezentujemy kod w MATLABie obliczający i rysujący funkcje bazowe B-spline na podstawie zadanego wektora węzłów. Możliwym alternatywnym darmowym środowiskiem do uruchomienia kodu jest Octave.

Po zainstalowaniu Octave otwieramy plik (pobierz kod lub zob. Załącznik 2 ). Uruchomienie kodu rozpoczyna się od procedury spline(), która ustawia przykładowy wektor węzłów knot_vector=[1,1,1,2,3,3,4,5,6,7,7,7], następnie oblicza liczbę funkcji bazowych używając procedury compute_nr_basis_functions(), oblicza stopień funkcji bazowych (jednorodny dla całego wektora węzłów) używając procedury compute_p, oraz wektor punktów x, w których policzone zostaną funkcje bazowe używając procedury mesh().

Kod generuje wartości funkcji bazowych w tych punktach, używając procedury compute_spline() i następnie rysuje funkcje używając w pętli komendy plot. Możliwa jest modyfikacja wektora węzłów w celu generacji różnych baz.


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

Transport ciepła za pomocą izogeometrycznej metody elementów skończonych

W poprzednim rozdziale wprowadziliśmy sformułowanie metody elementów skończonych, bazując na przykładzie przybliżenia bitmapy za pomocą funkcji B-spline. Nasze równanie wyglądało więc w wersji, którą nazwijmy sformułowaniem silnym w sposób następujący  \(  u(x,y) \approx BITMAP(x,y)  \) gdzie  \(  u(x,y)  \) oznaczało przybliżenie bitmapy za pomocą kombinacji liniowej funkcji B-spline  \(  u(x,y)=\sum_{i=1,...,N_x;j=1,...,N_y} u_{i,j } B^x_i(x)B^y_j(y) \), a  \(  BITMAP(x,y) \) oznaczało mapę bitową.

Następnie pokazaliśmy, że nasze sformułowanie silne przerobić można za pomocą mechanizmu uśredniania na postać zwaną sformułowaniem słabym lub sformułowaniem wariacyjnym:  \(  \int_{\Omega} u(x,y) v(x,y) dxdy= \int_{\Omega} BITMAP(x,y) v(x,y) dxdy \)

W sformułowaniu słabym nasze sformułowanie silne uśredniamy w wybranych fragmentach obszaru bitmapy. W tym celu przemnażamy sformułowanie silne przez tak zwane funkcje testujące, które określają sposób liczenia średniej (na całkowujemy przykład jeśli użyjemy do tego celu funkcji B-spline, to dostaniemy rozkłady zbliżone kształtem do rozkładów Gaussa, czyli wartości ze środka obszaru będą miały największe znaczenie, a obszary coraz bardziej oddalone od punktu środkowego będą miały coraz mniejszy wpływ aż do zera) oraz całkujemy całe równanie (pamiętając o tym, że całka intuicyjnie reprezentuje próbkowanie w poszczególnych punktach z uwzględnieniem wartości i pola próbkowanego obszaru, i sumowanie wyniku).

W module tym podamy inne przykłady zastosowań metody elementów skończonych, w których zamiast projekcji bitmapy obliczać będziemy rozwiązania różnych równań różniczkowych cząstkowych, przybliżających różne zjawiska fizyczne. W taki sam sposób jak przekształciliśmy nasze silne sformułowanie problemu projekcji bitmapy na sformułowanie słabe całkując i przemnażając przez funkcje uśredniające, tak samo zrobimy z różnymi równaniami różniczkowymi.


Transport ciepła za pomocą tradycyjnej metody elementów skończonych

W module tym podam przykład zastosowania tradycyjnej metody elementów skończonych pracującej na siatkach nieregularnych do rozwiązania problemu transportu ciepła w obszarze w kształcie litery L, opisanym w module Transport ciepła za pomocą izogeometrycznej metody elementów skończonych. Główna różnica polega na zastosowaniu elementów trójkątnych oraz wielomianów Lagrange'a rozpiętych na elementach trójkątnych. Z punktu widzenia faktoryzacji układów równań, tradycyjna metoda elementów skończonych generuje układ równań, który posiada więcej niewiadomych niż metoda izogeometryczna. Ponadto na granicy elementów skończonych wielomiany Lagrange'a stosowane w tradycyjnej metodzie elementów skończonych są jedynie klasy  \( C^0 \), co oznacza, że jak policzymy pochodne z rozwiązania to na krawędziach elementów pochodne te będą nieciągłe.

Metoda izogeometryczna daje bardziej "smukłe" i gładkie rozwiązania, dla których układy równań mają mniej niewiadomych, i generalnie koszt faktoryzacji jest niższy, szczególnie w przypadku gdy stosujemy tak zwaną poprawioną analizę izogeometryczną ("refined isogeometric analysis") i rozpinamy wielomiany B-spline na grupach elementów (po angielsku taka grupa elementów nazywa się "isogeometric patch"), natomiast na granicy grup elementów umieszczamy  \( C^0 \) separatory.

Algorytm metody elementów skończonych opisać można w sposób następujący

Zauważmy, że w algorytmie obliczamy całki po poszczególnych elementach z funkcjonału lewej i prawej strony. Innymi słowy w linii 6 liczymy  \(  L(i1)+= l(\psi^i_k)|_{E_k}=\int_{ \partial \Omega_N \cap E_k } g(x,y) v(x,y) dS  \) czyli całki po elemencie  \( E_k \) z funkcji  \( g(x,y) v(x,y) \) określonej na brzegu elementu   \( E_k \) przecinającego brzeg Neumanna. Musimy więc sprawdzić 3 krawędzie elementu   \( E_k \) oraz sprawdzić czy któraś z tych krawędzi nie leży na brzegu Neumanna, czyli czy   \( \partial E_k \cap \Gamma_N \neq \emptyset  \) gdzie  \(  \Gamma_N = \{(x,y):x=-1 \cup x=1 \cup y=1 \cup y=-1 \}  \). Jeśli fragment brzegu elementu leży na brzegu Neumanna, należy policzyć całkę używając jednowymiarowej kwadratury Gaussa na tym brzegu, tak jak opisano to w module "Sformułowanie wariacyjne a całkowanie numeryczne". Ponadto w linii 9 musimy policzyć całkę po elemencie z formy lewej strony  \(  B(i1,j1)+= a(\psi^i_k,\psi^j_k)|_{E_k} = \\ \int_{E_k} \left(\frac{\partial u }{\partial x }\frac{\partial v }{\partial x } + \frac{\partial u }{\partial y } \frac{ \partial v}{\partial y } \right)v(x,y) dxdy  \) Całkę tą należy policzyć, używając dwuwymiarowych kwadratur Gaussa na elemencie trójkątnym  \( E_k \). Po wygenerowaniu układu równań i zastosowaniu solwera dokładnego dostajemy rozwiązanie przedstawione na Rys. 4.


Równania konwekcji-dyfuzji

Przyglądnijmy się teraz innej klasie problemów obliczeniowych, tak zwanemu problemowi konwekcji-dyfuzji.  W problemie tym równanie różniczkowe w tak zwanej formie silnej dane jest wzorem  \(  \beta_x\frac{\partial u}{\partial x }+\beta_y\frac{\partial u }{\partial y }-\epsilon \left(\frac{\partial^2 u }{\partial x^2 }+\frac{\partial^2 u }{\partial y^2 }\right)=f(x,y)  \) Szukamy pola koncentracji, np. zanieczyszczeń  \(  \Omega\in (x,y) \rightarrow u(x,y) \in R  \) na obszarze  \(  \Omega  \). Wartość  \(  u(x,y)  \) oznacza koncentracje zanieczyszczeń w punkcie  \(  (x,y)  \). Człon  \(  \beta_x(x,y)\frac{\partial u }{\partial x }+\beta_y(x,y)\frac{\partial u }{\partial y }  \) oznacza tutaj adwekcje, czyli roznoszenie zanieczyszczeń przez wiatr. Składowe  \(  (\beta_x(x,y),\beta_y(x,y))  \) oznaczają składowe pola prędkości wiatru, czyli w punkcie  \(  (x,y)  \) wiatr wieje w kierunku równoległym do osi  \(  x  \) z prędkością  \(  \beta_x(x,y)  \) a w kierunku równoległym do osi  \(  y  \) z prędkością  \(  \beta_y(x,y)  \). Z drugiej strony człon  \(  \epsilon \left(\frac{\partial^2 u }{\partial x^2 }+\frac{\partial^2 u }{\partial y^2 }\right)  \) oznacza dyfuzję, czyli roznoszenie zanieczyszczeń na zasadzie dyfuzji. Jeśli zostanę zamknięty w pokoju z palaczem, to nawet jeśli nie będzieprzeciągu, dym papierosu dotrze do mojego nosa na zasadzie mechanizmu dyfuzji. Za prędkość tej dyfuzji odpowiada współczynnik  \(  \epsilon  \). Jeśli dodatkowo w pokoju było by otwarte okna i drzwi, to dym papierosowy byłby również popychany przez wiatr wiejący z prędkością na przykład  \(  (1,0)  \) (jeśli okno-drzwi leżą w płaszczyźnie równoległe do osi  \(  x  \) i przeciąg wieje z prędkością 1 m/s). Prawa strona, czyli funkcja  \(  f(x,y)  \) oznacza źródło zanieczyszczeń, czyli jeśli w punkcje  \(  (x,y)  \) znajduje się komin który emituje zanieczyszczenie, wówczas  \(  f(x,y)>0  \), jeśli natomiast w punkcie  \(  (x,y)  \) znajduje się zasysacz zanieczyszczeń (ostatnio takie zasysasze zanieczyszczeń konstrują Chińczycy w miastach) wówczas  \(  f(x,y)<0  \). Z koleji  \(  f(x,y)=0  \) oznacza że w punkcie  \(  (x,y)  \) nie pojawia się ani nie ubywa zanieczyszczeń. Zauważmy że człon dyfuzji zawiera Laplasjan, a człon adwekcji, iloczyn skalarny wektora  \(  \beta  \) i wektora prędkości wiatru. Możemy więc nasz problem przepisać tak:  \(  \beta(x,y) \cdot \nabla u(x,y) - \epsilon \Delta u(x,y) = f(x,y)  \) Sformułowanie słabe uzyskuje się w sposób następujący. Całkujemy i przemnażamy nasze równanie przez wybrane funkcje  \(  v(x,y) \) zwane funkcjami testującymi których używać będziemy do uśredniania naszego równania w obszarze na którym funkcje te są określone  \(   \int_{\Omega} \beta(x,y) \cdot \nabla u(x,y) -\int_{\Omega} \epsilon \Delta u (x,y) v(x,y) dxdy = \int_{\Omega} f(x,y) v(x,y) dxdy  \) W podobny sposób jak miało to miejsce w przypadku problemu projekcji bitmapy, każdy wybór funkcji testującej  \(  v(x,y)  \) służącej do uśredniania naszego problemu w obszarze w którym funkcja testująca jest określona, daje nam jedno równanie. Różne wybory funkcji testującej  \(  v(x,y)  \) dadzą nam więc różne równania . Nasz problem który chcemy rozwiązać wygląda więc tak: Szukamy koncentracji zanieczyszczeń, funkcji  \(  \Omega \ni (x,y) \leftarrow u(x,y) \in R  \) takiej że  \(  \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 } \frac{\partial^2 u(x,y) }{\partial x^2 }  v(x,y) dxdy -\int_{\Omega } \frac{\partial^2 u(x,y) }{\partial y^2 }  v(x,y) dxdy  = \\ \int_{\Omega } f(x,y) v(x,y) dxdy  \) dla dowolnych funkcji testujących  \(  \Omega \ni (x,y) \rightarrow v(x,y) \in R  \) (oczywiście nasza koncentracja zanieczyszczeń oraz funkcja testująca muszą być na tyle regularne, żeby dało się policzyć całki). Oznaczamy nasz problem: Znajdź  \( \Omega \ni (x,y) \rightarrow u(x,y) \in R  \) takie że  \(  a(u,v)=l(v)  \\ 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^2 u(x,y)}{\partial x^2 }  v(x,y) dxdy  -\int_{\Omega} \epsilon\frac{\partial^2 u(x,y)}{\partial y^2 }  v(x,y) dxdy \\ l(v) = \int_{\Omega} f(x,y) v(x,y) dxdy  \) Względem członu dyfuzji stosujemy wzór na całkowanie przez części, podobnie jak dla równań transportu ciepła. Robimy to dlatego żeby zredukować stopień pochodnych operujących na polu koncentracji zanieczyszczeń.  \(  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^2 u(x,y)}{\partial x^2 }  v(x,y) dxdy  -\int_{\Omega} \epsilon \frac{\partial^2 u(x,y)}{\partial y^2 }  v(x,y) dxdy \\ = \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 -\int_{\partial \Omega } \frac{\partial u }{\partial n } v dS  \) Zwyczajowo nasz problem adwekcji-dyfuzji, podobnie jak problem transportu ciepła, należy wyposażyć w warunki brzegowe. Dzielimy brzeg  \(  \partial \Omega = \Gamma_D \cup \Gamma_N  \) i wprowadzamy warunek brzegowy Dirichleta, na  \(  \Gamma_D  \) części brzegu  \(  \partial \Omega  \), który mówi mi o koncentracji zanieczyszczeń na brzegu obszaru  \(  u(x,y)=p(x,y) \textrm{ dla }(x,y) \in \Gamma_D  \) gdzie  \(  p(x,y)  \) to zadana pochodna koncentracji zanieczyszczeń w kierunku normalnym do brzegu obszaru. Ponadto wprowadzamy warunek brzegowy Neumanna, który opisuje nam prędkość zmian zanieczyszczeń na  \(  \Gamma_N  \) fragmencie brzegu  \(  \frac{\partial u(x,y)}{\partial n}=g(x,y) \textrm{ dla }(x,y) \in \Gamma_N  \) gdzie  \(  g(x,y)  \) to strumień stężenia zanieczyszczeń na brzegu. Równania konwekcji dyfuzji dla dużej różnicy wartości pomiędzy wektorem adwekcji a współczynnikiem dyfuzji (np. dwa rzędy wielkości lub więcej, czyli np.  \(   \beta=(1,0), \epsilon=10^{-2}  \)) wymagają specjalnych metod stabilizacji opisanych w rozdziale piątym. Dla małych różnic wartości tradycyjna lub izogeometryczna metoda elementów skończonych działa poprawnie.


Problem modelowy przepływu laminarnego (problem Stokesa)

Przyglądnijmy się teraz innej klasie problemów obliczeniowych, tak zwanemu problemowi Stokes'a. Równania te opisują przepływ cieczy [1], [2]. Spróbujmy najpierw zrozumieć sens tych rówńań. W jaki sposób można opisać przepływ cieczy na obszarze dwuwymiarowym? Załóżmy że szukamy rozwiązania problemu Stokesu (problemu przepływu cieczy) na obszarze w kształcie kwadratu  \(   \Omega = [0,1]^2  \). W każdym punkcie  \(  (x,y)\in \Omega \) naszego obszaru chcemy znać prędkość przepływu cieczy, która jest wektorem i każdemu punktowi przypisuje dwie współrzędne pola prędkości (prędkości w kierunku dwóch osi układu współrzędnych w tym punkcie)  \( \Omega \ni (x,y) \rightarrow {\bf u(x,y) }=(u_1(x,y),u_2(x,y))\in R^2  \) (gdzie pogrubione  \(  {\bf u }   \).  oznacza wektor dwóch składowych) oraz pole skalarne ciśnienia, które każdemu punktowi przypisuje jedną wartość ciśnienia w tym punkcie   \( \Omega \ni (x,y) \rightarrow p(x,y)\in R \). Mamy więc trzy niewiadome w każdym punkcie, dwie składowe prędkości oraz jedną składową ciśnienia. Potrzebujemy więc co najmniej trzech równań żeby rozwiązać nasz problem. Równanie Stokesa w ogólnej formie zapisuje się w postaci  \(  - \Delta {\bf u } + \nabla p = f  \). Pamiętając że pole prędkości ma dwie składowe, oraz przypominając sobie co oznacza Laplasjan oraz gradient, dostajemy dwa równania w tak zwanej formie silnej  \(  -\frac{\partial^2 u_1(x,y)}{\partial x^2} -\frac{\partial^2 u_1(x,y)}{\partial y^2} + \frac{\partial p(x,y)}{\partial x } = f_1(x,y)  \)  \(  -\frac{\partial^2 u_2(x,y)}{\partial x^2} -\frac{\partial^2 u_2(x,y)}{\partial y^2} + \frac{\partial p(x,y)}{\partial y } = f_2(x,y)  \) Dodatkowe trzecie równanie dostajemy zakładając pewne dodatkowe własności cieczy której przepływ chcemy obliczyć. Tradycyjnie, w równaniu Stokesa zakłada się że ciecz jest nieściśliwa, oraz że nie mamy źródeł ani ubytków cieczy w obszarze. Matematycznie zapisuje się ten warunek w postaci tak zwanej dywergencji, piszemy konkretnie że dywergencja jest zerowa  \(  \displaystyle{div} {\bf u}=0  \) gdzie  \( \displaystyle{div} {\bf u} = \nabla \cdot {\bf u} = \left( \frac{\partial }{\partial x}, \frac{\partial }{\partial y } \right) \cdot \left(u_1,u_2 \right) = \frac{\partial u_1(x,y)}{\partial x} +\frac{\partial u_2(x,y)}{\partial y } =0  \) Mamy więc następujący układ równań do rozwiązania  \(  -\frac{\partial^2 u_1}{\partial x^2} -\frac{\partial^2 u_1}{\partial y^2} + \frac{\partial p}{\partial x } = f_1  \)  \(  -\frac{\partial^2 u_2}{\partial x^2} -\frac{\partial^2 u_2}{\partial y^2} + \frac{\partial p}{\partial y } = f_2  \)  \(  \frac{\partial u_1}{\partial x} +\frac{\partial u_2}{\partial y } =0  \) Żeby rozwiązać równanie różniczkowe cząstkowe które spełnione jest na danym obszarze, musimy dodatkowo określić co dzieje się na brzegu tego obszaru. W ten sposób sprecyzujemy problem który chcemy rozwiązać. Każde inne warunki brzegowe dają nam inny problem i w konsekwencji inne rozwiązanie. W naszym przypadku wprowadzamy tak zwane warunki brzegowe Dirichleta na prędkość przepływu, które to mówią nam jakie są wartości pola prędkości na brzegu obszaru. Załóżmy że nasz obszar jest to zatoka na brzegu rzeki, która płynie jednostajnie z lewej strony na prawą, wzdłuż górnej granicy naszego obszaru  \(   \Omega  \). Zakładamy że prędkość przepływu na górnym brzegu obszaru wynosi  \(  {\bf u}(x,y) = (1,0) \textrm{ dla } x \in (0,1), y=1  \). Na pozostałej częśi brzegu zakładamy że prędkość przepływu wynosi zero  \(  {\bf u}(x,y) = (0,0) \textrm{ dla } x \in \{0,1\}, y\in (0,1) \cup x\in(0,1),y=0  \). Mamy więc określone warunki brzegowe definiujące wartości pola prędkości na brzegu obszaru. Warunki takie nazywamy warunkami brzegowymi Dirichleta. W problemie Stokesa obliczamy jeszcze ciśnienie, musimy więc przyjąć jakieś założenie o ciśnieniu. Przyjmujemy warunek iż sumarycznie ciśnienie na całym obszarze jest równoważone (ciśnienie próbkowane w różnych miejscach poprzez przemnożenie przez funkcję testującą oraz odcałkowanie, daje w sumie zero)  \(  \int_{\Omega} p(x,y) dxdy = 0  \). Możemy teraz skonstruować tak zwane sformułowanie słabe naszego problemu, które będzie nadawać się do symulacji metodą elementów skończonych. Sformułowanie słabe uzyskuje się w sposób następujący. Całkujemy i przemnażamy nasze równanie przez wybrane funkcje zwane funkcjami testującymi których używać będziemy do uśredniania naszego równania w obszarze na którym funkcje te są określone. Główna różnica w stosunku do problemu projekcji jest taka że mamy teraz układ trzech równań, potrzebujemy więc trzech funkcji testujących, po jednej do każdego równania, oznaczamy je   \(  v_1(x,y), v_2(x,y), q(x,y)  \). Przemnażamy równania i całkujemy po obszarze  \(  -\int_{\Omega} \frac{\partial^2 u_1(x,y)}{\partial x^2}v_1(x,y) dxdy -\int_{\Omega} \frac{\partial^2 u_1(x,y)}{\partial y^2}v_1(x,y) dxdy + \int_{\Omega} \frac{\partial p(x,y)}{\partial x }v_1(x,y) dxdy = \int_{\Omega}  f_1(x,y) v_1(x,y) dxdy \)  \(  -\int_{\Omega} \frac{\partial^2 u_2(x,y)}{\partial x^2}v_2(x,y) dxdy -\int_{\Omega} \frac{\partial^2 u_2(x,y)}{\partial y^2}v_2(x,y) dxdy + \int_{\Omega} \frac{\partial p(x,y)}{\partial y } v_2(x,y) dxdy= \int_{\Omega} f_2(x,y) v_2(x,y) dxdy \)  \(  \int_{\Omega} \frac{\partial u_1(x,y)}{\partial x}q(x,y) +\int_{\Omega} \frac{\partial u_2(x,y)}{\partial y }q(x,y) dxdy =0  \) W podobny sposób jak miało to miejsce w przypadku problemu projekcji bitmapy, każdy wybór funkcji testującej  \(  v(x,y)  \) służącej do uśredniania naszego problemu w obszarze w którym funkcja testująca jest określona, daje nam jedno równanie. Różne wybory funkcji testującej  \(  v(x,y) : v_1(x,y),v_2(x,y),q(x,y)  \) dadzą nam więc różne równania  \(  \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} dxdy +\int_{\partial \Omega} \frac{\partial u_1(x,y)}{\partial n} v_1(x,y) dS + \int_{\Omega} \frac{\partial p(x,y)}{\partial x }v_1(x,y) dxdy = \int_{\Omega}  f_1(x,y) v_1(x,y) dxdy \)  \(  \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 + \int_{\partial \Omega} \frac{\partial u_2(x,y)}{\partial n} v_2(x,y) dS + \int_{\Omega} \frac{\partial p(x,y)}{\partial y }v_1(x,y) dxdy = \int_{\Omega}  f_2(x,y) v_2(x,y) dxdy \)  \(  \int_{\Omega} \frac{\partial u_1(x,y)}{\partial x}q(x,y) +\int_{\Omega} \frac{\partial u_2(x,y)}{\partial y }q(x,y) dxdy =0  \) gdzie po całkowaniu przez części pojawiają się nowe człony - całki brzegowe liczone w kierunku prostopadłym do brzegu. W naszym problemie Stokesa związanym z obliczaniem przepływu w zatoce sąsiadującej z rzeką zakłada się że siły  \(  f_1(x,y)=f_2(x,y)=0  \). W warunku brzegowym mamy założone że prędkość na górnej krawędzi wynosi  \(  {\bf u}(x,y) = (1,0) \textrm{ dla } x \in (0,1), y=1  \). Innymi słowy wprowadzamy tutaj warunek brzegowy Dirichleta na prędkość przepływu. Ze sformułowaniem tym wiąże się jeden techniczny problem. Otóż pole przepływu na górnym brzegu jest niezerowe (skierowane w prawo) natomiast na bocznych brzegach (na lewym i prawym brzegu) pole przepływu przyjmujemy równe zero. Mamy więc przeskok z zera do jeden na pierwszej współrzędnej pola prędkości. Przeskok taki po pierwsze nie jest fizyczny (nie może być tak że woda w jednym punkcie nie płynie a zaraz obok płynie całkiem szybko, przejście z "nie płynięcia" do "płynięcia" odbywa się w gładki regularny sposób. Po drugie, jeśli chcielibyśmy rozwiązanie numeryczne przybliżać za pomocą metody elementów skończonych, rozwiązanie takie jest przedstawiane jako kombinacja liniowa wielomianów ciągłych. Nie ma takich wielomianów które przeskakują z zera do jeden. Postawienie problemu w taki sposób spowoduje powstanie osobliwości numerycznych w lewym górnym i prawym górnym brzegu obszaru. Żeby tego uniknąć, w tym przypadku wykonuje się następujący trick. Przyjmuje się  \( g(x,y) = 1 \textrm{ dla } x \in (\epsilon, 1-\epsilon), y=1  \)  \(  g(x,y) = \frac{x}{\epsilon} \textrm{ dla } x \in (0,\epsilon), y=1  \)  \(  g(x,y) = \frac{(1-x)}{\epsilon} \textrm{ dla } x \in (1-\epsilon, 1), y=1  \)  \(  {\bf u}(x,y)=(g(x,y),0)  \textrm{ dla } x \in (0,1), y=1  \) Wówczas faktycznie w rogach  obszaru mamy zerową prędkość i nie ma osobliwości numerycznych w obliczeniach metodą elementów skończonych. Dokonujemy następnie tak zwanego "przesunięcia" naszego problemu. Poszukiwane rozwiązanie dzielimy na dwie składowe  \( {\bf u}(x,y) = (h(x,y)+w(x,y),u_2(x,y)) \) gdzie  \(  h(x,y)=g(x,y)(1-y)  \) jest tak zwanym rozszerzeniem naszego niezerowego warunku brzegowego Dirichleta na cały obszar  \(  \Omega  \). Naszego przesunięcia dokonujemy więc tak naprawdę jedynie względem pierwszej składowej pola prędkości (ponieważ druga składowa jest równa zero). Wówczas pierwsze równanie zmienia się  \(  \int_{\Omega} \frac{\partial (h(x,y)+w(x,y))}{\partial x}\frac{\partial v_1(x,y)}{\partial x } dxdy +\int_{\Omega} \frac{\partial (h(x,y)+w(x,y))}{\partial y}\frac{\partial v_1(x,y)}{\partial y} dxdy +\int_{\partial \Omega} \frac{\partial (h(x,y)+w(x,y))}{\partial n} v_1(x,y) dS + \int_{\Omega} \frac{\partial p(x,y)}{\partial x }v_1(x,y) dxdy = 0  \) Człon z zadaną funkcją  \(  h(x,y)   \) jest znany i możemy przenieść go na prawą stronę  \(  \int_{\Omega} \frac{\partial w(x,y)}{\partial x}\frac{\partial v_1(x,y)}{\partial x } dxdy +\int_{\Omega} \frac{\partial w(x,y)}{\partial y}\frac{\partial v_1(x,y)}{\partial y} dxdy +\int_{\partial \Omega} \frac{\partial w(x,y)}{\partial n} v_1(x,y) dS + \int_{\Omega} \frac{\partial p(x,y)}{\partial x }v_1(x,y) dxdy =  \\ = \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_1(x,y)}{\partial y} dxdy +\int_{\partial \Omega} \frac{\partial h(x,y)}{\partial n } v_1(x,y) dS   \) Wszystkie człony znajdujące się po prawej stronie potrafimy obliczyć. Z koleji niewiadomą  \(  w(x,y)  \) wprowadzoną na lewej stronie możemy z powrotem oznaczyć jako  \(  u_1(x,y)  \) musimy jednak pamiętać po rozwiązaniu naszego problemu Stokesa że należy do niej później dodać z powrotem człon z rozszerzeniem warunku brzegowego. Wprowadzamy oznaczenia na naszą lewą i prawą stronę  \(  a({\bf u}, {\bf v}) =\int_{\Omega} \nabla {\bf u} : \nabla {\bf v}dxdy  \) gdzie  \(  :  \) oznacza sumowanie wszystkich składowych  \(  =\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   \) Nasz problem można teraz zapisać w następującej postaci  \(   a({\bf u},{\bf v}) + b(p,{\bf v }) = f({\bf v})  \quad \forall {\bf v}  \)  \(  b(q,{\bf u } ) = 0 \quad \forall q   \) Zauważmy że zastąpiliśmy układ trzech równań układem dwóch równań. Nasze dwa równania są "zakodowane" w pierwszym równaniu. Teraz, możemy przyjąć funkcje testujące  \(  {\bf v }=(v_1,0)   \) żeby dostać pierwsze równanie, oraz   \(  {\bf v }=(0,v_2)   \) żeby dostać drugie równanie. Mając w ten sposób opisany nasz problem, możemy teraz przyjąć że nasze poszukiwane rozwiązanie  \(  ({\bf u },p)= (u_1(x,y),u_2(x,y),p(x,y))  \) ma człony prędkości równe zero na brzegu  \(  u_1(x,y)=u_2(x,y)=0\textrm{ dla }(x,y)\textrm{ na }\partial \Omega \). Podobnie możemy przyjąć że nasze funkcje testujące, są również równe zero na brzegu \(  v_1(x,y)=v_2(x,y)=0\textrm{ dla }(x,y)\textrm{ na }\partial \Omega \) Określamy teraz funkcje używane do aproksymacji rozwiązania oraz do testowania. Precyzujemy jak wyglądać będzie nasza grupa 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]. 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]. Przyjmujemy teraz naszą aproksymację pól prędkości  \(  u_1(x,y) \approx \sum_{i,j=1,...,6} u_1^{i,j } B_{i,2}(x)B_{j,2}(y)  \)  \(  u_2(x,y) \approx \sum_{i,j=1,...,6} u_2^{i,j } B_{i,2}(x)B_{j,2}(y)  \) oraz ciśnienia  \(  p(x,y) \approx \sum_{i,j=1,...,6} p^{i,j } B_{i,2}(x)B_{j,2}(y)  \) Do testowania używamy również kombinacji liniowych funkcji B-spline  \(  v_1(x,y) \approx \sum_{k,l=1,...,6} v_1^{k,l } B_{k,2}(x)B_{l,2}(y)  \)  \(  v_2(x,y) \approx \sum_{k,l=1,...,6} v_2^{k,l } B_{k,2}(x)B_{l,2}(y)  \) oraz ciśnienia  \(  q(x,y) \approx \sum_{k,l=1,...,6} q^{k,l } B_{k,2}(x)B_{l,2}(y)  \) Przyjmujemy teraz najpierw funkcje bazowe  \(  ({\bf v },q) = (v_1,0,0)=(B_{k,2}(x)B_{l,2}(y),0,0) \textrm{ dla }k=l,...,6   \) oraz wstawiamy do równania . Dostajemy macierze  \(  	A1_{i,j=1,...,6;k,l=1,...,6}  \int_{\Omega} \frac{B_{i,2}(x)}{\partial x}B_{j,2}(y)\frac{\partial B_{k,2}(x) }{\partial x}B_{l,2 }(y)dxdy + \int_{\Omega} B_{i,2}(x)\frac{\partial B_{j,2}(y)}{\partial y}B_{k,2}(x)\frac{\partial B_{l,2}(y)}{\partial y }dxdy   \)  \(  	B1_{i,j=1,...,6;k,l=1,...,6} = \int_{\Omega} \frac{\partial B_{i,2}(x) }{\partial x } B_{j,2}(y) B_{k,2}(x)B_{l,2}(y)  d,6)   \) Następnie przyjmujemy funkcje bazowe  \(  ({\bf v },q) = (0,v_1,0)=(0,B_{k,2}(x)B_{l,2}(y),0)  \textrm{ dla }k=l,...,6  \) oraz wstawiamy do równania . Dostajemy macierze  \(  	A2_{i,j=1,...,6;k,l=1,...,6}=\int_{\Omega} \frac{\partial B_{i,2}(x)}{\partial x} B_{j,2}(y)\frac{\partial B_{k,2}(x)}{\partial x}B_{l,2}(y)dxdy + \int_{\Omega} B_{i,2}(x)\frac{\partial B_{j,2}(y)}{\partial y }B_{k,2}(y)\frac{\partial B_{l,2}(x)}{\partial y }dxdy  \)  \(  	B2_{i,j=1,...,6;k,l=1,...,6} =\int_{\Omega} B_{i,2}(x)B_{j,2}(y) B_{k,2}(x)\frac{\partial B_{l,2}(y)}{\partial y }  dxdy \) Dodatkowo przyjmując funkcje bazowe  \(  ({\bf v },q) = (0,0,q)=(0,0,B_{k,2}(x)B_{l,2}(y))  \textrm{ dla }k=l,...,6  \) oraz wstawiamy do równania . Dostajemy macierze  \(  Q1_{i,j=1,...,6;k,l=1,...,6} =\int_{\Omega } \frac{\partial B_{i,2}(x)}{\partial x} B_{j,2}(y) B_{k,2}(x) B_{l,2}(y)  dxdy \)  \(  Q2_{i,j=1,...,6;k,l=1,...,6} =\int_{\Omega} B_{i,2}(x)\frac{\partial B_{j,2}(y)}{\partial y}B_{k,2 }(x)B_{l,2 }(y)  dxdy   \) oraz prawą stronę  \(  F1_{k,l=1,...,6}= \int_{\Omega} \frac{\partial h(x,y)}{\partial x}\frac{\partial B_{k,2}(x)}{\partial x } B_{l,2}(y)dxdy +\int_{\partial \Omega} \frac{\partial h(x,y)}{\partial n } B_{k,2}(x)B_{l,2}(y)dS   \)  \(  F2_{k,l=1,...,6}= \int_{\Omega} \frac{\partial h(x,y)}{\partial y}B_{k,2}(x)\frac{\partial B_{l,2}(y)}{\partial y} dxdy +\int_{\partial \Omega} \frac{\partial h(x,y)}{\partial n } B_{k,2}(x)B_{l,2}(y)dS   \) Nasz układ równań wygląda następująco  \(  \left( \begin{matrix} A1 & 0 & B1  \\ 0 & A2 &  B2 \\ Q1 & Q2 & 0 \end{matrix} \right) \left( \begin{matrix} u_1 \\ u_2 \\ p \end{matrix} \right) =\left( \begin{matrix} F1 \\ F2 \\ 0  \end{matrix} \right)  \) Dokładne rozwiązanie równania Stokesa wymaga dodatkowej stabilizacji równania. W kolejnych modułach omówione zostaną dwie metody stabilizacji, jedna oparta na metodzie minimalizacji reziduum, druga oparta na metodzie DG (Discontinuous Galerkin).