Obliczenia z użyciem programu MATLAB
Transkrypt
Obliczenia z użyciem programu MATLAB
Obliczenia z użyciem programu MATLAB Skrypt powstał w ramach prac Centrum Korepetycji Koła Naukowego Studentów Politechniki „Gambrinus”. www.gambrinus.pwr.wroc.pl Autorzy: Karol Białowąs – rozdziały 1,2,4,7 Anna Borowska – rozdział 3 Paweł Góra – rozdział 5 Natalia Gemza - rozdział 6 Spis treści: 1. Podstawy, proste obliczenia ....................................................................... 1 1.1 Interfejs ........................................................................................................ 1 1.2 Proste obliczenia .......................................................................................... 2 1.3 Wbudowane funkcje .................................................................................... 2 1.4 Zmienne ....................................................................................................... 3 1.5 Przydatne polecenia ..................................................................................... 4 1.6 Przenoszenie danych z arkusza kalkulacyjnego do Matlaba ....................... 5 2. Macierze i wektory ..................................................................................... 6 2.1 Sposoby definiowania macierzy i wektorów ............................................... 6 2.2 Dostęp do elementów macierzy/wektorów .................................................. 7 2.3 Operacje na wybranym wierszu/kolumnie ................................................... 9 2.4 Podstawowe operacje rachunku macierzy ................................................... 10 2.5 Rozwiązywanie układów równań liniowych metodą macierzy odwrotnej .. 11 3. Wielomiany ................................................................................................ 12 3.1 Obliczanie wartości wielomianu (polyval) .................................................. 12 3.2 Obliczanie pierwiastków wielomianu (roots) .............................................. 12 3.3 Mnożenie i dzielenie wielomianów (conv i deconv) ................................... 13 3.4 Pochodna wielomianu (polyder) .................................................................. 13 3.5 Aproksymacja wielomianowa (polyfit) ....................................................... 14 4. Funkcje i skrypty ........................................................................................ 15 4.1 Skrypty ........................................................................................................ 15 4.2 Funkcje ........................................................................................................ 17 4.3 Zmienne globalne ........................................................................................ 21 4.4 Funkcja jako argument innej funkcji ........................................................... 22 4.5 Znajdowanie miejsc zerowych dowolnej funkcji (fzero) ............................ 23 5. Pętle i instrukcje warunkowe ..................................................................... 25 5.1 Pętla for ....................................................................................................... 25 5.2 Pętla while ................................................................................................... 26 5.3 Instrukcja warunkowa if .............................................................................. 28 6. Grafika w Matlabie .................................................................................... 6.1 Funkcja plot ................................................................................................. 31 6.2 Edycja wykresu ............................................................................................ 31 31 6.3 Hold on ........................................................................................................ 35 6.4 Subplot ......................................................................................................... 36 6.5 Interpolacja .................................................................................................. 37 6.6 Funkcja fplot ................................................................................................ 38 7. Całkowanie ................................................................................................ 7.1 Funkcja trapz ............................................................................................... 40 7.2 Funkcja ode45 ............................................................................................. 42 7.3 Funkcja ode45 – całkowanie układów równań ........................................... 45 7.4 Zdarzenia (Events) ...................................................................................... 46 40 1. Podstawy, proste obliczenia Matlab to zaawansowane środowisko do obliczeń matematycznych. Udostępnia ogromną ilość specjalistycznych funkcji oraz język skryptowy pozwalający nawet tworzyć programy z GUI (graficznym interfejsem użytkownika). W kursie tym postaramy się przybliżyć niewielką część najpotrzebniejszych inżynierowi – chemikowi funkcji programu. 1.1 Interfejs Zacznijmy od przyjrzenia się interfejsowi. Okno po uruchomieniu programu Matlab. Elementy, które będą nas interesować wyróżniono czerwonym kolorem. 1 – przycisk uruchamiający edytor M-plików 2 – przycisk uruchamiający Simulink 3 – Command Window 4 – okno Workspace O dwóch pierwszych pozycjach więcej powiemy w dalszych częściach kursu. Na razie korzystać będziemy jedynie z: ● Command Window – okno, w którym możemy bezpośrednio wpisywać polecenia i w którym zobaczymy wyniki ich wykonania ● Workspace – w tym oknie znajdują się zmienne utworzone w czasie aktualnej sesji 1 1.2 Proste obliczenia Pierwsza operacja jaką wykonamy to zwykłe dodawanie. Wpiszmy w oknie Command Window: >> 3+2 i wciśnijmy enter. Powinniśmy zobaczyć następujący wynik: >> 3+2 ans = 5 >> Zwróć uwagę, że w oknie Workspace pojawiła się zmienna o nazwie ans i wartości 5. Do zmiennej tej będziemy mogli się później odwoływać. Analogicznie możemy obliczać wartości wszystkich innych wyrażeń algebraicznych. Spróbuj teraz samodzielnie wykonać następujące operacje i sprawdź ich wyniki: 3-2 3*2 3/2 3^2 Ostatnia z tych operacji to podnoszenie do potęgi. Spróbujmy teraz obliczyć wartość nieco bardziej skomplikowanego wyrażenia: 2.3∗e 3.46∗10−3 1.23−3 4.2 >> (2.3*exp(3.4) + 6e-3)/(1.23 - 3*sqrt(4.2)) ans = -14.0140 Zauważ, że w Matlabie mozesz stosować nawiasy. Kolejność wykonywania działań jest standardowa, tj. potęgowanie, następnie mnożenie/dzielenie itd., z uwględnieniem nawiasów. W przykładzie tym zwróć uwagę na to jak zapisano wyrażenie 6*10-3 - 6e-3. Jest to tak zwany zapis wykładniczy. Warto pamiętać że liczby można w matlabie zapisywać właśnie w ten sposób. 1.3 Wbudowane funkcje Kolejna istotna rzecz pokazana w tym przykładzie to funkcje. exp(3.4) oraz sqrt(4.2) Łatwo domyślić się co one oznaczają – exp to funkcja eksponent czyli „e do x” a sqrt to pierwiastek kwadratowy z liczby. Matlab udostępnia jeszcze wiele funkcji. Kilkanaście często używanych zebrano w tabeli poniżej. Funkcja Co oblicza: exp(x) e^x sqrt(x) Pierwiastek kwadratowy log(x) Logarytm naturalny 2 log10(x) Logarytm dziesiętny abs(x) Wartość bezwzględna sin(x), cos(x), tan(x) Wartości funkcji trygonometrycznych (x – w radianach) asin(x), acos(x, atan(x) Funkcje cyklometryczne, wynik w radianach deg2rad(x), rad2deg(x) Przeliczanie odpowiednio stopni na radiany (deg2rad) i radianów na stopnie(rad2deg) sinh(x),cosh(x),tanh(x) Funkcje hiperboliczne mod(x,y) Reszta z dzielenia x przez y round(x) Zaokrąglanie do liczby całkowitej floor(x) Zaokrąglanie do najbliższej liczby całkowitej w górę ceil(x) Zaokrąglanie do najbliższej liczby całkowitej w dół sum(X) Oblicza sumę wszystkich elementów macierzy/wektora min(X), max(X) Wartość najmniejszego/największego elementu macierzy/wektora Spróbuj wykonać kilka działań używając każdej z tych funkcji. W jednym z kolejnych rozdziałów nauczysz się także pisać własne funkcje. 1.4 Zmienne Podczas obliczeń w Matlabie możemy również wykorzystywać zmienne. Oto przykład ich użycia: >> a=3 a = 3 >> b=2 b = 2 >> a+b ans = 5 >> c=log(a) c = 1.0986 >> d=b^c d = 2.1415 Zmienne pozwalają zapamiętywać wartości pod postacią różnych symboli. Nie muszą to być pojedyncze litery, mogą to być praktycznie dowolne ciągi znaków – ważne żeby znaki w nazwie nie były rozdzielone spacją. Po wykonaniu powyższego ciągu poleceń przyjrzyj się zawartości okna Workspace. Powinny się w nim pojawić nazwy i wartości naszych zmiennych. Ze zmiennych będziemy korzystać do samego końca kursu zatem jeszcze zdążysz się z nimi oswoić. Na razie przydadzą Ci się one jedynie podczas wykonywania dłuższych ciągów obliczeń, gdy z danej wartości musisz skorzystać wielokrotnie. Zmienne przydadzą Ci się również podczas obliczania skomplikowanych wyrażeń. Czasem łatwiej rozbić takie wyrażenie na kilka części i każdą z nich obliczyć oddzielnie – w krotkich wzorach łatwiej uniknąć błędu. Oto prosty przykład wykorzystania zmiennych w tym właśnie celu: 3 Spróbujmy obliczyć wartość wyrażenia: 3e2.1 2⋅ ⋅ 2−ln 2.8 2 3 2⋅sin 2 ⋅e 2 3.2⋅ln 2.2 3 2⋅sin 2 2 >> a=(3+exp(2.1))/(2-log(2.8)) a = 11.5070 >> b=(2*sin(3/2*pi))/sqrt(2) b = -1.4142 >> c=exp(3.2*log(2.2)/b) c = 0.1680 >> wynik=2*a*b^2*c wynik = 7.7305 >> Powyższe wyrażenie bez użycia zmiennych do przechowywania wyników obliczeń pośrednich wygląda tak: >> wynik=2*(3+exp(2.1))/(2-log(2.8))*((2*sin(3/2*pi))/sqrt(2))^2* (exp(3.2*log(2.2)/((2*sin(3/2*pi))/sqrt(2)))) wynik = 7.7305 >> Przy takim zapisie znacznie łatwiej o błąd który może być trudny do znalezienia, np.: >> wynik=2*(3+exp(2.1)/(2-log(2.8)))*((2*sin(3/2*pi))/sqrt(2))^2* (exp(3.2*log(2.2)/((2*sin(3/2*pi))/sqrt(2)))) wynik = 7.6690 1.5 Przydatne polecenia Na koniec jeszcze kilka przydatnych poleceń/cech Matlab-a o których warto wiedzieć. Historia poleceń: wciskając klawisz strzałki w górę w oknie Command Window cofamy się do wykonywanych wcześniej operacji i możemy je powtórzyć lub lekko zmodyfikować i wykonać tę zmodyfikowaną wersję. clc – komenda, przy pomocy której czyścimy zawartość okna Command Window who – wypisanie w oknie Command Window wszystkich utworzonych zmiennych ; - wyniki poleceń gdy linię zakończymy średnikiem nie będą wyświetlane, np.: >> a=3+2; >> a=3+2 a = 5 4 będzie to przydatne później – przy pisaniu własnych funkcji. format long/short – format wyświetlania liczb rzeczywistych, np.: >> a=sqrt(2); >> a a = 1.4142 >> format long >> a a = 1.414213562373095 >> format short >> a a = 1.4142 clear – usuwa wszystkie zmienne utworzone w danej sesji clear nazwa_zmiennej – usuwa wybraną zmienną 1.6 Przenoszenie danych z arkusza kalkulacyjnego do Matlab-a Aby przenieść dane z większości popularnych arkuszy kalkulacyjnych do programu Matlab, wystarczy te dane zaznaczyć i po kliknięciu prawego klawisza myszki wybrać opcję kopiuj, następnie w matlabie należy utworzyć tablicę, do której skopiujemy dane: >> tab1=[] tab1 = [] Nazwa tablicy pojawi się w okienku Workspace - tam należy kliknąć na nią dwukrotnie. Obok powinna pojawić się wtedy tabela w której możemy w prosty sposób edytować zawartość tablicy. Należy wtedy kliknąć prawym przyciskiem na komórkę tabeli i z rozwijanego meu wybrać paste aby wkleić dane skopiowane wcześniej z arkusza. Kopiowanie danych z arkusza kalkulacyjnego do Matlab-a 5 2. Macierze i wektory Warto wiedzieć, że nazwa programu Matlab pochodzi od Matrix laboratory. Zgodnie z tym co nazwa sugeruje, Matlab umożliwia bardzo proste wykonanie wielu operacji na macierzach i wektorach. W zasadzie macierze i wektory stanowią typ danych, którym najczęściej będziesz się posługiwał. 2.1 Sposoby definiowania macierzy i wektorów Definiowanie macierzy w Matlabie jest bardzo proste: >> A=[1 2 3 4;5 6 7 8;9 10 11 12] A = 1 2 3 4 5 6 7 8 9 10 11 12 >> Spacja oddziela kolejne wyrazy w wierszu, natomiast przejście do nowej linii lub średnik oznaczają rozpoczęcie kolejnego wiersza. Jeszcze jeden przykład: >> B=[1 2 3 4 5 6 7 8] B = 1 3 5 7 >> 2 4 6 8 Istnieją jeszcze trzy bardzo przydatne sposoby tworzenia wektorów (macierzy posiadających tylko jeden wiersz lub tylko jedną kolumnę) w programie Matlab. Tymi sposobami otrzymamy wektory zawierające wartości z określonego przedziału w odstępach równych, lub w postaci ciągu arytmetycznego. Sposób pierwszy prezentuje poniższy przykład: >> C=[0:10:50] C = 0 10 >> D=[0:5:23] D = 0 5 >> 20 30 40 10 15 20 50 Ogólnie możemy to zapisać jako [początek : odstęp : wartość maksymalna] Zauważ, że do wektora trafiają tylko wyrazy mniejsze lub równe wartości maksymalnej. 6 Dwa kolejne sposoby tworzenia wektora to funkcje linspace i logspace. >> linspace(1,50,5) ans = 1.0000 13.2500 >> 25.5000 37.7500 50.0000 pierwszy i drugi argument funkcji linspace to wartości pierwszego i ostatniego elementu wektora, ostatni – liczba elementów wektora. Elementy wektora tworzonego funkcją linspace są od siebie równo oddalone. Funkcja logspace generuje wektor w którym kolejne elementy to wartości 10^x. Najlepiej zilustrować działanie tej funkcji przykładem: >> logspace(-2,2,5) ans = 0.0100 0.1000 >> 1.0000 10.0000 100.0000 Otrzymaliśmy wektor zawierający 5 wartości, od 10^-2 do 10^2. Wartości to kolejno 10^-2, 10^-1, 10^0 itd. Przydatne polecenia do tworzenia macierzy to ones(m), eye(m), zeros(m) oraz te same funkcje wywoływane z dwoma argumentami, tj.: ones(m,n) itd.... Funkcje te tworzą macierze kwadratowe o wymiarze m gdy wywolamy je z jednym argumentem lub macierze posiadajace m wierszy i n kolumn jeśli wywołamy je z dwoma argumentami. Spróbuj użyć każdej z tych funkcji w obu formach aby zobaczyć jakie macierze powstają w wyniku ich działania. 2.2 Podstawowe operacje rachunku macierzy Najprostsze operacje rachunku macierzy to dodawanie i odejmowanie macierzy. Macierze muszą mieć ten sam wymiar. Dodawanie i odejmowanie macierzy wygląda dokładnie tak samo jak dodawanie i odejmowanie od siebie dwu zmiennych. Oto przykład dodawania macierzy: >> A=[1 2 3;4 5 6] A = 1 2 3 4 5 6 >> B=[1 2 3;7 8 9] B = 1 2 3 7 8 9 >> C=A+B C = 2 4 6 11 13 15 >> Analogicznie wygląda odejmowanie macierzy (spróbuj odjąć od siebie macierze A i B). Mnożenie macierzy przez liczbę jest równie proste: >> A=[1 2 3;4 5 6] A = 1 2 3 4 5 6 7 >> A*2 ans = 2 8 >> 4 10 6 12 Mnożenie macierzy i podnoszenie jej do potęgi jest już nieco bardziej skomplikowane. Mamy tu do wyboru dwie opcje. Albo mnożymy macierze zgodnie z zasadami rachunku macierzy albo mnożymy/dzielimy przez siebie odpowiadające sobie elementy macierzy. W poniższej tabeli podano odpowiednie przykłady. Najpierw zdefiniujemy dwie macierze: >> A=[1 2 3;4 5 6;7 8 9] A = 1 2 3 4 5 6 7 8 9 >> B=[11 12 13;14 15 16;17 18 19] B = 11 12 13 14 15 16 17 18 19 >> Wyniki wykonania komend z lewej kolumny znajdują się w prawej kolumnie tabeli. >> A*B 90 96 102 216 231 246 342 366 390 >> A.*B 11 24 39 56 75 96 119 144 171 >> A./B 0.0909 0.1667 0.2308 0.2857 0.3333 0.3750 0.4118 0.4444 0.4737 Zatem zapis bez kropki poprzedzającej symbol mnożenia/dzielenia oznacza zwykłe mnożenie/dzielenie macierzy, natomiast w przypadku użycia operatora .* lub ./ wymnażane/dzielone przez siebie są odpowiadające sobie elementy macierzy. Analogicznie wygląda potęgowanie. W wypadku użycia operatora ^, Matlab wykonuje zwykle mnożenie macierzy, natomiast .^ oznacza podniesienie każdego elementu macierzy do danej potęgi. Aby lepiej zrozumieć różnicę, przyjżyj się poniższemu przykładowi: >> A=[2 2 A = 2 2 2 >> B=A^3 B = 72 72 72 >> C=A.^3 C = 8 8 8 2; 2 2 2; 2 2 2] 2 2 2 2 2 2 72 72 72 72 72 72 8 8 8 8 8 8 8 Matlab umożliwia także proste wykonanie operacji takich jak transpozycja macierzy, wyliczenie wyznacznika, wartości własnych oraz wyznaczenie macierzy odwrotnej. Aby otrzymać macierz transponowaną, należy użyć operatora ' : >> A=[1 1 1;2 2 2;3 3 3] A = 1 1 1 2 2 2 3 3 3 >> B=A' B = 1 2 3 1 2 3 1 2 3 >> Pozostałe operacje: ● ● ● inv(X) – wyznaczenie macierzy odwrotnej do macierzy X det(X) – obliczenie wyznacznika macierzy X eig(X) – wyznaczenie wartości własnch macierzy X 2.3 Dostęp do elementów macierzy/wektorów Na początek utwórzmy przykładową macierz wielkości 7x7: >> A=magic(7) A = 30 39 38 47 46 6 5 14 13 15 21 23 22 31 >> 48 7 8 16 24 32 40 1 9 17 25 33 41 49 10 18 26 34 42 43 2 19 27 35 36 44 3 11 28 29 37 45 4 12 20 W przykładzie poszliśmy trochę na skróty – komenda magic tworzy macierz kwadratową o zadanym wymiarze, w której sumy wszystkich wierszy i kolumn są równe i w której nie powtarza się żaden element. Odczytanie pojedynczego elementu: >> A(1,1) ans = 30 >> A(4,5) ans = 34 >> Jako pierwszy argument podajemy wiersz, jako drugi kolumnę, wiersze i kolumny macierzy numerowane są od 1. 9 Odczytanie wybranego fragmentu macierzy wykonujemy tak: >> A(2,2:5) ans = 47 7 9 18 Operator : pozwala zdefiniować zakres interesujących nas elementów. Tu odczytaliśmy elementy od 2 do 5 z wiersza 2. Operatora dostępu : możemy użyć również do odczytania całych wierszy/kolumn – wtedy nie podajemy zakresu jaki chcemy odczytać a jedynie wstawiamy sam operator :, tak jak w poniższych przykładach: >> A(:,2) ans = 39 47 6 14 15 23 31 >> A(2,:) ans = 38 47 >> 7 9 18 27 29 Jak się łatwo domyślić A(:,:) oznacza całą zawartość macierzy A. 2.4 Operacje na wybranym wierszu/kolumnie Zajmiemy się teraz operacjami w których chcemy wykonać działania tylko na wybranym wierszu lub wybranej kolumnie macierzy, takimi jak: mnożenie wiersza/kolumny przez liczbę, dodawanie wierszy/kolumn, usuwanie wierszy/kolumn >> A=magic(7); >> A(3,:)=2*A(3,:) A = 30 39 48 1 38 47 7 9 92 12 16 34 5 14 16 25 13 15 24 33 21 23 32 41 22 31 40 49 >> A(4,:)=A(4,:)-A(3,:) A = 30 39 48 1 38 47 7 9 92 12 16 34 -87 2 0 -9 13 15 24 33 21 23 32 41 22 31 40 49 >> 10 18 52 34 42 43 2 19 27 70 36 44 3 11 28 29 74 45 4 12 20 10 18 52 -18 42 43 2 19 27 70 -34 44 3 11 28 29 74 -29 4 12 20 W powyższym przykładzie najpierw pomnożono wiersz 3 macierzy przez 2 a następnie od wiersza 4 odjęto wiersz 3. Wybrany wiersz/kolumnę macierzy usuniemy w następujący sposób: 10 >> A(:,3)=[] A = 30 39 38 47 92 12 -87 2 13 15 21 23 22 31 >> 1 9 34 -9 33 41 49 10 18 52 -18 42 43 2 19 27 70 -34 44 3 11 28 29 74 -29 4 12 20 W przykładzie tym usunęliśmy kolumnę 3 macierzy będącej wynikiem poprrzednich dwu operacji. 2.5 Rozwiązywanie układów równań liniowych metodą macierzy odwrotnej przykład Na koniec rozwiążemy jeszcze prosty układ równań wykorzystując metodę macierzy odwrotnej: x−2y3z=−7 3x y4z=5 2x5yz=18 Układ zapiszemy w postaci macierzowej: A⋅X=B 1 −2 3 x −7 3 1 4⋅y = 5 2 5 1 z 18 Rozwiązanie ukłądu znajdziemy dzięki własnościom macierzy odwrotnej: −1 A⋅X=B⇒ X =A ⋅B Następnie zdefiniujmy w Matlabie odpowiednie macierze A i B oraz wyznaczymy rozwiązanie układu: >> A=[1 -2 3;3 1 4;2 5 1] A = 1 -2 3 3 1 4 2 5 1 >> B=[-7;5;18] B = -7 5 18 >> X=inv(A)*B X = 2.0000 3.0000 -1.0000 Zatem rozwiązanie układu równań to: x=2, y=3 i z=-1. 11 3. Wielomiany Wielomiany w matlabie przechowywane są w postaci wierszowego wektora współczynników. Pierwszy wyraz wektora zawiera współczynnik przy najwyższej potędze zmiennej niezależnej, kolejne wyrazy to współczynniki przy kolejnych, coraz niższych potęgach zmiennej, ostatni to wyraz wolny wielomianu. Dla przykładu wielomian: Wprowadzamy do matlaba w postaci wektora: >> p=[3,2,-1] 3.1 Obliczanie wartości wielomianu (polyval): Funkcja polyval(p,x) oblicza wartość wielomianu o współczynnikach zawartych w wektorze p w punktach wyspecyfikowanych w zmiennej x. Dla przykładu posłużono się powyższym wielomianem p(x): >> p=[3,2,-1] p = 3 2 -1 >> polyval(p,1) ans = 4 Oczywiście można od razu wprowadzić kilka zmiennych x w postaci wektora: >> polyval(p,[1,3,0,7]) ans = 4 32 -1 160 3.2 Obliczanie pierwiastków wielomianu (roots): Funkcja roots pozwala obliczyć pierwiastki wielomianu. Przykład: >> roots(p) ans = -1.0000 0.3333 Oczywiście Matlab oblicza także pierwiastki zespolone: >> q=[3,1,0,-1]; >> roots(q) ans = -0.4658 + 0.5834i -0.4658 - 0.5834i 0.5982 12 3.3 Mnożenie i dzielenie wielomianów (conv i deconv): Funkcja conv pozwala obliczyć wektor współczynników wielomianu będącego iloczynem wielomianów reprezentowanych przez jej argumenty, tj: >> p=[1,1]; >> q=[1,2,2]; >> c=conv(p,q) c = 1 3 4 2 Funkcja deconv pozwala obliczyc iloraz oraz resztę z dzielenia jednego wielomianu przez drugi: >> p=[2,2,2,1]; >> q=[1,2]; >> [w,r]=deconv(p,q) w = 2 -2 6 r = 0 0 0 -11 3.4 Pochodna wielomianu (polyder): Funkcja polder(p) oblicza wektor współczynników wielomianu będącego pochodną wielomianu reprezentowanego przez p. >> p=[3,2,-1]; >> polyder(p) ans = 6 2 Można także obliczyć pochodną iloczynu wielomianów – po prawej stronie znaku równości znajduje się pojedyncza zmienna: >> p=[3,2,-1]; >> q=[4,0,-1,1]; >> k=polyder(p,q) k = 60 32 -21 2 3 Powyższy wynik można uzyskać stosując znaną już funkcję conv: >> p=[3,2,-1]; >> q=[4,0,-1,1]; >> polyder(conv(p,q)) ans = 60 32 -21 2 3 13 Oczywiście w łatwy sposób można obliczyć pochodną ilorazu wektorów. Składnia tej funkcji jest taka sama jak w przypadku funkcji deconv – po prawej stronie znaku równości znajduje się tablica z dwiema zmiennymi: >> p=[3,2,-1]; >> q=[4,0,-1,1]; >> [w,r]=polyder(q,p) w = 12 16 -9 -6 r = 9 12 -2 -1 -4 1 gdzie: w – wynik dzielenia wielomianów, r – reszta z dzielenia. 3.5 Aproksymacja wielomianowa (polyfit): Główną funkcją wykorzystywaną przy aproksymacji wielomianowej jest polyfit. Oblicza ona współczynniki wielomianu zadanego stopnia aproksymującego dane wejściowe w sensie minimum sumy kwadratów. Składnia funkcji: >> p=polyfit(x,y,n) x,y – wektory zawierające dane n – stopień wielomianu jaki chcemy otrzymać >> x=[1 3 5 7 9 11]; >> y=[1 13 17 23 31 39]; >> p=polyfit(x,y,3) p = 0.0463 -0.8512 7.8505 >> x2=1:0.5:11; >> y2=polyval(p, x2); >> plot(x,y,'x',x2,y2) -5.5853 14 15 4. Funkcje i skrypty Matlab posiada własny, rozbudowany język skryptowy pozwalający łatwo tworzyć zaawansowane programy. W tym rozdziale omówimy krótko tworzenie prostych skryptów i funkcji. Skrypt to ciąg poleceń zapisany w pliku, które po uruchomieniu skryptu zostają kolejno przez Matlab-a wykonane. Z funkcji korzystałeś już wielokrotnie zatem pewnie wiesz już dobrze co to jest i do czego służy. Skrypty i funkcje zapisywane są w postaci tzw. m-plików, czyli plików z rozszerzeniem .m. W przypadku funkcji, każda funkcja musi być zdefiniowana w oddzielnym mpliku o nazwie takiej, jak nazwa danej funkcji. Aby otworzyć edytor plików m wybierz z menu: File → New → M-File Powinien zostać uruchomiony edytor (u Ciebie może wyglądać trochę inaczej): Okno edytora m-plików 4.1 Skrypty Napiszmy skrypt obliczający czas wypływu cieczy z cylindrycznego zbiornika. Jako dane potrzebne będą (w nawiasach podano przyjęte w skrypcie wartości): D – średnica zbiornika (3m) h – wysokość do której zbiornik jest napełniony (4m) d – średnica otworu, przez który wypływa ciecz (100mm = 0.1m) µ - współczynnik wypływu (0.8) g – przyspieszenie ziemskie (9.81 m/s2) Całkowity czas wypływu ze zbiornika w sekundach obliczymy ze wzoru: t wyp = 2⋅D2 ⋅ h u⋅d 2 2g 16 W edytorze m-plików wprowadź następującą treść: D=3 h=4 d=0.1 u=0.8 g=9.81 t_wyp=(2*D^2)/(u*d^2*sqrt(2*g))*sqrt(h) Teraz z menu edytora m-plików wybieramy Debug → Safe file and Run (lub podobną opcję – nazwy mogą się nieznacznie różnić w zależności od wersji programu Matlab). Zostaniesz zapytany pod jaką nazwą zapisać skrypt – nazwij skrypt t_wyplywu. Po zapisaniu skrypt zostanie uchomiony. Przejdź do okna Command Window. Tam znajdziesz wyniki obliczeń: D = h = d = u = g = 3 4 0.1000 0.8000 9.8100 t_wyp = 1.0159e+03 >> Inna metoda wywołania skryptu to wpisanie jego nazwy w wierszu poleceń. Teraz wróć do edytora i zmień wysokość cieczy w zbiorniku na 2 m. Ponownie wybierz z menu Debug opcję Safe file and Run. Otrzymasz czas opróżniania zbiornika dla nowych danych. Tym razem program nie powinien pytać o nazwę skryptu (skrypt zostanie ponownie zapisany pod podaną wcześniej nazwą). Wynik powinien być następujący: D = h = d = u = 3 2 0.1000 0.8000 g = 9.8100 t_wyp = 718.3697 >> Spróbuj jeszcze dodać znak średnika (;) na końcu pierwszych pięciu linii: D=3; h=4; d=0.1; u=0.8; g=9.81; t_wyp=(2*D^2)/(u*d^2*sqrt(2*g))*sqrt(h) 17 Sprawdź jak wtedy będzie wyglądał wynik uruchomienia skryptu. Jeśli nie wiesz dlaczego tak jest, wróć do części 1.5 pierwszego rozdziału kursu – tam dowiesz się czym skutkuje zakończenie linii średnikiem. 4.2 Funkcje Funkcjom poświęcimy trochę więcej uwagi. Będziesz je wykorzystywał i pisał bardzo często podczas pracy z programem Matlab. Własne funkcje będziesz pisał na przykład podczas numerycznego obliczania całek. Z menu (edytora m-plików lub Matlaba) wybierz: File → New → M-File Funkcja, którą stworzymy na początek będzie obliczać objętość molową gazu doskonałego: R⋅T p⋅v=R⋅T ⇒ v p , T = p function wynik=v(p,T) %funkcja oblicza objętość molową [m^3/mol] %p - ciśnienie [Pa] %T - temperatura [K] R=8.314; %[J/(mol*K)] wynik=R*T/p; end Teraz zapisujemy nasz plik. Plik musi nazywać się dokładnie tak samo jak funkcja – czyli w naszym przypadku będzie to v.m i taką właśnie nazwę zaproponuje nam Matlab. Zapamiętaj, w jakim dokładnie folderze zapisujesz plik. Teraz przejdź do okna Command Window i wpisz: >> v(101300,273.15) ans = 0.0224 >> Obliczyliśmy w ten sposób objętość, zajmowaną przez mol gazu doskonałego w warunkach normalnych – 22.4 dm3 czyli 0.0224m3. Jeśli otrzymałeś komunikat: >> v(101300,273.15) ??? Undefined function or method 'v' for input arguments of type 'double'. >> Nie przejmuj się. Prawdopodobnie folder w którym zapisałeś swoją funkcję nie znajduje się na liście folderów w których Matlab szuka funkcji (tzw. Path). Musisz ten folder do ścieżki dodać. Z menu w głównym oknie programu wybierz: File → Set Path... W oknie, które się pojawi wybierz Add Folder... a następnie wybierz folder, w którym zapisałeś swoją funkcję. Teraz wszystko powinno działać. Przyjrzyjmy się teraz bardziej szczegółowo napisanej przez nas funkcji. 18 function wynik=v(p,T) Plik z definicją funkcji powinien się zaczynać słowem kluczowym function – informuje ono Matlab-a, że zaraz zdefiniujemy funkcję. Po słowie kluczowym function podajemy nazwy zmiennych, których wartości funkcja zwróci. W naszym przypadku jest to pojedyncza zmienna o nazwie wynik ale może to być również wektor lub macierz (przykłady w dalszej części rozdziału). Następnie znak równości i nazwa funkcji, po której w nawiasie znajduje się lista argumentów przyjmowanych przez funkcję (u nas są to p i T). %funkcja oblicza objętość molową [m^3/mol] %p - ciśnienie [Pa] %T - temperatura [K] W kolejnych trzech linijkach zaczynających się od znaku % umieszczono komentarze mówiące co funkcja liczy oraz w jakich jednostkach podawać agumenty wejściowe. Linie zaczynające się od znaku % są ignorowane przez Matlab-a, możemy w nich wpisać co chcemy – zwykle właśnie jednostki lub jakiś krótki opis działania znajdującego się w kolejnych linijkach kodu. Komentarze takie nie są konieczne do działania funkcji ale warto je pisać, żeby potem nie mieć wątpliwości co dokładnie robi nasza funkcja i jakich parametrów od nas oczekuje. R=8.314; %[J/(mol*K)] wynik=R*T/p; end Dalej podobnie jak w skrypcie piszemy ciąg poleceń, które Matlab ma wykonać, w tej części obliczamy wartości które funkcja ma zwrócić – my w tej części definiujemy zmienną R – uniwersalną stałą gazową oraz obliczamy wynik – molową objętość gazu. W obliczeniach wykorzystujemy przekazane do funkcji argumenty (w naszym przykładzie argumenty to p i T). Definiowanie funkcji powinniśmy kończyć słowem kluczowym end. Poniżej przykład funkcji, zwracającej kilka wartości: function [f g]=fg(x) %funkcja oblicza wartości dwu funkcji : %f(x)=2x+2 %g(x)=x^2+2x+1 f=2*x+2; g=x^2+2*x+1; end >> [j k]=fg(2) j = 6 k = 9 >> a=fg(2); >> Przyjżyj się ostatniemu wydanemu poleceniu – jak myślisz, co powinno się znaleźć w zmiennej a? Pewnie spodziewasz się, że a będzie wektorem zawierającym wartości 6 i 9, czyli: [6 9] Nie. Zmienna a będzie miała wartość 6. Zostanie pod nią podstawiona pierwsza wartość z wektora zwracanego przez funkcję. Pamiętaj o tym – dzięki temu unikniesz wielu błędów. Kolejnym często 19 popełnianym błędem jest używanie w funkcjach operatorów mnożenia i potęgowania macierzowego zamiast operatorów wykonujących te operacje „element po elemencie”. Aby zilostrować ten problem, stwórzmy kolejną funkcję – podobną do fg(x): function [f g]=fg1(x) %funkcja oblicza wartości dwu funkcji : %f(x)=2x+2 %g(x)=x^2+2x+1 f=2.*x+2; g=x.^2_2.*x+1; end Różnica między fg i fg1 polega wyłącznie na tym, że operatory mnożenia i potęgowania macierzowego (* i ^) zastąpiono w funkcji fg1 operatorami mnożenia i potęgowania „element po elemencie” (.^, .*). Wykonanie poniższych poleceń pozwoli nam zobaczyć różnice między obiema funkcjami. >> fg1(2) ans = 6 >> [j k]=fg1(2) j = 6 k = 9 >> x=[1:1:5] x = 1 2 3 4 >> [j k]=fg(x) ??? Error using ==> mpower Matrix must be square. Error in ==> fg at 6 g=x^2+2*x+1; >> [j k]=fg1(x) j = 4 6 8 10 k = 4 9 16 25 >> 5 12 36 Jak widać dla pojedynczego argumentu funkcje fg i fg1 dają dokładnie takie same rezultaty. W kolejnej operacji tworzymy wektor x. Chcemy obliczyć wartości funkcji f(x) i g(x) dla każdego elementu wektora. W przypadku użycia funkcji fg otrzymujemy błąd: ??? Error using ==> mpower Matrix must be square. Error in ==> fg at 6 g=x^2+2*x+1; Błąd wynika z tego, że chcemy podnosić do potęgi macierz, która nie jest kwadratową, co zgodnie z definicją potęgowania macierzy jest operacją niedozwoloną. Funkcja fg1(x) daje pożądany rezultat – w wektorach k i j znajdują się odpowiednio wartości funkcji f i g dla kolejnych liczb w wektorze x. Podobnie w przypadku operatorów mnożenia i dzielenia: 20 function z=f(x,y) z=y*x; end function z=f1(x,y) z=y.*x; end >> fun1(1,2) ans = 2 >>fun(1,2) ans = 2 >> fun1(x,y) ans = 0 0.5000 2.0000 4.5000 >> fun(x,y) ??? Error using ==> mtimes Inner matrix dimensions must agree. Error in ==> fun at 2 z=y*x; >> 8.0000 W funkcjach zwykle powinniśmy używać operatorów wykonujących operacje „element po elemencie”, zatem zapamiętaj aby operatorów macierzowych (czyli tych normalnych, „bez kropki”, których poza Matlabem używamy praktycznie zawsze) używać tylko wtedy kiedy naprawdę chodzi nam o wykonanie operacji potęgowania/mnożenia/dzielenia macierzy. Poniżej znajduje się prosty przykład pokazujący działanie średnika na końcu linii wewnątrz funkcji. Zmodfikuj funkcję fg1(x), usuwając średnik z końca linii, w której liczona jest wartość g(x): function [f g]=fg1(x) %funkcja oblicza wartości dwu funkcji : %f(x)=2x+2 %g(x)=x^2+2x+1 f=2.*x+2; g=x.^2_2.*x+1 end >> x=[1:1:5] x = 1 2 >> k=fg1(x) g = 4 9 k = 4 6 >> k=fg1(x); g = 4 9 >> 3 4 5 16 25 36 8 10 12 16 25 36 Zwróć uwagę, że gdy linijka w funkcji nie jest zakończona średnikiem, wynik przeprowadzonej w niej operacji zawsze zostanie wyświetlony (zwróć uwagę na ostanie wywołanie). Spróbuj usunąć średnik również z linii, w której liczona jest wartość funkcji f(x). 21 4.3 Zmienne globalne Oprócz argumentów, różne wartości możemy przekazywać do funkcji także jako zmienne globalne. Nie jest to „elegancki” sposób i raczej powinno się go unikać, jednak czasami sposób ten pozwala znacznie uprościć skrypty i funkcje. Na początek utwórz funkcję: function fn1(k) k=k+1 m=m+2 end Następnie wykonaj następujące polecenia: >> j=4 j = 4 >> m=3 m = 3 >> fn1(j) k = 5 ??? Undefined function or variable 'm'. Error in ==> fn1 at 3 m >> j j = 4 >> Tworzymy dwie zmienne – j oraz m. Następnie wywołujemy funkcję, przekazując jej jako argument m. W funkcji do argumentu dodawana jest liczba jeden ale zauważ, że po zakończeniu działania funkcji wartość j pozostała niezmieniona. Funkcja zgłasza również błąd – nie zdefiniowano zmiennej m. Teraz zmodyfikuj funkcję fn1: function fn1(k) global m k=k+1 m=m+1 end A następnie wydaj następujące polecenia w oknie Command Window: >> clear >> global m >> j=3 j = 3 >> m=4 m = 4 >> fn1(j) k = 4 m = 5 >> m 22 m = 5 >> j j = 3 Teraz zmienną m definiujemy jako globalną. Pamiętaj, że aby użyć jakiejś zmiennej jako globalnej musisz zdeklarować ją jako globalną zarówno w swoim skrypcie/w Command Window jak też w każdej funkcji, która z niej korzysta. Następnie tworzymy zmienną j o wartości 3 oraz zmiennej globalnej m nadajemy wartość 4. Wywołujemy funkcję fn1 – podobnie jak poprzednio. Zauważ, że w wyniku działania funkcji wartość zmiennej m zmieniła się. Zatem kolejną rzeczą o której musisz pamiętać jeśli używasz zmiennych jest to, że po zakończeniu działania funkcji operującej na takich zmiennych ich wartości nie powracają automatycznie do stanu sprzed wywołania funkcji. 4.4 Funkcja jako argument innej funkcji Przekazanie funkcji jako argumentu do innej funkcji jest bardzo przydatną możliwością. Skorzystasz z niej np. podczas całkowania numerycznego funkcji. Stworz następujące pliki z funkcjami: function y=f1(x) y=2*x.^2+3; end function y=f2(x) y=2*exp(x+1); end function y=f3(x) y=2*x+1; end Teraz stworzymy przykładową funkcję, do której będziemy przekazywać nasze funkcje jako argumenty. Funkcja ta zwróci różnicę wartości funkcji prekazanej jako fn_1 i funkcji przekazanej jako fn_2 dla argumentu x. function y=diff_fun(fn_1,fn_2,x) y=fn_1(x)-fn_2(x); end Następnie przejdź do okna Command Window i spróbuj wykonać następujące operacje: >>format long >> x=[1:1:10] x = 1 2 3 4 5 6 7 8 9 10 >> diff_fun(@f1,@f2,x) ans = 1.0e+05 * Columns 1 through 3 -0.000097781121979 -0.000291710738464 -0.000881963000663 Columns 4 through 6 -0.002618263182052 -0.007538575869855 -0.021182663168569 Columns 7 through 9 -0.058609159740835 -0.160751678551508 -0.438879315896134 23 Column 10 -1.195452834303956 >> f1(1)-f2(1) ans = -9.778112197861301 >> -0.000097788e5 ans = -9.778800000000000 >> diff_fun(f2,f3,x) ??? Input argument "x" Error in ==> f2 at 2 y=2*exp(x+1); >> diff_fun(@f2,@f3,x) ans = 1.0e+05 * Columns 1 through 3 0.000117781121979 Columns 4 through 6 0.002878263182052 Columns 7 through 9 0.059469159740835 Column 10 1.197272834303956 >> is undefined. 0.000351710738464 0.001021963000663 0.007958575869855 0.021802663168569 0.161891678551508 0.440339315896134 Zwróć uwagę na sposób wywołania funkcji, do której jako parametry przekazujemy inne funkcje. diff_fun(@f1,@f2,x) Przed nazwą przekazywanej funkcji musimy umieścić znak @. Jest to operator zwracający tzw. uchwyt funkcji, wystarczy jednak, żebyś zapamiętał że gdy przekazujesz jako argument funkcję, przed jej nazwą powinienneś umieścić znak @ - inaczej możesz otrzymać błędy, takie jak np. ten: >> diff_fun(f2,f3,x) ??? Input argument "x" is undefined. Error in ==> f2 at 2 y=2*exp(x+1); Unikniesz ich stawiając znak @ przed nazwą funkcji przekazywanej jako argument do innej funkcji. 4.5 Znajdowanie miejsc zerowych dowolnej funkcji (fzero) Jednym z przykładów funkcji do których przekazujesz jako argument własną funkcję, jest wbuowana w Matlab-a fzero. Pozwala ona znaleźć punkt, w którym Twoja funkcja zwraca wartość zero. Funkcję tę można zastosować do funkcji jednej zmiennej. Jej wywołanie wygląda tak: x_szukane = fzero(@fn,x0) pod zmienną x_szukane podstawiony zostanie wynik – x, przy którym wartość zwracana przez funkcję fn jest równa zero. x0 to punkt, od którego należy rozpocząć poszukiwania. Miejsce zerowe powinno znaleźć się w okolicy tego punktu. Jeśli wiemy na przykład do jakiego przedziału ma należeć poszukiwana wartość – możemy jako x0 podać punkt, będący środkiem tego przedziału. Działanie funkcji polega na znalezieniu w okolicy punktu x0 przedziału, na którego końcach 24 wartości zwracane przez funkcję mają różne znaki a następnie dokładne zlokalizowanie miejsca zerowego w tym przedziale. Rozwiążemy następujący problem: Mając dane równanie: 2 = 11.17⋅1−x x ⋅ 1−x 1−1.17⋅x Należy znaleźć wartość x z przedziału (0;1), dla której =1.76 . Aby móc użyć funkcji fzero, musimy przekształcić równanie do postaci: 2 0= 11.17⋅1−x x ⋅ − 1−x 1−1.17⋅x Tworzymy funkcję: function y=f4_5(x) a=x./(1-x); b=(1+1.17*(1-x))./(1-1.17*x); y=a.^2*b – 1.76; end Zapisujemy ją i przechodzimy do okna CommandWindow. Tam wydajemy komendę: >> x=fzero(@f4_5,0.5) x = 0.4217 >> Otrzymujemy punkt, w którym wartość funkcji f4_5 jest równa zero. 25 5. Pętle i instrukcje warunkowe 5.1 Pętla for Załóżmy, że chcemy wypełnić jednowymiarową tablicę liczbami od 1 do 10. Wystarczy więc, że zastosujemy następujący fragment kodu: >> A(1) = 1; >> A(2) = 2; >> A(3) = 3; i tak dalej aż do 10. Nieco prostszym i efektywniejszym sposobem jest np. zastosowanie następującego kodu: >> A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; Wciąż jednak nie jest to jednak metoda najefektywniejsza. Gdybyśmy musieli wypełnić tablicę, która posiada 1000 pozycji, jej wypełnianie byłoby dosyć męczące. Tutaj pojawia się możliwość zastosowania pętli. Działanie pętli można określić w bardzo prosty sposób: powtarza pewien fragment programu określoną ilość razy. Działanie pętli przybliżę teraz przykładem: function f = fun(x) f = x^2 +12*x -3; Tworzymy skrypt o nazwie run.m: k=5; for a=1:k fun(a) end Następnie w oknie Command Window wydajemy polecenie: >> run Naciskamy enter i na ekranie powinien pojawić się następujący kod: ans = 10 ans = 25 ans = 42 ans = 61 ans = 82 Teraz wyjaśnienia: Mamy funkcję daną następującym wzorem: 26 f(x) = x2 + 12x – 3 Chcemy obliczyć wartość tej funkcji dla x od 1 do 5 i użyć do tego celu programu Matlab. W tym celu: ● tworzymy plik „fun.m”, w tym pliku umieszczamy funkcję, która następnie będzie wywoływana dla kolejnych wartości „x” ● tworzymy plik „run.m”, ten plik jest plikiem wykonawczym i w nim umieszczamy pętlę ● w „command window” wpisujemy „run” i otrzymujemy wyniki działania kodu Ponieważ zakładam, że czytelnik posiadł już umiejętność pisania własnych funkcji, przejdę do omówienia punktu, w którym tworzymy plik „run.m”. W 1. linijce kodu definiujemy zmienną „k” i przypisujemy jej wartość „5”. W 3. linijce kodu rozpoczyna się pętla „for”. Budowa pętli for jest następująca: n= 1; k= 5; for a = n:k „code” end Pętla zaczyna się słowem kluczowym „for”. Następnie podawany jest warunek działania pętli. Pętla będzie się wykonywać k-n+1 razy. W wyżej wymienionym przypadku będzie to 5 razy, ale za k i n można podstawić dowolne liczby naturalne oraz 0. W miejscu gdzie znajduje się wyraz „code”, umieszcza się wszystkie instrukcje, które mają być powtarzane. W rozpatrywanym wyżej przykładzie w miejscu „code” umieszczone jest wywołanie funkcji od zmiennej „a”. Zmienna „a”, nazywana też licznikiem pętli, w trakcie działania pętli zmienia swoją wartość w zakresie od n do k z krokiem równym „1”. Tak więc za każdym razem gdy zostanie wywołana nasza funkcja, wartość zmiennej „a” będzie o „1” większa. Na końcu znajduje się słowo kluczowe „end”, które kończy pętlę for. Wyjaśniłem już na przykładzie podstawy działania pętli for. Teraz pora na bardziej szczegółowe omówienie. Pętla zawsze wykonuje się określoną ilość razy. Ilość ta zawsze znana jest przed definicją. Kolejne iteracje pętli są zależne od spełnienia warunku ilości wykonanych iteracji a nie warunku logicznego, co ma miejsce w przypadku pętli „while". Zmienna, która jest licznikiem pętli (w naszym przykładzie zmienna „a”), w każdej iteracji zmienia się o „1”. 5.2 Pętla while Pętla while podobnie jak pętla for służy do wielokrotnego powtarzania kodu. W odróżnieniu od pętli for ilość iteracji nie jest znana w momencie pisania kodu. Dla zobrazowania działania pętli posłużymy się poprzednim przykładem. Za pomocą pętli while będziemy obliczać wartość funkcji: f(x) = x2 + 12x – 3 w tym celu piszemy kod: function f = fun(x) f = x^2 +12*x -3; 27 oraz tworzymy skrypt run2.m n=1; k=5; a=1; while a<=k fun(a) a=a+1; end I uruchamiamy go, wpisując w oknie Command Window: >> run2 wyniki: ans = 10 ans = 25 ans = 42 ans = 61 ans = 82 Jak widać zastosowanie pętli while w tej postaci daje ten sam efekt jak pętla for. W tym jednak przypadku powinniśmy zastosować pętlę while do innych zadań. Dobrym przykładem będzie zastosowanie pętli while do obliczenia po ilu iteracjach wartość funkcji osiągnie lub przekroczy oczekiwaną wartość. Przyjmijmy, że wartością graniczną w naszym przykładzie będzie z=1000. Teraz piszemy kod: function f = fun(x) f = x^2 +12*x -3; tworzymy skrypt run3.m z=1000; a=1; y=0; while y>=z y = fun(a) a=a+1; end a I uruchamiamy go wpisując w oknie Command Window: >> run3 wyniki: a = 28 28 Otrzymana wartość „28” oznacza, że funkcja osiągnęła lub przekroczyła wartość „1000” po 28 iteracjach, czyli nasza funkcja f(x) dla x=28 przyjmuje wartość: f(28) >= 1000. 5.3 Instrukcja warunkowa if Instrukcje warunkowe, podobnie jak pętle, spotyka się we wszystkich językach programowania. Stosuje się je w miejscach, w których chcemy w zależności od wartości jakieś zmiennej podjąć jedno z kilku działań jakie opisaliśmy w programie. Dobrą analogią do instrukcji warunkowej jest skrzyżowanie, na którym możemy skręcić w prawo lub lewo. Najprostszym przykładem instrukcji warunkowej jest taka konstrukcja: Skrypt przyklad.m if w_ktora_strone == 'w prawo' ulica = 'ul. Zielona' end Następnie przechodzimy do Command Window: >> w_ktora_strona = 'w prawo'; >> przyklad ulica = ul. Zielona W przykładzie stworzyliśmy plik przyklad.m , w którym umieszczona została instrukcja warunkowa. Następnie po zapisaniu pliku definiujemy zmienną „w_ktora_strona” w command window. Po nadaniu jej wartości wywołujemy plik z instrukcją warunkową. W tym przypadku warunek został spełniony i wykonany został kod znajdujący się wewnątrz instrukcji. Oprócz prostych konstrukcji, jak ta wyżej, można tworzyć bardzo rozbudowane wyrażenia z wieloma opcjami. Aby to lepiej zobrazować zmodyfikujemy nasz przykład ze skrzyżowaniem. Załóżmy, że chcemy jakoś uregulować ruch na skrzyżowaniu czterech ulic. Ponieważ nie chcemy, żeby w każdą ulicę mógł wjechać dowolny samochód musimy postawić jakieś znaki, które pewnym pojazdom zabraniałyby wjazdu na daną ulicę. Określiliśmy, że będziemy się kierować 3 kryteriami - masą pojazdu - wysokością pojazdu - czy właściciel jest mieszkańcem osiedla W tym celu tworzymy 3 zmienne: masa [ton] wysokosc [m] wlasciciel [tak/nie] Teraz modyfikujemy nasz plik przyklad.m do takiej postaci: if (masa < 3.5)&(wysokosc < 5) wynik = 'moze jechac w prawo' 29 elseif (masa < 3.5)&(wlasciciel == 'tak') wynik = 'moze jechac w lewo' elseif wysokosc < 3.5 wynik = 'moze jechac prosto' else wynik = 'nie moze jechac w zadnym kierunku' end Następnie przechodzimy do okna Command Window: >> >> >> >> masa = 3; wysokosc = 3.5; wlasciciel = 'tak'; przyklad wynik = moze jechac w prawo >> masa = 2.5; >> wysokosc = 1.8; >> wlasciciel = 'nie'; >> przyklad wynik = moze jechac w prawo >> masa = 5; >> wysokosc = 4; >> wlasciciel = 'nie'; >> przyklad wynik = nie moze jechac w zadnym kierunku Jak widać za pomocą bardziej złożonych instrukcji warunkowych możemy znacznie precyzyjniej określać jaki kod ma zostać wykonany w zależności od grupy zmiennych. Jest to bardzo przydatne przy pisaniu bardziej złożonych algorytmów. Dobrym przykładem jest np. dokładność równań stanu gazów w zależności od ciśnienia i temperatury panujących w układzie. Gdybyśmy chcieli obliczyć zależność ciśnienia od temperatury i objętości i narysować wykres dla bardzo dużego zakresu ciśnienia, wówczas musielibyśmy użyć kilku równań stanu gazu, po to żeby zminimalizować błąd obliczeń. Tworzymy skrypt gaz.m % deklarujemy zmienne n = 1; % licznosc = 1 mol R = 8.314; % stała gazowa V = 0.0224; % objetość 1 mola w cum warunkach normalnych % wspolczynniki rownania sztywnych kul i van der Waals'a dla metanu a = 0.228; b = 42.8e-6; % tworzymy petle for T = 1:1500 %instrukcja warunkowa - wybieranie rownania stanu gazu if T<500 % rownanie stanu gazu doskonalego p = (n*R*T)/V; elseif (T>=500)&(T<1000) % rownanie sztywnych kul p = (n*R*T)/(V-n*b); elseif T>=1000 % rownanie van der Waals'a p = ((n*R*T)/(V-n*b)) + (a*n^2)/(V^2); 30 end; temperatura(T) = T; cisnienie(T) = p; end; plot(temperatura, cisnienie); W Command Window uruchamiamy nasz skrypt. >> gaz Jako efekt jego działania powinniśmy otrzymać następujący wykres: 5 6 x 10 5 4 3 2 1 0 0 500 1000 1500 W kodzie uzależniliśmy wybór równania stanu gazu od temperatury panującej w układzie. W rezultacie otrzymaliśmy tablicę punktów, z której wydrukowaliśmy wykres. 31 6. Grafika w Matlabie Końcowym etapem większości obliczeń inżynierskich jest graficzna prezentacja otrzymanych wyników. Interpretacja niektórych, bardzo skomplikowanych czasami, obliczeń nie byłaby możliwa gdyby nie istnienie funkcji graficznych umożliwiających w prosty sposób tworzenie wykresów czy diagramów. Jest to również jeden z prostszych sposobów na sprawdzenie poprawności obliczeń. Operując macierzami o kilkunastu czy kilkudziesięciu wymiarach nie jesteśmy w stanie stwierdzić, czy otrzymane wartości mają sens fizyczny a ich zmiany w czasie nie przeczą prawom natury. 6.1 Funkcja plot Funkcja „plot” jest podstawowym narzędziem służącym do tworzenia dwuwymiarowych wykresów. Argumentami tej funkcji są ciągi liczb, reprezentowanych przez nazwę lub wpisywanych wprost. Wpisując w okno poleceń: >> a=[1.23 3.45 5.98 7.43 9.56]; >> b=[1 3 4 6 8]; >> plot(a,b) w osobnym oknie otworzy się wykres automatycznie nazwany „Figure1”: 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10 Wektory a i b są odpowiednio wartościami rzędnych i odciętych. Gdy argumentem jest natomiast tylko jeden wektor, rzędną staje się jego indeks lub, w przypadku liczb zespolonych, OX staje się osią rzeczywistą, a OY osią urojoną. 6.2 Edycja wykresu Jak widać na powyższym przykładzie MATLAB domyślnie formatuje wykresy w pewien określony 32 sposób, ich wygląd można zmienić na dwa sposoby. Pierwszy z nich to edycja otrzymanego już wykresu poprzez polecenia w pasku narzędzi ‘INSERT’ oraz ‘TOOLS⟶ EDIT PLOT’: wstawianie nazwy osi OX wstawianie nazwy osi OY wstawienie tytułu wstawienie legendy Jest to sposób na szybkie zatytułowanie wykresu bez potrzeby wchodzenia w tryb edycji. Tryb edycji umożliwia między innymi zmianę: -tytułu wykresu i osi -grubości, koloru, typu linii i znaczników -skala i zakres wartości na osi -rodzaj i wielkość czcionki -koloru tła -siatki -a nawet danych (z dostępnych w pamięci-o ile zgadza się wymiar obu wektorów) Zmiany te mogą być wprowadzane również odpowiednimi instrukcjami z okna poleceń, używanie trybu edycji wykresu jest jednak znacznie prostsze dla użytkowników poruszających się na co dzień w świecie Windowsa. Drugim sposobem formatowania wykresów jest wprowadzenie dodatkowej zmiennej do funkcji PLOT. Decyduje ona o rodzaju, wielkości, kolorze znaczników oraz linii interpolacyjnej. Zmiany te 33 specyfikuje się używając odpowiednich oznaczeń wpisywanych w pojedynczych nawiasach: Symbol r b m k g y Kolor Czerwony Niebieski Różowy czarny Zielony Żółty Symbol -: -. Styl linii Linia ciągła Linia przerywana Linia kropkowana Linia punktowa W domyślnych wykresach nie są stosowane znaczniki, co jest korzystne gdy operujemy na dużej ilości punktów jednak zwykle warto dokładnie zaznaczyć punkty, gdy na jednym wykresie umieszczamy kilka serii danych. Znacznikami mogą być: ’+’, ‘∘’, ‘*’, ‘x’, ‘.’, ‘d’-romb, ‘s’kwadrat. Dzięki tym oznaczeniom można polepszyć wygląd powyższego wykresu, wpisując w okno poleceń: >> plot(a,b,'*k--')%kolejność wpisywania symboli nie ma znaczenia otrzymamy: 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10 Jak widać grubość linii jest zawsze taka sama, kolor znaczników natomiast zawsze odpowiada kolorowi linii. Można to zmienić stosując następujące polecenia: polecenie ‘markersize’ ‘linewidth’ ‘markerfacecolor’ ‘markeredgecolor’ Działanie Zmiana wielkość znaczników Zmiana grubości linii Zmiana koloru wypełnienia znaczników Zmiana koloru krawędzi znaczników 34 Polecenia te wpisuje się w pojedynczych nawiasach, oddzielając je od innych oznaczeń przecinkiem (wartości grubości linii czy wielkości znaczników należy jednak wpisać bez nawiasów), np. >> plot(a,b,'*k--',’markersize’,20) Dodatkowe komendy pomocne w tworzeniu wykresów: >> title('tytul wykresu') >> xlabel('nazwa osi OX') >> ylabel('nazwa osi OY') >> xlim([2 67])%ustawienie zakresu osi OX% >> ylim('auto')%przywrócenie automatycznego ustawienia zakresu osi OY% Aby jednocześnie ustawić zakres obu osi można posłużyć się poleceniem „axis”, którego argumentem jest wektor z zakresem wartości dla obu osi: >> axis([2 4 6 9]) Dzięki komendom „axis” można sterować skalami osi: polecenie axis manual funkcja obecne zakresy osi są stosowane do następnych wykresów tworzonych na tym samym wykresie (z zastosowaniem polecenia ”hold on” przywracane jest automatyczne dobieranie skali osi przez program zakres skali jest ściśle dostosowany do wartości danych na wykresie skala osi zmienia się by pole wykresu było kwadratem axis auto axis tight axis square 6 5 4 3 2 1 0 1 2 3 10 5 asix equal 0 5 10 skale obu osi stają się takie same 35 8 6 4 2 0 2 4 10 8 6 4 2 0 2 4 6 8 10 axis image analogicznie do „axis equal” skale obu osi są identyczne natomiast pole wykresu jest dostosowywane ściśle do osiąganych wartości 6 4 2 0 2 10 8 6 4 2 0 2 4 6 8 10 W praktyce inżynierskiej jak wiadomo często stosuje się skale logarytmiczne. I w tym przypadku MATLAB przychodzi nam z pomocą. Aby jedna lub obie osie przestawić na skale logarytmiczną wystarczy zastąpić polecenie „plot” jednym z następujących: polecenie semilogx semilogy loglog funkcja skala logarytmiczna dla osi OX skala logarytmiczna dla osi OY obie osie w skali logarytmicznej 6.3.Hold on Często również na jednym wykresie chcemy zamieścić kilka krzywych. Można to osiągnąć na dwa sposoby. Jeden z nich wymaga użycia nowego polecenia: ‘hold on’lub ‘hold all’. Dzięki niemu każde kolejne użycie „plot” (lub innego analogicznego polecenia) spowoduje dodanie nowej krzywej do istniejącego już wykresu do momentu gdy zastosujemy ‘hold off’, przy czym komenda ‘hold all’ zapewnia przydzielanie każdej kolejnej krzywej innego koloru. Użycie ‘hold on’ natomiast oznacza identyczne formatowanie każdej kolejnej krzywej. >> plot(y,x) >> hold all >> plot(x,y) >> plot(x,y) >> hold on >> plot(y,x) 36 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 0 1 0 1 2 3 4 5 6 7 8 9 0 10 0 1 2 3 4 5 6 7 8 9 10 6.4 Subplot W momencie gdy serie danych które chcemy umieścić na wykresie różnią się wartościami na tyle, że jedne z nich są praktycznie niewidoczne warto użyć komendy ‘subplot’ dzięki której w jednym oknie możemy umieścić kilka wykresów. Argumentami tej funkcji są trzy wartości: liczba kolumn, liczba wierszy oraz numer pozycji w której ma się znaleźć dany wykres: >> subplot(2,2,3)%tworzone są cztery pozycje, a dany wykres znajduje się w trzeciej% Pozycje numerowane są od strony lewej do prawej i od wiersza górnego w dół. Jeżeli chcemy dany wykres umieścić w zajętym już miejscu wystarczy do polecenia wpisać ‘replace’: >> subplot(2,2,3,'replace') Po poleceniu ‘subplot’ musi wystąpić kolejne precyzujące co i jak chcemy wykreślić: >> subplot(2,1,1);fplot(@(x)x^2+x-12,[1 3.5]) >> subplot(2,1,2);fplot(@(x)exp(x)-3.5*x,[0 2.4]) Po powyższych poleceniach otrzymamy nastepujące dwa wykresy: 5 0 5 10 1 1.5 2 2.5 3 3.5 3 2 1 0 1 0 0.5 1 1.5 2 37 Podobny efekt można uzyskać edytując powstały już wykres: Dodawanie nowych wykresów do otwartego już okna 6.5 Interpolacja Przy niezwykłej mnogości poleceń umożliwiających formatowanie wykresów jednej ich cechy nie można zmienić wprost-charakteru linii interpolacyjnej. Przy dużej liczbie punktów nie ma to znaczenia gdyż będzie praktycznie niewidoczna jednak dla kilkunastu punktów wykres może okazać się zbyt ”kanciasty”. Aby to zmienić należy posłużyć się funkcją „interp1” , która służy do interpolacji funkcji jednej zmiennej. Przy pracy z funkcjami dwóch zmiennych należy posługiwać się „interp2” a dla ambitnych próbujących działać w przestrzeni trzech zmiennych powstało „interp3”. Algorytm postępowania w przypadku każdej z powyższych funkcji jest taki sam. Tworząc wykres, na który nanosimy punkty doświadczalne automatycznie są one łączone prostą, dlatego przed dodaniem krzywej interpolacyjnej należy te prosta usunąć. >> x=0:10; >> y=atan(x); >> plot(x,y,'o') Kolejnym krokiem jest stworzenie punktów dla danego zakresu argumentów: >> x1=linspace(0,10,200);%aby krzywa interpolacyjna była płynna należy w danym przedziale wygenerować dość dużą liczbę argumentów % >> y1=interp1(x,y,x1,'spline'); W efekcie otrzymamy następujący wykres (dla porównania zamieszczono również wykres tej samej funkcji bez użycia interpolacji): 1.5 1.5 1 1 0.5 0.5 0 0 1 2 3 4 5 6 7 8 9 10 0 0 1 2 3 4 5 6 7 8 9 10 Argumentami funkcji „interp1” są jak widać w powyższym przykładzie: punkty między którymi 38 ma przebiegać krzywa interpolacyjna, wygenerowane argumenty oraz element decydujący o rodzaju interpolacji. Oprócz zastosowanego w przykładzie polecenia „spline”( jedna z interpolacji kwadratowych) można wybrać również: polecenie linear rodzaj krzywej interpolacja liniowa-powstałe odcinki są wielomianem pierwszego stopnia interpolacja kwadratowa-powstałe odcinki są wielomianami drugiego stopnia metoda „najbliższego sąsiada”wygenerowane argumenty przyjmują wartość dla najbliższego punktu cubic nearest Jeżeli podczas interpolacji nie uściślimy jej rodzaju wykonane obliczenia będą dotyczyć interpolacji liniowej, czyli uzyskany efekt będzie prawie identyczny jak w przypadku braku jakiejkolwiek naszej ingerencji. 6.6 Funkcja fplot Korzystając ze znanych już narzędzi , aby stworzyć wykres zdefiniowanej funkcji należy obliczyć jej wartości dla zadanego zbioru argumentów: >> x=linspace(2,10,5); >> y=exp(x); >> plot(x,y) Proces ten znacznie ułatwia funkcja FPLOT, która wykonuje praktycznie cała pracę za nas. Argumentami tej funkcji są: zdefiniowana wcześniej funkcja oraz zakres dla którego chcemy stworzyć wykres. Zakres ten może dotyczyć zarówno argumentów jak i wartości funkcji: >> fplot(@sin,[2 5 -0.6 0.6]) %wykres funkcji f(x)=sin(x) dla argumentów z przedziału (2,5) z ograniczeniem wartości funkcji od -0.6 do 0.6% >> fplot(@sin,[2 5]) %wykres funkcji f(x)=sin(x) dla argumentów z przedziału (2,5) 1 0.5 0.8 0.4 0.6 0.3 0.4 0.2 0.1 0.2 0 0 0.1 0.2 0.2 0.4 0.3 0.4 0.6 0.5 0.8 2 2.5 3 3.5 4 4.5 5 1 2 2.5 3 3.5 4 4.5 5 Jak widać a przykładzie program automatycznie dobiera przedział wartości funkcji (od minimalnej 39 do maksymalnej osiąganej przez dana funkcję w wybranym przedziale). Jeśli jednak chcemy osiągnąć efekt „zoomu” należy uściślić oba przedziały. Funkcja użyta w poleceniu powinna być wcześniej zdefiniowana jednak możliwe jest wypisanie matematycznej formuły (gdy przykładowo wiemy, że nie będziemy w przyszłości korzystać z danej funkcji): >> fplot(@(x)sin(x)+cos(x)-1,[2 10]) 0.5 0 0.5 1 1.5 2 2.5 2 3 4 5 6 7 8 9 10 40 7. Całkowanie Matlab oferuje wiele funkcji slużących do całkowania numerycznego. Omówimy dwie z nich – ode45 – najpopularniejszą i najbardziej uniwersalną funkcję całkującą dostępną w Matlab-ie oraz funkcję trapz – imlpementującą obliczanie całek metodą trapezów. Na początek rozważmy prostą funkcję: y=x 2 Całka nieoznaczona z tej funkcji to oczywiście 1 3 z= x 3 7.1 Funkcja trapz Zaczniemy od trapz. Funkcję, tak jak większość funkcji programu Matlab możemy wywołać na wiele sposobów. Szczegółowe informacje i opis funkcji otrzymasz, wpisując help trapz w oknie Command Window. My będziemy korzystać z wywołania, w którym jako argumenty przekazujemy wektory wartości X i Y. Obliczanie całki metodą trapezów zilustrowano na poniższym rysunku: Ilustracja całkowania metodą trapezów Całkowanie tą metodą polega na przybliżaniu przebiegu funkcji pomiędzy kolejnymi znanymi punktami liniami prostymi i obliczeniu sumarycznego pola powstałych tym sposobem trapezów. Funkcja ta przydaje się gdy mamy np. punkty pomiarowe i musimy obliczyć przybliżone pole pod krzywą utworzoną z tych punktów. Przykład użycia funkcji trapz poniżej: >> >> >> >> >> >> >> z1 x1=[0:1:10]; x2=[0:0.1:10]; x3=[0:0.01:10]; y1=x1.^2; y2=x2.^2; y3=x3.^2; z1=trapz(x1,y1) = 335 >> z2=trapz(x2,y2) z2 = 333.3500 >> z3=trapz(x3,y3) z3 = 333.3335 >> 1/3*(10^3) ans = 333.3333 41 >> Pierwsze trzy operacje to utworzenie wektorów dla zmiennej niezależnej (x) o róznym zagęszczeniu punktów w tym samym przedziale. Następnie dla każdego z tych wektorów liczymy wartości funkcji – y, odpowiadajace wszystkim jego wyrazom i obliczamy całkę przy pomocy funkcji trapz – jej wartość zapisuejmy w zmiennej z. W ostatniej linii obliczono dokładną wartość całki z funkcji y=x2 w przedziale 0 – 10. Jak można się było spodziewać – dla coraz większego zagęszczenia punktów otrzymujemy za pomocą funkcji trapz coraz dokładniejszy wynik. Obliczmy, wykorzystując funkcję trapz, minimalną ilość ciepła potrzebną do ogrzania 1 kg naftalenu od temperatury 25 st. C (298.15 K)do 80 st. C (353.15 K) w procesie izobarycznym. Szukana ilość ciepła będzie równa zmianie entalpii naftalenu, zatem: T=353.15 K Q= H = ∫ C p T dT T=298.15 K Pokażemy dwa sposoby rozwiązania tego zadania – gdy dysponujemy tablicą z wartościami ciepła właściwego naftalenu pod stalym ciśnieniem dla różnych temperatur oraz gdy dysponujemy korelacją pozwalajacą te wartości obliczać. Zaczniemy od przypadku w którym mamy wartości ciepła właściwego w różnych temperaturach zebrane w tabeli. T [K] Cp [J/(kg*K] 298,15 1570,10 303,15 1586,02 308,15 1601,93 313,15 1617,85 318,15 1633,76 323,15 1649,68 328,15 1665,59 333,15 1681,51 338,15 1697,42 343,15 1713,34 348,15 1729,25 353,15 1745,17 W pierwszm kroku musimy posiadane informacje na temat ciepła i temperatury wprowadzić do programu Matlab. W tym celu utworzymy tablicę i skopiujemy do niej wartości temperatury i ciepła właściwego sposobem, omówionym w pierwszym rozdziale. Ilustracja na rysunku poniżej: Wklejanie tablicy temperatura – ciepło właściwe Następnie wykonujemy całkowanie przy pomocy funkcji trapz – w ten sposób obliczymy ilość energii, którą należy dostarczyć. >> trapz(t_cp(:,1),t_cp(:,2)) ans = 9.1170e+04 >> Zatem wymagana energia to około 91.17kJ. Gdy dysponujemy zależnością funkcyjną również możemy użyć funkcji trapz – podobnie jak wtedy, gdy liczyliśmy przybliżoną wartość całki z funkcji y=x 2. Korelacja na ciepło właściwe pod 42 stałym ciśnieniem dla naftalenu ma postać: cp [ ] J =621.093.183∗T kg⋅K A obliczenia możemy wykonać w następujący sposób: >> T=[298.15:1:353.15]; >> cp=621.09 + 3.183 * T; >> trapz(T,cp) ans = 9.1170e+04 >> Najpierw tworzymy tablicę z wartościami temperatury, dla których w kolejnej linii liczymy wartości ciepła właściwego. Następnie korzystamy z funcji trapz aby obliczyć wartość szukanej całki. 7.2 Funkcja ode45 Najczęściej używaną funkcją służącą do całkowania w Matlab-ie jest funkcja ode45. Ode to rodzina funkcji całkująchych, w skład której wchodzi wiele funkcji nadających się do rozwiązywania różnych klas problemów (sztywne układy równań różniczkowych, różna wymagana dokładność obliczeń). ode45 jest najbardziej uniwersalną funkcją spośród nich. Wszystkie funkcje z rodziny ode wywołuje się w praktycznie identyczny sposób. Informacje na temat innych funkcji z rodziny ode znajdziesz wpisując w Command Window help ode45 i podążając za odnośnikami do innych funkcji z rodziny ode, znajdującymi się pod koniec opisu funkcji ode45. Funkcje z rodziny ode pozwalają nam uzyskać przebieg całki funkcji, możemy dzięki nim całkować układy rownań różniczkowych a także wszukiwać punkty, dla których całka osiąga określoną wartość. Podstawowy sposób wywołania funkcji ode45 (i innych funkcji z rodziny ode) wygląda następująco: [x,y] = ode45(@funkcja_do_scałkowania,[xp xk],yp) Funkcja zwraca dwa wektory – x i y. Wektor x zawiera wartości zmienej niezależnej a wektor y – odpowiadające im wartości całki z funkcji funkcja_do_scałkowania w przedziale od xp do xk. Jako parametr yp przekazujemy wartość funkcji którą całkujemy dla punktu xp. Obliczenie wartości całki z funkcji y=x 2 w przedziale 0 – 10 za pomocą funkcji ode45 obejmuje dwa kroki. W pierwszym - tworzymy plik z naszą funkcją: function dxdy=f1(x,y) dxdy=x.^2; Następnie przechodzimy do Command Window i tam obliczamy wartość całki za pomocą funkcji 43 ode45: >> [x,y]=ode45(@f1,[0 10],0); >> y(length(y)) ans = 333.3333 >> x(length(x)) ans = 10 >> x(1) ans = 0 Jeśli otrzymałeś błąd, to prawdopodobnie dlatego, ze plik z funkcją f1 nie znajduje się w jednym z katalogów, w których Matlab poszukuje m-plików z funkcjami. Problem ten i jego rozwiązanie opisano w rozdziale 4.2. y(length(y)) oznacza odwołanie się do ostatniego elementu wektora y. Analogicznie dla wektora x. Zauważ, że jako wynik dostaliśmy od razu dość dokładną wartość całki (przynajmniej do 4-go miejsca po przecinku), ale to samo i to nawet prościej, bo bez poleceń typu y(length(y)) mogliśmy uzyskać przy pomocy funkcji trapz. Aby poznać możliwości jakie zyskujemy dzięki funkcji ode45, zacznijmy od prostego przykładu, w którym rozważymy model cylindrycznego zbiornika przepływowego, przedstawiony na poniższym rysunku: Zbiornik przepływowy Równanie opisujące zmiany ilości cieczy w zbiorniku, przy założeniu że ciecz wpływająca do zbiornika ma taką samą gęstość jak ciecz wypływająca ze zbiornika, ma postać: dV =F in−Fout dt Zakładamy, że natężenie strumienia wpływającego do zbiornika jest stałe. Natężenie strumienia, wypływającego ze zbiornika jest proporcjonalne do pierwiastka kwadratowego wysokości cieczy w zbiorniku, zatem: d A⋅h =F in−⋅ h dt dh A =F in −⋅ h dt dh F in = − ⋅ h dt A A 44 Gdzie A to powierzchnia dna zbiornika dana wzorem: A= ⋅D 4 2 Aby uzyskać zależność wysokości cieczy w zbiorniku od czasu musimy scałkować równanie: dh F in = − ⋅ h dt A A Równanie to jest na tyle proste, że bez problemu daje się scałkować analitycznie ale my użyjemy do jego rozwiązania funkcji ode45. Dzięki niej otrzymamy zależność wysokości cieczy w zbiorniku od czasu w przedziale od tp = 0 [s] do tk = 400 [s]. Zakładamy, że w chwili początkowej w zbiorniku nie było cieczy, czyli: ht p=0[s ]=0[m] Tak jak poprzednio najpierw tworzymy m-plik z naszą funkcją obliczającą dh/dt: function dhdt=wysokosc(t,h) Fi=0.07; %m^3/s A=2.5; %m^2 beta=0.08; %m^(5/2)/s dhdt=(Fi-beta.*sqrt(h))./A; i zapisujemy go. Następnie używając funkcji ode45 obliczamy szukaną zależność wysokości od czasu a uzyskane wartości nanosimy na wykres (funkcją plot): >> [t,h]=ode45(@wysokosc,[0 400],0); >> plot(t,h) Oto uzyskany wykres zmian wysokości cieczy w zbiorniku w czasie. Wykres zmian poziomu cieczy w zbiorniku Gdybyśmy chcieli teraz powtórzyć obliczenia dla zbiorników o innych wymiarach, za każdym razem musielibyśmy edytować plik z funkcją wysokosc. Znacznie wygodniej byłoby gdybyśmy zwyczajnie przekazywali wymiary zbiornika jako argument dla tej funkcji. Funkcja wyglądała by wtedy tak: 45 function dhdt=wysokosc2(t,h,Fi,A,beta) dhdt=(Fi-beta.*sqrt(h))./A; Aby funkcja ode45 przekazała do funkcji którą ma scałkować dodatkowe parametry należy ją wywołać w następujący sposób: [x,y] = ode45(@funkcja_do_scałkowania,[xp xk],yp,[],p1,p2,p3,itd...) Jak widać na końcu naszego wywołania dodajemy pusty wektor a za nim kolejne parametry przekazywane do funkcji. W miejsce pustego wektora możemy podać dodatkowe opcje dla funkcji rozwiązującej nasze równanie – przykład takiej opcji pokażemy później. Całkowanie naszej funkcji wysokosc2 wygląda tak: >> [t,h]=ode45(@wysokosc2,[0 400],0,[],0.07,2.5,0.08); >> plot(t,h) >> Powinniśmy otrzymać dokładnie taki sam wykres jak poprzednio. Zapamiętaj, że jako dodatkowy argument możesz także przekazać uchwyt funkcji. Może się to czasem przydać – szczególnie przy pisaniu uniwersalnych skryptów w których będziesz chciał mieć możliwość łatwej zmiany na przykład funkcji obliczających parametry mediów biorących udział w procesie. 7.3 Funkcja ode45 – całkowanie układów równań Całkowanie układów równań różniczkowych również można wykonać przy pomocy funkcji ode45. Rozważmy następujący przykład: W izotermicznym reaktorze zbiornikowym pracującym w sposób okresowy biegną następujące reakcje elementarne: 1: A B 2 : B C 3:C D Stałe szybkości poszczególnych reakcji w temperaturze procesu wynoszą: k1 = 3.46 . 10-3 [1/s] k2 = 6.51 . 10-2 [1/s] k2 = 1.38 . 10-4 [1/s] Stężenie A w reaktorze w chwili t=0 wynosi 20 kmol/m3. Należy obliczyć stężenia poszczególnych składników w zależności od czasu prowadzenia procesu. Zmiany stężenia składników opisuje układ równań różniczkowych: d CA =−k 1⋅C A dt d CB =k 1⋅C A−k 2⋅C B dt d CC =k 2⋅C B −k 3⋅C C dt d CD =k 3⋅C C dt 46 Stężenie składnika A w chwili 0 wynosiło 20 kmol/m3, natomiast pozostałe składniki nie byly obecne w mieszaninie reakcyjnej. Tworzymy plik z funkcją z naszym układem równań: function dcdt=stezenia(t,c) k1=3.46e-3; %1/s k2=6.51e-2; %1/s k3=1.38e-4; %1/s dcdt=[-k1*c(1), k1*c(1)-k2*c(2), k2*c(2)-k3*c(3), k3*c(3)]; Należy zwrócić uwagę, że w przypadku całkowania układu rownań, jako argument będący zmienną zależną (w naszym przypadku c, czyli wektor zawierajacy stężenia składników) funkcja otrzymuje nie pojedyncza liczbę jak to się działo dotychczas ale tablicę liczb, zawierajacą stężenia poszczególnych składników w chwili t. W naszym przypadku pierwszy element tej tablicy – c(1) – odpowiada składnikowi A, drugi (c(2)) – składnikowi B itd. Zauważ, że jako wartość funkcji także zwracany jest wektor, którego kolejne pola odnoszą się do szybkości zmian stężenia poszczególnych składników, przy czym kolejność skłądników jest taka, jak w wektorze c. Wywołanie funkcji ode45 w celu rozwiązania układu rownań różni się tym, że musimy podać wektor wartości początkowych dla wszystkich zmiennych zależnych (w naszym przypadku – wartości stężeń wszystkich składników w chwili t=0s, czyli [20 0 0 0]). >> [t,c]=ode45(@stezenia,[0 500],[20 0 0 0]); Następnie na wykres nanosimy przebiegi zależności stęzenia od czasu dla poszczególnych reagentów – po każdej komendzie plot spójrz na wykres, żeby zobaczyć która linia odpowiada któremu składnikowi. >> >> >> >> >> >> hold on plot(t,c(:,1)) plot(t,c(:,2)) plot(t,c(:,3)) plot(t,c(:,4)) 7.4 Zdarzenia (Events) Niekiedy mając dany różniczkowy model jakiegoś zjawiska (np. model zbiornika z cieczą, jak w jednym z poprzednich podpunktów), chcemy wiedzieć dla jakiej wartości zmiennej niezależnej, zmienna zależna osiągnie określoną wartość (np. po jakim czasie uzyskamy określony poziom cieczy w zbiorniku). Możemy w takim wypadku narysować wykres i odczytać z niego interesujacą nas wartosc z pewną dokładnością, jednak lepiej skorzystać w takiej sytuacji z obecnego w matlabie mechanizmu zdarzeń. Należy w tym celu najpierw napisać funkcję, która wykryje dla nas wystąpienie zdarzenia a następnie „poinformować” o jej istnitniu funckję całkującą – w naszym przypadku ode45. Rozważymy dwa przykłady – znajdziemy czas, po którym poziom cieczy w zbiorniku z punktu 7.2 47 wyniesie 0.5 metra oraz czasy, po których stopień przereagowania składnika A w przykładzie z punktu 7.3 wyniesie 0.5, 0.8 i 0.9. Zacznijmy zatem od zbiornika. Funkcja opisująca szybkość zmian wysokości cieczy w zbiorniku ma postać: function dhdt=wysokosc(t,h) Fi=0.07; %m^3/s A=2.5; %m^2 beta=0.08; %m^(5/2)/s dhdt=(Fi-beta.*sqrt(h))./A; Musimy teraz stworzyć dodatkową funkcję, która będzie odpowiedzialna za wykrycie wystąpienia zdarzenia – w naszym przypadku będzie to moment, w którym poziom cieczy w zbiorniku wyniesie 0.5 metra. function [wartosc, koniec, kierunek]=ev_05m(t,h) wartosc = h-0.5; koniec=0; kierunek=0; Funkcja musi zwracać trzy argumenty – pierwszy to wartość. Matlab przyjmuje, ze zdarzenie nastąpiło wtedy, kiedy argument wartosc jest równy zero – dlatego właśnie od wysokości odejmujemy wysokość która nas interesuje. Kolejny argument określa, czy po wystąpieniu zdarzenia matlab ma przerwac liczenie całki (wtedy zmiennej koniec nadajemy wartość 1) czy ma kontynuować obliczanie całki- wtedy tak jak w tym przykładzie zmiennej nadajemy wartość 0. Zmienna kierunek pozwala dodatkowo określić czy interesuje nas zdarzenie występujące tylko wtedy, gdy funkcja jest rosnąca (wtedy jej pochodna jest dodatnia a my musimy nadać zmiennej kierunek wartość 1), malejąca (-1) lub czy jest to nam obojętne (wtedy tak, jak w naszym przypadku zmiennej kierunek przypisujemy wartość 0). Ostatnim krokiem przed wywołaniem funkcji całkującej z wykorzystaniem zdarzenia jest utworzenie specjalnego zbioru dodatkowych opcji dla funkcji ode45, przy pomocy którego przekażemy informację o interesującym nas zdarzeniu: >> opt=odeset('Events',@ev_05m); odeset tworzy taki właśnie zbiór opcji. Wszystkie poza tymi, które wymienimy w wywołaniu funkcji (w tym przypadku jest to opcja Events), otrzymują wartości domyślne. Jako kolejny argument przekazujemy uchwyt naszej funkcji która ma służyć do wykrycia zdarzenia. Wpisując w Command Window help odeset dowiesz się jakie inne opcje możesz przekazywać do funkcji całkującej. Następnie wykonujemy całkowanie: >> [t,h,t_05m,h_05m]=ode45(@wysokosc,[0 400],0,opt); t_05m i h_05m to zmienne, w których znajdą się odpowiednio czas, w którym wystąpiło zdarzenie i wysokość dla której wykryto wystąpienie zdarzenia (w naszym przypadku h_05m powinno być rowne 0.5). Spróbuj teraz wykonać polecenie: >> plot(t,h,'-',t_05m,h_05m,'o') 48 Aby zobaczyć gdzie na wykresie znajduje się znaleziony przy pomocy mechanizmu zdarzeń punkt. Teraz spróbuj samodzielnie zmodyfikować zdarzenie tak, żeby otrzymać czas, po którym poziom cieczy w zbiorniku wyniesie 0.65 m (powinienneś otrzymać wynik 88.7118 s). Aby lepiej oswoić się z mechanizmem zdarzeń rozważymy jeszcze jeden przykład – znajdziemy czasy, dla których stopień przereagowania skladnika A w przykładzie z rozdziału 7.3 wyniesie 0.5, 0.8 i 0.9. Oczwiście skorzystamy z napisanej wcześniej funkcji opisującej zmiany stężeń składników w czasie: function dcdt=stezenia(t,c) k1=3.46e-3; %1/s k2=6.51e-2; %1/s k3=1.38e-4; %1/s dcdt=[-k1*c(1), k1*c(1)-k2*c(2), k2*c(2)-k3*c(3), k3*c(3)]; Pisząc funkcję wykrywającą zdarzenia, musimy pamiętać że w wektorze c mamy stężenia a nie stopień przereagowania. Musimy zatem przeliczyć stopeiń przereagowania na odpowiadające mu stężenie, zatem: c 0−c = c0 stąd: c=c0⋅1− Stężenie początkowe substratu a wynosiło 20 kmol/m3, zatem : kmol kmol ⋅1−0.5=10 3 3 m m kmol kmol c 08=20 3 ⋅1−0.8=4 3 m m kmol kmol c 09=20 3 ⋅1−0.9=2 3 m m c 05=20 Funkcja wykrywająca zdarzenia będzie więc miała postać: function [wartosc, koniec, kierunek]=evnt(t,c) wartosc = [c(1)-10; c(1)-4; c(1)-2]; koniec=[0;0;0]; kierunek=[0;0;0]; Następnie tworzymy strukturę z opcjami dla funkcji ode45, do której przekazujemy informacje o naszej funkcji: >> opt=odeset('Events',@evnt); Oraz całkujemy: >> [t,c,te,ce]=ode45(@stezenia,[0 1000],[20 0 0 0],opt); >> te 49 te = 200.3315 465.1555 665.4871 >> ce ce = 10.0000 4.0000 2.0000 0.5613 0.2244 0.1123 9.3058 15.1773 16.8434 0.1329 0.5983 1.0444 W wektorze ce znajdują się stężenia wszystkich związków w reaktorze w chwili wystąpienia zdarzenia (osiągnięcia zadanego stężenia składnika A) a w odpowiadających im wierszach wektora te czasy, po których osiągane są szukane stopnie przereagowania. 50