145 79 5MB
Polish Pages [167] Year 2014
JAN WASILEWSKI
ZABAWY Z KOMPUTEREM I FIZYKĄ SKOKI, RZUTY I RAKIETY |
M ODELE JEDNOPUNKTOW E
Spis treści
Wstęp: dla kogo ta książka ................................................................... 7 W.l. Konwencje nazewnicze i typograficzne .............................. .-.................... 9 11 W.2. Ogólny układ książki - jak to czytać ..............................
I.
T r o c h ę p r o s t e j f i z y k i , p r o s t e p r o g r a m o w a n i e ........
15
1.1. Położenie jako funkcja czasu .................................. 15 16 1.2. II zasada dynamiki Newtona .................................... 1.3. Co to jest równanie różniczkowe zwyczajne ........................... 17 1.4. Jak można to najprościej rozwiązać: metody Eulera i Verleta ................ 18 19 1.5. Najprostszy test - rzut ukośny ................................... 1.6. Rakieta jako punkt o zmiennej masie: III zasada dynamiki Newtona ........ 21 1.7. Ruch w powietrzu: najprostsze modelowanie oporu ośrodka ........... 23 1.8. Przystępujem y do zabawy z komputerem - zaczynamy programować ....... 26 1.8.1. Wybór struktur danych ................... 27 P-I. Pascal: program P r_ev_test.dpr ........................... P-I.l. Inicjowanie struktur danych ......................................................................... ....... P-I.2. Realizacja wzorów na przyspieszenie ................ P-I.3. Integratory Eulera i Verleta .......... P-I.4. Wybór warunków początkowych, czyli w co się bawić ........... P-I.5. Analiza błędów numerycznych ...................... P-I.6 . Program zarządzający ......
28 31 34 37 39 43 46
C-I. C++: program C r_ev_test.cc ................ C-I.l. Inicjowanie struktur danych ................................................................................ C-I.2. Realizacja wzorów na przyspieszenie ................................ C-I.3. Integratory Eulera i Verleta ............... C-I.4. Wybór warunków początkowych ..... C-I.5. Analiza błędów numerycznych ................................ C-I.6 . Program zarządzający ..............................
53 54 57 59 60 64 66
F-I. F9x: program F r_ev_test.f90 ............... F-I.l. Inicjowanie struktur danych ................................................ F-I.2. Realizacja wzorów na przyspieszenie ........................................................ F-I.3. Wybór warunków początkowych ........................................................................ F-I.4. Analiza błędów numerycznych ................................. F-I.5. Integratory Eulera i Verleta ................................................................................. F-I.6 . Program zarządzający ......... Z-I. K ilka testów - przystępujem y do zabawy ............................................ .......... Z-I.O. Jak z danych pliku otrzymać wykresy? ...... Z-I.l. Zabawa nr 1 - rzut ukośny bez oporu .............................................. Z-I.2. Zabawa nr 2 - wytaczamy działo ..................................................... Z-I.3. Zabawa nr 3 - skaczemy, bez spadochronu ..................................................... Z-I.4. Zabawa nr 4 - skąd tu wziąć rakietę............................................,........................ Z-I.5. Zabawa nr 5 - wracamy na boisko ....... .........................
II. Jeszcze trochę prostej fizyki, elementy programowania obiektowego
..................
II. 1. Odrobina termodynamiki ....... ................. 11.1.1. Równanie Clapeyrona i wzór barometryczny ............... 11.2. Modelowanie bariery dźwięku ................................................................ 11.3. Dopóki Ziemia kręci się.............................................. 11.3.1. Siła Coriolisa ...................................................................... 11.4. Wprowadzenie do rozszerzonego program u ...........„....................... 11.4.1. Klasy i obiekty ...................... 11.4.2. Przeciążanie operatorów ................ 11.4.3. Naprowadzanie rakiety na cel - pewien prosty algorytm ..... ,...................... 11.4.4. Jeśli parametry ruchu zmieniają się gwałtownie: przełączanie logistyczne ... 11.4.5. Protokoły ekranowe, pliki z wynikami ............ P-II. Obiektowo w Pascalu
....................
P -II.I. FPC/Lazarus: program Pv3_prog.lpr ................ P-II.1.1. Działania na wektorach w przestrzeni trójwymiarowej, klasa P_T_V3 ........ P-II.1.2. Klasa parametrów stałych P_T_CPAR i jej konstruktor ............... P-II. 1.3. Klasa parametrów zmiennych P_T_VPAR ...... P-II.1.4. Budowa wektora przyspieszenia ............................. ........... ; .............. P-II.1.5. Program zarządzający ....................
73 77
81 84 88
90 93
100 100 101
106 108 1\ 115
119 119
120 125 128 131
136 137 138
141 144 145 148
148 153 160 170 171 175
P-II.2. DP: to samo trochę inaczej, program Dv3_prog.dpr ............... P-II.2.1. Typ obiektowy w Delphi-Pascalu .................................................................. P-II.2.2. Wektory jako obiekty w przestrzeni trójwymiarowej, typ D_T_V3 ............. P-II.2.3. Parametry stałe: typ D_T_CPAR i jego konstruktor ............................. P-II.2.4. Parametry zmienne: typ D_T_VPAR i jego konstruktor .............................. P-II.2.5. Budowa wektora przyspieszenia ................ P-II.2.6. Program zarządzający ........... ........
181 181 182 188 197 198 200
C -II. Obiektowo w C++: program Cv3_prog.cc ..... C-II.l. Klasa wektorów w przestrzeni trójwymiarowej C_T_V3 ...... C-II.2. Klasa parametrów stałych C_T_CPAR i jej konstruktor ........... C-II.3. Klasa parametrów zmiennych C_T_VPAR ..... C-II.4. Funkcje niekorzystające z wektorów ....... C-II.5. Budowa wektora przyspieszenia .............. C-II.6 . Program zarządzający ........
207 207 215 225 226 229 232
F-II. Obiektowo w F9x: program Fv3_prog.f90 ................................................... F-II.l. Moduły, konstruktory, pseudo-konstruktory ........ F-II.2. Prolog - moduł stałych globalnych Fv3_def ............. F-II.3. Moduł działań na wektorach Fv3_v_class ............................ F-II.4. Moduł funkcji pomocniczych Fv3_f un_lib .............. F-II.5. Parametry stałe: moduł Fv3_cpar_class, typ F_T_CPAR ............................... F-II.6 . Parametry zmienne: moduł Fv3_vpar_class, typ F_T_VPAR ................ F-II.7. Budowa wektora przyspieszenia ..................................... F-II.8 . Program zarządzający .........................................
238 238 240 241 248 252 261 262 265
Z -II. Skoki, rzuty i rakiety, czyli dalsze zabawy z komputerem i fizyką (wcale nie zabaw ne... ) .......
— 273
Z -II.l. Skoki - ze spadochronem .............. Z-II.1.1. Desant spadochronowy, a tu wieje wiatr................. Z-II.1.2. Felix Baumgartner skacze ze stratosfery ........ Z-II.1.3. Francis G. Powers ratuje się z U2 ............................ Z-II.1.4. Misja Apollo: powrót z kosmosu ..........
273 273 276 281 284
Z -II.2. R zuty ................ Z-II.2.1. Wracamy na boisko ............................ Z-II.2.2. Strzelamy na Toruńskim Poligonie Artyleryjskim (najstarszym na świecie) ............ Z-II.2.2.1. Strzela się na ogół do jakiegoś celu ................................. Z-II.2.2.2. .. .który czasem także porusza się...............
288 288 289 291 295
Z-II.2.3. Działo paryskie Z-II.3. Rakiety ............................... •.................. Z-II.3.1. Wracamy na poligon, tym razem z katiuszą Z-II.3.2. Rakieta V2 ........................ ........................... Z-II.3.3. Rakietą S-75 w samolot U2 .... ...................
Kompendium konstrukcji językowych Parę uwag końcowych ......................... Bibliografia ....................... ....................
296
Wstęp: dla kogo ta książka
Dla kogo? Oczywiście dla tych, którzy lubią „bawić się” komputerem, nie tylko przez surfowanie po sieci czy korzystanie z gier komputerowych i gotowego oprogramowa nia, ale także przez samodzielne zaprogramowanie „czegoś”. Ale jak wybrać to „coś”? Jako fizyk proponuję wybrać „coś z fizyki”, która opisuje zachowanie obiektów w ota czającym nas świecie materialnym. Zakładam więc, że czytelnik, który ma ochotę „coś” zaprogramować, przynajmniej przeczuwa, że programowanie komputerów to nie jest zajęcie tylko dla zawodowców, ale także dla zwykłych ludzi, a nawet - że może to być zabawa intelektualna nie gorsza niż na przykład układanie krzyżówek czy rebusów. Zakładam także, że czytelnik zetknął się już z podstawowymi pojęciami związanymi z komputerami, słyszał coś o programowaniu, potrafi posługiwać się jakimś edytorem znakowym (Word i jego odpowiedniki nie są tu właściwe), nie musi jednak znać na po ziomie roboczym żadnego z trzech szeroko rozpowszechnionych języków, które będą tu użyte: Pascala, C lub Fortranu. Wszystkie budowane w tej książce programy wystą pią w tych trzech wersjach językowych. W wypadku Pascala chodzi o wersję zaimple mentowaną w systemie Delphi (wersja 7; będzie tu ona oznaczana DP), poziom aplika cji konsolowych (to znaczy - bez korzystania z możliwości graficznych Delphi); można użyć także kompilatora FPC - Free Pascal Compiler (w ramach systemu operacyjnego UNIX/LINUX, nakładki Cygwin na system Windows, lub w systemie Lazarus: ta ostat nia wersja, symulująca DP, będzie tu oznaczana LP). Od razu zaznaczam, że budowane tu programy nie dadzą się użyć w Turbo-Pascalu (TP) bez istotnych przeróbek. Te same programy zbudowane zostaną także w wersji C++ (system operacyjny UNIX/LINUX, ewentualnie nRkładka Cygwin). Przedstawione zostaną także odpowiedniki tych pro gramów w obiektowych wersjach Fortranu: 90, 95 i nowszych, które określać będę tu wspólnie jako „Fortran 9x”. Jeśli ktoś zna tylko Fortran 77 (lub nawet wersje wcześniej sze), będzie miał okazję przyjrzeć się obiektowej wersji Fortranu. Jeśli ktoś już posłu guje się bez problemów jednym z wymienionych języków, może w tej książce przyjrzeć się jego relacjom do dwóch pozostałych. Pomoże w tym zamieszczone na końcu książ ki Kompendium, zestawiające w formie tabelarycznej poszczególne konstrukcje trzech używanych tu języków. Każdego z tych języków nauczałem na różnych poziomach przez wiele lat studentów wczesnych (często pierwszych) lat studiów na kierunkach ścisłych: fizyka, astronomia, informatyka, chemia. Miałem więc do czynienia z młodymi ludźmi, często bezpośred-
8
WSTĘP: DLA KOGO TA KSIĄŻKA
nio po maturze, na ogół jeszcze nieznającymi „matematyki wyższej”, ale przynajmniej znającymi w podstawowym zakresie funkcje trygonometryczne. I to nam na razie wy starczy, potem przydadzą się jeszcze funkcje wykładnicze i logarytmiczne. W wielu wypadkach studenci ci nie mieli żadnego przygotowania w zakresie programowania, przedmiot „informatyka w szkole” obejmował dla nich, poza podstawową obsługą kom putera, wyłącznie elementy pakietu Microsoft Office (znacznie rzadziej - Open Offi ce). Większość tych, którzy deklarowali jakieś umiejętności programistyczne, zdobyła je samodzielnie, rzadziej w klubach, kółkach itp., raczej wyjątkowo na lekcjach w szko le. Umiejętności te, choć cenne, okazywały się w trakcie zajęć na ogół fragmentaryczne i pozbawione bardziej ogólnego aparatu pojęciowego w zakresie informatyki. Przyjmo wałem więc praktycznie zawsze „opcję 0 ”, rozpoczynając cykl nauki od podstaw. Języków programowania uczyć można w różny sposób. Klasyczny podręcznik to syste matyczny wykład struktur językowych, od najprostszych do coraz bardziej złożonych, z małymi (z konieczności) przykładami na każdym etapie. Ale można metodykę uczenia języka zbudować inaczej, w duchu „studium przypadku” (case study): * mamy konkretny problem, który chcemy rozwiązać przy pomocy komputera; ■ analizujemy, co możemy w ramach tego problemu na komputerze zrobić: formu łujemy model (fizyczny, matematyczny, logiczny, informatyczny...) naszego pro blemu i zadania w ramach modelu; ustalamy, jakie mamy dane i jakiego rodzaju wyniki chcemy otrzymać;
WSTĘP: DLA KOGO TA KSIĄŻKA
9
■ po kolejnym etapie takiego postępowania możemy podsumować i usystematyzo wać tak poznane elementy języka. Taka metodyka zastosowana jest w tej książce, wynika ona z moich doświadczeń w uczeniu studentów. Nie jest to więc typowy podręcznik do nauki któregoś z trzech przedstawionych tu języków; m ojąintencjąjest pokazanie tych języków w zastosowaniu do rozwiązywania praktycznych problemów obliczeniowych i na takiej drodze pokaza nie i usystematyzowanie różnych struktur językowych. Wszystkie konstrukcje Pascala, C++ i Fortranu, aż do podstaw programowania obiektowego, potrzebne do rozwiązywa nia przedstawionych tu prostych zagadnień z fizyki wystąpią w tej książce wraz z nie zbędnymi objaśnieniami. Jednak omówione zostaną nie w kolejności przyjętej w stan dardowych podręcznikach, ale w kolejności takiej, jaką narzuci rozwiązywany problem. Wszystkie zamieszczone tu programy i podprogramy kompilują się bez błędów i utwo rzą działające aplikacje. Kody wykonawcze tych programów można pobrać z mojej stro ny internetowej: www.fizyka.umk.pl/~janwas (zakładka: dydaktyka | do pobrania | pcf_l); wersje w Pascalu mogą być wykonane pod systemem Windows, wersje w Fortra nie i w C - pod systemem UNIX/LINUX. Zasadniczo nie udostępniam kodów źródło wych w sposób umożliwiający ich pobranie, załadowanie do komputera i bezpośrednią kompilację: kody te są jednak umieszczone w całości w tej książce. To moja świado mie wybrana i od dawna stosowana metoda w nauczaniu programowania: chcesz rozu mieć działający program - to, mając sprawdzony wzór, uważnie go przeanalizuj i naj lepiej spróbuj napisać samodzielnie (czasami spoglądając na zamieszczony przykład), a w ostateczności - uważnie przepisz.
■ na tym etapie musimy rozejrzeć się, jakie rodzaje (typy i struktury) danych moż liwe są do użycia w pracy na komputerze;
W.l. Konwencje nazewnicze i typograficzne * analizujemy drogę prowadzącą od danych do wyników - formułujemy algo rytm, czyli jednoznaczny, masowy (tzn. wykonalny dla każdego przewidywa nego przez nas zestawu danych) i skończony (tzn. wykonalny w skończonej licz bie kroków) przepis na przejście od danych do wyników;
Nazwy wszystkich segmentów programowych zaczynają się od P (ewentualnie D), C lub F - w zależności od użytego języka. Jest to bardziej jednoznaczne od samego tylko rozszerzenia nazwy (*.ext), bo różne kompilatory stosująróżne rozszerzenia w ramach tego samego języka. Dalsze litery w nazwie mogą określać warianty programu:
* dzielimy algorytm na coraz mniejsze, coraz bardziej szczegółowe zadania (pod)algorytmy: ustalamy, jakich danych potrzebuje konkretny (pod)algorytm, skąd je pobiera, jakie produkuje wyniki;
P/Dx_name.ext: program można skompilować jako aplikację konsolową w Pascalu:
■ dopiero teraz przystępujemy do tego, co popularnie nazywa się „programowa niem”: zapisujemy tak sformułowane podalgorytmy przy użyciu takich struktur wybranego języka, jakie są nam aktualnie potrzebne, poznając na tej drodze ten język w praktycznym zastosowaniu;
Cx_name.ext:
FPC {Free Pascal Compiler [1,2]) lub w Delphi (DP - Delphi Pascal wersja 7; zob. np. [3,4,5]); nie można jednak skompilować go w Turbo-Pascalu (TP, np. [6 ]); program/segment w C++; zob. np. [7,8];
10 Fx_natne.f9x:
WSTĘP: DLA KOGO TA KSIĄŻKA
program/segment napisany w Fortranie 90 [9,10,15], 95 lub nowszym [11,12], oznaczanymi tu generalnie jako F9x, kompilatory gfortran, g95, ifort...
Wszystkie teksty źródłowe segmentów programowych wstawiane są w ramki i pisane czcionką stałej szerokości (tu wybrałem consolas), bo są pobierane wprost z działających programów. Ta sama czcionka stosowana jest w tekście wszędzie tam, gdzie występują elementy programów. Słowa kluczowe każdego z języków, tzn. zrozumiałe dla kompilatora bez jawnego dołączania dodatkowych bibliotek, są pogrubione. Nazwy zmiennych tworzone są wg tradycji fortranowskiej: nazwy zaczynające się od liter i-n przenoszą wartości całkowitoliczbowe zapisane stałopozycyjniej pozostałe nazwy przenoszą wartości rzeczywiste podwójnej precyzji zapisane zmiennopozycyjnie na 8B wg standardu IEEE 754 [16] (z uwzględnieniem nieskończoności, NaN-ów i możliwości denormalizacji mantysy); zagadnienia omawiane w tej książce nie mogą być skutecznie rozwiązywane w standardowej precyzji pojedynczej (4B). Nazwy zmiennych logicznych zaczynają się od rl_J, nazwy typów zaczynają się od, lub zawierają, rT_J. lako zasadę stosowane są małe litery, tylko w parametrach formalnych podprogramów i w nazwach typów używane są litery wielkiej Pascal i Fortran nie rozróżniają małych i wielkich liter (poza łańcuchami), rozróżnia je natomiast C.
WSTĘP: DLA KOGO TA KSIĄŻKA
11
W.2. Ogólny układ książki - jak to czytać Książka podzielona jest na dwie części: Część I. Trochę prostej fizyki, proste programowanie Postępując w duchu „studium przypadku” formułujemy nasz problem: chcemy zba dać ruch obiektu materialnego w polu siły ciężkości, w stawiającym opór powietrzu i z uwzględnieniem faktu, że obiekt ten może być także rakietą, czyli tracić masę. Obiekt modelujemy jako punkt materialny - punkt matematyczny posiadający masę, i zaczy namy od przypomnienia, na poziomie szkolnych wiadomości z fizyki, jak opisuje się ruch takiego punktu: ogólnych wzorów na położenie punktu w przestrzeni trójwymia rowej (3D) i jego prędkość - jako funkcji czasu. Przypominamy sobie II zasadę dyna miki Newtona i zauważamy, że jest to równanie, w którym przyspieszenie (czyli druga pochodna położenia po czasie) jest proporcjonalne do zadanego układu sił działających na nasz obiekt. Równanie, w którym nieznana funkcja (położenie) określona jest przez warunki narzucone na jej pochodne (przyspieszenie) - to równanie różniczkowe, a zna ne nam ze szkoły wzory na położenie i prędkość można interpretować jako szczególny przypadek rozwiązań tego równania. Następnie wychodzimy trochę poza szkolną fizy kę, przyglądając się, jak zmienia się to równanie w sytuacji, gdy masa punktu w trakcie ruchu nie jest stała: to pozwala nam sformułować podstawowe równanie ruchu rakiety, traktowanej jako punkt materialny. Wprowadzamy także na najprostszym poziomie mo del oporu powietrza przeciwdziałającego ruchowi.
lako zasadę stylistyczną stosowane są wcięcia na początku każdego nowego poziomu zagłębienia (otwarcie instrukcji złożonej, sekcji selektora, pętli itp.)j wcięcia są ignorowane przez kompilatory, ale ułatwiają rozumienie struktury kodu źródłowego, wiele edytorów stosuje je automatycznie.
Na takim poziomie wiadomości z fizyki przechodzimy do sformułowania programu ob liczeniowego, który umożliwi wyznaczenie położenia i prędkości punktu, także w przy padku zmiennej masy i oporu powietrza, poprzez rozwiązywanie odpowiedniego ukła du równań różniczkowych.
W objaśnieniach struktur językowych poza tekstami programów elementy ujęte w nawiasy kwadratowe [ ... ] są (jak zwykle) opcjonalne.
W rozdziale P-I program formułujemy w Pascalu jako aplikację konsolową w Delphi, wraz z obszernym omówieniem zasad zapisu algorytmów w tym języku; dla czytelni ków nieznających Pascala jest to więc równocześnie elementarz tego języka. Program jest obszernie komentowany, zarówno we wstępach do poszczególnych jego segmentów, jak i w samym tekście: podstawowe komentarze są częścią tekstu źródłowego (w szcze gólności pisane s ą -ja k całość programu - czcionką consolas, bez polskich znaków dia krytycznych: proszę mi to, powszechne w dobie SMS-ów, przestępstwo wybaczyć). Do datkowo komentarze dotyczące głównie relacji poszczególnych fragmentów programu do zagadnień fizyki lub informatyki, do wzorów w tekście, ujęte są w ramkach i pisane czcionką „tekstową” - nie występują one w tekście źródłowym programu.
Wzory i wszystkie elementy w nich występujące (także w tekście) pisane są kursywą, wektory (tablice 1-indeksowe) pisane są czcionką pogrubioną: v , f itp., macierze (tablice 2-indeksowe) - literami wielkimi: A , M.
W rozdziale C-I to samo zagadnienie formułujemy w C++ w systemie UNIX/LINUX wraz z obszernym omówieniem zasad zapisu poszczególnych konstrukcji iezvkowvch-
12
WSTĘP: DLA KOGO TA KSIĄŻKA
dla czytelników nieznających C++ jest to więc równocześnie elementarz tego języka. Poszczególne elementy programu nie są tym razem omawiane wstępnie (jak w wersji P), ale obszernie komentowane w tekście: podstawowe komentarze są w języku angielskim - w celu oswojenia z anglojęzyczną terminologią tej dziedziny - i są częścią tekstu źró dłowego, a dodatkowe komentarze ujęte w ramkach mają taki sam sens, jak w wersji P i nie występują w programie źródłowym. Merytorycznie wszystkie komentarze są po wtórzeniem komentarzy w wersji P. W rozdziale F-l to samo zagadnienie formułujemy w Fortranie 9x w systemie UNIX/ LINUX wraz z obszernym omówieniem zasad zapisu algorytmów w tym języku; dla czytelników nieznających F9x jest to tym samym elementarz tego języka, nawiązujący także do nieco odmiennych zasad zapisu programów w Fortranie 77 i wersjach wcze śniejszych. Program jest komentowany podobnie jak wersja C: podstawowe komentarze sąw języku angielskim i są częścią tekstu źródłowego; dodatkowe komentarze, dotyczą ce głównie relacji poszczególnych fragmentów programu do zagadnień fizycznych lub informatycznych, ujęte są w ramkach - nie występują one w tekście źródłowym. Me rytorycznie komentarze te są powtórzeniem komentarzy w wersjach P i C. F9x nie jest w Polsce językiem tak popularnym jak P lub C++ (odwrotnie jest w USA), nie ma też dużego wyboru źródeł podręcznikowych. Jest jednak sporo osób znających wcześniej sze wersje Fortranu, szczególnie F77 [13,14]; przyjrzenie się wersji obiektowej może być dla tych osób interesujące. Każdy z tych trzech rozdziałów (P/C/F-I) jest w znacznym stopniu autonomiczny. Czy telnik zainteresowany tylko jednym językiem programowania może więc w zasadzie pominąć oba rozdziały dotyczące innych języków. Jednak osoby zainteresowane tylko wersjami C lub F zachęcam do przeczytania wprowadzeń do poszczególnych segmen tów w wersji P. Po sformułowaniu przedstawionych wyżej trzech wersji programów przystępujemy w rozdziale Z-I do naszych „zabaw”. Do obliczeń możemy użyć dowolną wersję pro gramu: zarówno dialog ekranowy, jak i struktura wytworzonych plików z wynikami są we wszystkich wersjach identyczne. Na początku omawiamy jednak wykorzystanie tworzonych plików do graficznego prezentowania wyników obliczeń, przy użyciu sze roko rozpowszechnionego i dostępnego bezpłatnie systemu GNU Plot. Wybrane zostały cztery przypadki, dla których znane są rozwiązania analityczne (Z-I.1-Z-I.4); daje to możliwość oceny, na ile wiarygodne są nasze rozwiązania numeryczne. Ostatnia z na szych „zabaw” (Z-I.5) jest całkowicie ogólna, ale nie mamy w tym przypadku rozwią zań analitycznych dla porównania.
WSTĘP: DLA KOGO TA KSIĄŻKA
13
Część II. Jeszcze trochę prostej fizyki, elementy programowania obiektowego W tej części zaczynamy od polepszenia naszego modelu: dodajemy jeszcze trochę fizy ki, także prostej, częściowo znanej z programu licealnego. Pogłębiamy opis oporu po wietrza, wprowadzamy efekty związane z ruchem na obracającej się Ziemi (lub dowol nej innej planety), a także z przekraczaniem bariery dźwięku. Niektóre z omawianych w tej części modeli i algorytmów testowane były w ramach kilku kierowanych przeze mnie dyplomowych prac inżynierskich z informatyki stosowanej [17-19]. Odpowiedni, ogólniejszy program obliczeniowy formułujemy w języku wektorów w przestrzeni trój wymiarowej, do czego przydaje się także mała powtórka z geometrii. Jest to okazja do wprowadzenia podstawowych elementów programowania obiektowego: klas i obiektów, konstruktorów i destruktorów, przeciążania operatorów. Po wstępnych rozważaniach dotyczących tych koncepcji formułujemy, podobnie jak w części I, program w wersjach P, C i F. W rozdziale P-II program formułujemy w Pascalu w ramach kompilatora FPC w sys temie Lazarus (LP), ponieważ Pascal w wersji Delphi (DP) nie ma możliwości przecią żania operatorów. Nie powtarzamy „elementarza” języka z części I, omawiamy jedynie nowe elementy związane z użyciem klas. Program jest obszernie komentowany w tek ście: podstawowe komentarze są częścią tekstu źródłowego, a dodatkowe obszerniejsze komentarze ujęte są w ramkach - nie występują one w tekście źródłowym programu. Dla osób, które woląpozostać w ramach systemu Delphi, formułujemy wersję D w języ ku typów obiektowych (bez klas, mimo iż w Delphi są one oczywiście dostępne) i bez korzystania z przeciążania operatorów. W rozdziale C-II program formułujemy w C++ w systemie UNIX/LINUX. Nie po wtarzamy „elementarza” języka z części I, omawiamy jedynie niezbędne elementy pro gramowania obiektowego. Program jest obszernie komentowany, na takich samych zasadach jak w części I. Merytorycznie komentarze te są powtórzeniem komentarzy w wersjach P/D. W rozdziale F-II program formułujemy w F9x w systemie UNIX/LINUX. Nie powta rzamy „elementarza” języka z części I, omawiamy jedynie nowe elementy związane z technikami obiektowymi, które w F sąnieco odmienne od P i C. Program jest obszer nie komentowany, na takich samych zasadach jak w części I. Merytorycznie wszystkie te komentarze są powtórzeniem komentarzy w wersjach P/D i C. Każdy z tych trzech rozdziałów (P/C/F-II) jest w rozsądnym stopniu autonomiczny. Czytelnik zainteresowany tylko jednym językiem programowania może więc praktycz nie bez szkody dla zrozumienia dalszego tekstu pominąć całkowicie oba rozdziały do tyczące innych języków. Natomiast osoby zainteresowane podobieństwami i różnicami
14
WSTĘP: DLA KOGO TA KSIĄŻKA
w zapisie konkretnych struktur językowych w różnych językach mają możliwość doko nania porównań. Po sformułowaniu tych czterech (P/D/C/F) wersji programu przystępujemy w rozdziale Z-II do trochę poważniejszych „zabaw”. Tym razem nie dysponujemy żadnymi rozwią zaniami analitycznymi, dysponujemy jednak różnego rodzaju danymi „globalnymi” do tyczącymi realnych wydarzeń. Próbujemy te dane odtworzyć, modelując te wydarzenia naszym, w gruncie rzeczy dość prostym, programem. Przykłady podzielone są na pod rozdziały: Z-II.l. Skoki - ze spadochronem; Z-11.2. Rzuty - strzelanie z działa, także do ruchomego celu; Z-I1.3. Rakiety, w tym z wykorzystaniem pewnej techniki napro wadzania rakiety na cel, także ruchomy. Podsumowaniem zagadnień programistycznych jest umieszczone na końcu książki Kompendium konstrukcji językowych - zestawienie w formie tabelarycznej wszyst kich konstrukcji P, C i F użytych w tej książce z pewnymi uzupełnieniami i rozszerze niami, a dla Fortranu także z uwzględnieniem konstrukcji starszej, ale ciągle szeroko używanej wersji F77, stanowiącej integralną część F9x. Parę uwag końcowych to ogól niejsza refleksja nad zawartym w książce materiałem.
I. Trochę prostej fizyki, proste programowanie
LI. Położenie jako funkcja czasu Najpierw przypomnimy sobie trochę wiadomości ze szkolnego (licealnego) kursu fi zyki. Zapewne każdy pamięta z takiego kursu wzór na drogę x(t) w ruchu jednostajnie przyspieszonym jako funkcję czasu t: (1) x(t) = x 0 + v * t + V2*ag*t2 gdzie xg to położenie dla t = 0, vg- (stała) prędkość, a0 - (stałe) przyspieszenie; zgodne z wymaganiami języków programowania, wszystkie operatory, także we wzorach, będą wpisywane jawnie: * to operator mnożenia, / to operator dzielenia, (.. .)2 - podnoszenie do kwadratu, V(...) - pierwiastek kwadratowy itp. Ci, którzy zetknęli się już z podstawowymi elementami rachunku różniczkowego, wie dzą, jak można funkcję (ciągłą i różniczkowalną) rozwinąć na szereg jej pochodnych w otoczeniu wybranego punktu na osi zmiennej niezależnej. Jeśli więc znamy położenie punktu dane przez jego wektor wodzący r (tzn. kolekcję 3 składowych skalarnych, naj częściej oznaczanych x, y, z) w chwili czasu t, to położenie po „małym” przyroście cza su o h można wyrazić jako: (2) r(t + h) = r(t) + (dr/dt)*h + ’/2*(cfr/dt2)*h2 + .... Pierwsza pochodna drogi po czasie (dr/dl) to właśnie prędkość v w chwili t, a druga po chodna: (cPr/dt2) = (dv/dt) (czyli pierwsza pochodna prędkości po czasie) to przyspiesze nie a w chwili t. Wzór (1) jest więc szczególnym przypadkiem ogólnej formuły (2), wte dy gdy ruch jest 1-wymiarowy (tylko wzdłuż osi x), a prędkość vg i przyspieszenie ag są stałe. Możemy teraz napisać (2) w alternatywnej postaci: (3) r(t + h) = r(t) + v(t)*h + / 2*a(t)*h2 A co z kropkami... na końcu (2)? Dynamika - część mechaniki zajmująca się przyczy nami ruchu - poucza nas, że wszystkie zjawiska związane z ruchem dają się poprawnie
16
I. TROCHĘ PROSTEJ FIZYKI, PROSTE PROGRAMOWANIE
opisać przez znajomość tylko przyspieszenia, wyższe pochodne położenia po czasie nie są w tym wypadku potrzebne - o tym za chwilę. Warto wspomnieć tu, że numeryczna analiza funkcji ciągłych (tu: składowych skalarnych wektora r(t)) praktycznie zawsze oznacza ich dyskretyzacje, tzn. zastąpienie przez linię łamaną z jakimś (najczęściej sta łym) krokiem w zmiennej niezależnej, tradycyjnie oznaczanym h. Jakość takiej aprok symacji zależy oczywiście od wybranego kroku: im jest on mniejszy, tym jest lepsza; ale dzieje się to kosztem nakładu obliczeniowego, który może być poważny: potrzebny jest więc tu rozsądny kompromis. Prędkość v(t) można oczywiście także rozwinąć na szereg podobny do (2) i „obciąć” go podobnie jak w (3):
17
1.3. Co to jest równanie różniczkowe zwyczajne
w sobie także I zasadę: jeśli na ciało nie działa żadna siła (wypadkowa), to nie dozna je ono żadnego przyspieszenia, czyli stan jego ruchu nie ulega zmianie - jak mawiano w szkole. Dla przyjętej wyżej numeracji współrzędnych możemy więc zapisać tę zasa dę wzorem: (7) a/t) = f/t)/m
j = l,...k
f . to składowe siły działającej na ciało, m - jego masa. Czemu jednak nie ma zależności masy od czasu? W takim sformułowaniu II zasady dynamiki istotnie masa jest stała. No ale w tej książce zamierzamy zajmować się także rakietami, których masy z założenia są zmienne w czasie... Zajmiemy się tym w punkcie 1.6. dotyczącym rakiet.
(4) v(t + h) = v(t) + (dv/dt)*h + ... = v(t) + a(t)*h W tej książce będziemy zajmowali się ruchem obiektów fizycznych, które można mo delować przez pojedyncze punkty. Wszystkie omawiane dotychczas wektory będą więc miały 3 składowe skalarne - x, y, z, ale wygodniej nam będzie zamiast tych tradycyj nych oznaczeń po prostuje ponumerować: 1, 2, 3. Wzory (3) i (4) można więc będzie za pisać w formie: (5) v ft + h)= v/t) + a/t)*h
j = 1, ... k
(6) r /t + h) = r/t) + v/t)*h + /2*a/t)*h2 W tych wzorach liczba zmiennych niezależnych k wcale nie musi być równa 3: jeśli mamy zagadnienie ruchu 1 punktu na płaszczyźnie, to k = 2, jeśli tylko w jednym wy miarze, to k= 1. A gdy przejdziemy do zagadnień wielopunktowych, ten sposób zapisu będzie nadal ważny. Przy okazji zamieniona została kolejność wzorów: najpierw pręd kość, potem położenie; to może okazać się użyteczne.
1.2. II zasada dynamiki Newtona To chyba też każdy pamięta ze szkoły: Przyspieszenie jakiego doznaje ciało je st wprost proporcjonalne do siły na nie działa jącej i odwrotnie proporcjonalne do jego masy To fundamentalne stwierdzenie, znane jako II zasada dynamiki Newtona (zawarte w jego dziele Philosophiae naturalis principia mathematica wydanym w roku 1687), jest wnioskiem z obserwacji i doświadczeń: tak zachowują się realne ciała. Zawiera ono
1.3. Co to jest równanie różniczkowe zwyczajne Na razie zajmiemy się ruchem punktu o stałej masie. Łącząc II zasadę dynamiki w po staci (7) z wzorami (5), (6 ), otrzymujemy przepis na wyznaczenie położenia i prędkości w czasie t + h, jeśli znamy te wielkości w chwili t i układ sił działających w tym mo mencie: (8) v /t + h )= v /t) + (f/t)/m)*h
j = 1, ... k
(9) r /t + h) = r/t) + v/t)*h + ‘/2*(f/t)/m)*h2
W tym układzie równań niewiadomymi (i poszukiwanymi) wielkościami są funkcje r/t), określone w (9) przez zadany układ sił f/t) i znane - przez wzór (8) - prędkości v/t) - pochodne poszukiwanych funkcji. Równanie, w którym nieznana funkcja jednej zmiennej określana jest przez swoje pochodne, nazywa się równaniem różniczkowym zwyczajnym {ordinary differential equation - ODE). Jeśli równocześnie poszukujemy kilku funkcji tej samej zmiennej (jak w naszym przypadku: wszystkich składowych skalarnych wektora wodzącego jako funkcji czasu), to równania dla poszczególnych funkcji mogą być wzajemnie sprzężone przez zależność każdego z nich od pochodnych wszystkich szukanych funkcji. Będzie to układ równań różniczkowych zwyczajnych. Brzmi to trochę groźnie, ale nie przerażajcie się: mimo iż teoria równań różniczkowych jest raczej zaawansowanym działem analizy matematycznej, a na rzecz konstruktorów samolotów i rakiet, a także prognozowania pogody, falowania i pływów morskich, wy znaczania trajektorii satelitarnych itp. pracują całe instytuty (często utajnione...), w któ rych najpotężniejsze komputery rozwiązują właśnie układy równań różniczkowych, nasz układ równań Newtona (8,9) jest wyjątkowo prosty i numerycznie łatwo rozwiązywalny.
18
I. TROCHĘ PROSTEJ FIZYKI, PROSTE PROGRAMOWANIE
1.4. Jak można to najprościej rozwiązać: metody Eulera i Verleta Wzory (8,9) stanowią bezpośredni przepis na wyznaczanie prędkości i położenia w chwili t+h, jeśli znamy te wielkości, a także wartości przyspieszeń (lub - co na jedno wychodzi —wartości wszystkich działających sił) w chwili t. Wzory te stanowią algo rytm rozwiązywania równania Newtona metodą Eulera (zob. np. [20]) dość oczywistą, ale powszechnie uważaną za mało dokładną. Formalnie ze wzoru (9) położenie może być wyznaczone z dokładnością nie lepszą niż h3, a prędkość ze wzoru ( 8) - z dokład nością nie lepszą niż h2. Jednak prędkość wchodzi do (9), więc metodą Eulera położenie wyznaczyć można z dokładnością rzędu tylko h2. Zauważmy przy okazji, że jeśli reali zując metodę Eulera, najpierw obliczymy „nowe” prędkości (8 ), to obliczając nowe poło żenia (9), możemy już te nowe prędkości wykorzystać, albo nawet użyć w (9) „ważoną” średnią „starych” i „nowych” prędkości: (10) r /t + h) = r/t) +(P*v/t) + (1- P)*v/t+h)) *h + A*(f/t)/m)*h2
j = 1, ... k
P jest tu dowolnie wybraną stałą z przedziału między 1 (standardowy algorytm Eulera) a 0 - użycie tylko „nowej” prędkości v(t+h); P = 'A oznacza użycie średniej arytmetycz nej obu prędkości. Czy to jedyna prosta możliwość rozwiązywania równania Newtona? Okazuje się, że można zaproponować metodę prostszą i formalnie dokładniejszą. W tym celu napiszmy wzór (6 ) dla kroku - h , czyli wykonanego „wstecz”: (11)
r/t - h) = r/t) - v/t)*h + A*a/t)*h2
j = 1, ... k
Dodając stronami (6 ) i (11), otrzymamy: (12)
r/t+h) + r/t-h) = 2*r/t) + a/t)*h2
r/t+h) = 2*r/t) - r/t-h) + a/t)*h2
krok Eulera, r(t) z tego kroku przyjąć jako r(t-h) dla metody Verleta, a r(t+h) - jako r(t) dla tej metody, w wyniku której na podstawie wzoru (13) otrzymamy r(t+h). Wzór (13) może być stosowany w sposób w pewnym sensie podobny do „ważenia” w ( 10), przez wprowadzenie parametru P umożliwiającego np. wygaszanie sztucznych oscylacji, któ re czasem pojawiają się w procesie całkowania równań różniczkowych: (14) r/t+h) = (l+ p)*r/t)-p* r/t-h) + a/t)*h2
j = 1, ... k
P jest tu dowolnie wybraną stałą z przedziału 1 („czysty” Verlet) - 0 (całkowicie „tłu miony” Verlet); ten wzór będzie realizowany w programie, jest on też wykorzystywany w MD. No, ale prędkość jednak będzie potrzebna: i w MD np. do obliczenia energii kinetycznej, i w naszych „zabawach” - do obliczania np. oporu ośrodka. Odejmując stronami równa nia (6 ) i ( 11), otrzymamy przepis na jej obliczenie: (15) v/t) = A*(r/t+h) - r/t-h))/h Ci, którzy poznali już podstawy rachunku różniczkowego, łatwo zauważą, że wzór ten wyraża po prostu pierwszą pochodną poszukiwanej funkcji r(t) jako średnią arytme tyczną lewo- (t-h) i prawo- (t+h) stronnych aproksymacji numerycznych tej pochodnej; nie powstaje tu jednak żadna „nowa” prędkość, to jest po prostu prędkość w chwili t\ tym samym wzór ten nie gwarantuje tej samej dokładności co (13), formalnie ma on do kładność h3, mimo iż w wyniku odejmowania formuł „wycięte” zostają z takiej aprok symacji wszystkie pochodne parzystego stopnia. Jeśli jednak potrzebna jest nam „nowa” prędkość, możemy ją obliczyć formalnie ze wzoru (5), dodając do (15) przyczynek od przyspieszenia w chwili t, który już i tak znamy.
j = 1, ... k
1.5. Najprostszy test - rzut ukośny
skąd możemy obliczyć r/t+h): (13)
19
1.5. Najprostszy test - rzut ukośny
j = 1, ... k
Zauważmy, że w tym równaniu prędkość w ogóle nie występuje, więcej - nie wystąpi łyby też i dalsze pochodne nieparzystego rzędu położenia; zatem dokładność takiej me tody wzrasta do rzędu h4. Równanie (13) jest podstawą metody Verleta [21] całkowania (rozwiązywania) układów równań Newtona, i jest szeroko wykorzystywana w meto dach dynamiki molekularnej (MD —Molecular Dynamics, zob. np. [22,23]). Jak dłu go nie musimy obliczać prędkości, tak długo jest miło, chociaż mamy drobny problem: skąd wziąć na starcie r(t-h)1 Nie ma rady: na starcie trzeba wykonać dokładnie jeden
Punkt materialny to punkt matematyczny wskazywany przez wektor wodzący r, obda rzony masą m (punkt fizyczny, punkt „masywny”). Nasz najprostszy przykład testowy to punkt materialny poruszający się w polu siły ciężkości, czyli dobrze znany ze szko ły rzut ukośny (czasem może być też poziomy lub pionowy, to zależy tylko od wyboru warunków początkowych). Dla punktu o masie m równanie Newtona ma w tym przy padku postać: (16) m*a=m*g, czyli przyspieszenie a = g
20
I. TROCHĘ PROSTEJ FIZYKI, PROSTE PROGRAMOWANIE
g to wektor przyspieszenia grawitacyjnego o długości (na Ziemi, średnio) g = 9.81 m/s2. Nic jednak nie stoi na przeszkodzie żebyśmy, nie ruszając się sprzed komputera, porzu cali sobie na Księżycu (g = 1.63 m/s2), Marsie (g = 3,69 m/s2), Jowiszu (g = 20.87 m/s2) a nawet na Słońcu (g = 273.95 m/s2). Wektory a i g są więc współliniowe, a przyspiesze nie nie zależy od masy punktu. Użyliśmy zapisu wektorowego, ale można pokazać że ruch taki, na przyjętym tu poziomie modelowania, jest płaski, dwu-wymiarowy (model 2D): wynika to z zasady zachowania momentu pędu w tego rodzaju ruchu (zob. np. [24]). Wybierając na płaszczyźnie ruchu oś r, jako poziomą, oś r ja k o pionową, skierowaną do góry, składowe wektora przyspieszenia zapiszemy jako: (17) at = 0 - grawitacja działa tylko pionowo, a2 = - g - grawitacja działa „w dół”. Stałe fizyczne (mianowane!) m, g, które tu wprowadziliśmy, są przykładem parametrów „materiałowych”, które czynią z czysto matematycznych równań różniczkowych - rów nania różniczkowe fizyki, modelujące pewną rzeczywistość materialną. Analityczne rozwiązania układu równań (17) są doskonale znane ze szkoły: (18) r f i) = r0I + V01*t
Vf i ) =V0,
r2( t ) = r 02 + v0 * t - / 2*g*t2
1.6. Rakieta jako punkt o zmiennej masie: III zasada dynamiki Newtona
21
1.6. Rakieta jako punkt o zmiennej masie: III zasada dynamiki Newtona Zacznijmy od przypomnienia III zasady dynamiki Newtona, niewątpliwie znanej z fi zyki szkolnej: Jeżeli ciało A działa na ciało B siłą fAB, to ciało B działa na ciało A z siłą fBA, równą co do wartości bezwzględnej, lecz przeciwnie skierowaną: f AB = - f BA Nie jest istotne, które z tych ciał nazwiemy A, które B - oddziaływanie jest wzajemne i symetryczne. Czy to ma coś wspólnego z ruchem rakiety? Ano tak: rakieta porusza się na koszt ubytku swojej masy, więc rakieta (tracąca masę) może być „ciałem A”, a odrzu cana (a lepiej - wyrzucana) masa - „ciałem B”. Traktując układ rakieta + gazy napędo we jako układ zamknięty wiemy ponadto, że pęd takiego układu jest zachowany, zmiana pędu takiego układu może być tylko wynikiem działania sił zewnętrznych, w tym wy padku —siły ciężkości m*g. Przyjmijmy, że w chwili t rakieta ma względem „ziemi”, czyli „nieruchomego” (uwaga: to nie jest takie oczywiste, zob. dalej I I .3) układu współrzędnych, w którym opisujemy ruch, prędkość v(t), a gazy napędowe (czyli wyrzucana masa) mają względem rakiety prędkość w(t)\ to znaczy, że ich prędkość w równaniach ruchu „względem ziemi” wyno si w(t) + v(t). Tu trzeba jednak zauważyć, że:
V2(t) =V02-S*t ■ jest to suma wektorów, a nie skalarów;
r0l, rg2 to współrzędne punktu startowego; vgp vg2 to składowe prędkości na starcie: naj wygodniej jednak dla prędkości podawać nie składowe kartezjańskie (1, 2) - które tra dycyjnie oznaczać można także (x, y), ale wartość vg i kąt rzutu a, wówczas: O9) V = v*cos(a),
vB2 = v*sin(a)
Znane są również ze szkoły wzory na zasięg poziomy rzutu d i wysokość w [24]: (20) d = vg2*sin(2*a)/g,
■ wektory te, zgodnie z III zasadą dynamiki, mająten sam kierunek, ale przeciwne zwroty (jeśli to rakieta balistyczna, niesterowana), tzn. że kąt między nimi wy nosi 180°;
w = v*sin2(a)/(2*g)
Zespół wszystkich tego rodzaju parametrów, a także innych ustalonych wielkości, które wystąpią w układzie równań różniczkowych, najlepiej będzie zorganizować jako pew ną strukturę danych, którą można łatwo przekazywać pomiędzy poszczególnymi seg mentami programowymi. Przyjmujemy jako dobrą zasadę programowania, że wszystkie czynności dotyczące struktur danych formułujemy jako podprogramy; takie podejście jest wyrazem proceduralności w programowaniu.
■ jeśli rakieta jest sterowana, kąt miedzy tymi wektorami może zmniejszyć się o najwyżej kilka stopni; sterowanie rakietą to bardzo delikatna sprawa, odby wa się przez niewielkie odchylanie strumienia gazów napędowych od osi rakiety (sterowanie strumieniowe), zob. II.4.3. Wykorzystując II zasadę dynamiki w postaci ogólniejszej niż (7): zmiana pędu równa się popędowi siły działającej (popęd siły to iloczyn siły przez czas jej działania), napiszemy dla rakiety zmianę pędu w (małym) czasie dt, w którym traci ona (małą) masę dm:
22
I. TROCHĘ PROSTEJ FIZYKI, PROSTE PROGRAMOWANIE
(21) dp = f*dt + dm * (v + w)
1.7. Ruch w powietrzu: najprostsze modelowanie oporu ośrodka
23
(27) r/t) = r02 - Z2*g*f + ms*w*{u(t)*[ln\u(t)\ - 1] + l}/c
drugi człon po prawej stronie to właśnie uciekająca z prędkością v + w masa dm. Prze chodząc do pochodnych po czasie otrzymamy, zgodnie z zasadami różniczkowania ilo czynu: (22) (dp/dt) = / + (v + w)*(dm/dt) = d(m*v)/dt - v*(dm/dt) + m*(dv/dt)
(28) v/t) = v02 - g*t + w*ln\u(t)\ Wzory te obowiązują oczywiście tylko tak długo, jak długo pracuje silnik rakiety, tzn. do t = tk (24); potem rakieta zachowuje się tak jak punkt o masie mf bez napędu, tzn. wg wzorów (18). Spełnienie wzorów (27,28) przez nasze rozwiązania numeryczne będzie istotnym testem stosowalności użytych tu metod całkowania równania różniczkowego.
Redukując po obu stronach identyczny człon v*(dm/dt), otrzymamy ostatecznie równa nie ruchu rakiety: (23) (dv/dt) = a(t) =f(t)/m(t) + w(t)*(dm(t)/dt)/m(t) Drugi człon po prawej stronie jest przyspieszeniem silnika rakiety —jego wymiar to: [m/s] * [l/kg] *[ kg/s] = [m/s2] - istotnie, jest to wymiar przyspieszenia. W najprostszym modelu założymy, że masa rakiety maleje liniowo w czasie, od masy startowej ms do masy finalnej mf: (24) m(t) - m - c * ł m(t) =mf
dla t > tk
(25) a =f/m - w*c/m c jest prędkością spalania paliwa, wymiarem c jest [kg/s], wymiarem w*c jest [m/s]* [kg/s] = [kg*m/s2] = N (niuton); zatem w*c, a ogólniej \w*dm/dt\ jest siłą ciągu silnika ra kiety. Po podzieleniu przez masę otrzymujemy więc przyspieszenie rakiety. Na to, żeby rakieta mogła w ogóle wystartować, jest konieczne, żeby składowa pionowa tego przy spieszenia była większa niż przyspieszenie grawitacyjne g. Dla ruchu pionowo w górę można otrzymać analityczne wyrażenia na położenie (wyso kość) r/t) i prędkość wznoszenia v/t) (zob. [25], rozdz. XVIII; nie jest to takie całkiem proste...). Wprowadzając funkcję pomocniczą
otrzymuje się:
Zacznijmy od ujęcia najprostszego, nie wnikającego głębiej w zjawisko oporu, jaki sta wia ośrodek ciągły poruszającemu się w nim przedmiotowi. Ktoś powie, że przecież punkt materialny, jako punkt matematyczny, nie ma rozmiarów, a więc i oporu nie może stawiać... No, ale tego rodzaju idealizacje są koniecznym kompromisem przy budowa niu modeli, a sensowność modelu weryfikuje porównanie wyników obliczeń ze sposo bem zachowania się realnych ciał.
dla t < t k = (ms ~m ^/c
wówczas (dm/dt) = -c; tk to cżas pracy silnika rakiety. Równanie ruchu rakiety przyjmie więc postać
(26) u(t) = 1- c*t/ms
T.7. Ruch w powietrzu: najprostsze modelowanie oporu ośrodka
Na pewno sensowne jest stwierdzenie, że opór to siła skierowana „przeciw” ruchowi: współliniowa z wektorem prędkości i mająca przeciwny do niego zwrot, i jakoś do tej prędkości proporcjonalna. Ci, którzy prowadzą samochód (a jeszcze lepiej - jeżdżą na motocyklu) wiedzą, że opór powietrza jest z grubsza proporcjonalny do kwadratu pręd kości: czuje się to przede wszystkim w portfelu - wzrasta istotnie zużycie paliwa...; opór proporcjonalny do samej prędkości występuje tylko w wypadku ruchu powolnego w ośrodku gęstym. No i wystąpi tam jakiś współczynnik proporcjonalności: (29) f° p = -b*v2*v/v = -b*v*v, v = V(v,2 + v / + v f) v to skalarna wartość prędkości - długość wektora v, b jest owym współczynnikiem pro porcjonalności, w tym wypadku o wymiarze [kg/m]; wówczas wymiar prawej strony to [kg/m]*[m2/s2] = [kg*m/s2] = [N] (niuton) - istotnie, jest to wymiar siły, chociaż wymiar b wydaje się trochę dziwaczny... Tu nazwiemy b „efektywnym współczynnikiem opo ru aerodynamicznego” - pewną stałą, którą spróbujemy dobrać metodą eksperymentu obliczeniowego, nie wnikając głębiej, co on w sobie kryje - tym zajmiemy się w części II tej książki. Dla ruchu jednowymiarowego, pionowo w górę lub w dół, można otrzymać rozwiąza nia analityczne równania Newtona z siłą oporu (29) dla składowej pionowej (zob. [25], rozdz. X); nie jest to proste, ale student np. III roku fizyki teoretycznej dałby sobie z tym radę. Zamiast współczynnika b użyjemy stałych (masa m jest tu stała):
24
I. TROCHĘ PROSTEJ FIZYKI, PROSTE PROGRAMOWANIE
(30) s = b/m,
p = '¡(s/g),
q = V(ś*g)
Wówczas dla ruchu w górę: (31) r2(t) = r02 + (ln\v0 *p*sin(q*t) + cos(q*t)\)/s (32) v2(t) = p*[v02*cos(q*t) - (sin(q*t))/p]/[vg2*sin(g*t) + (cos(q*t))/pJ maksymalna wysokość (33) r -
= ln\l+(v0 *p)2\/(2*s)
zostanie osiągnięta po czasie (34) r® = (arctan(v02*p))/q
25
z (30,38) b « 0.39, co dość dokładnie odtwarza tę prędkość; jeśli skoczek z roz winiętym spadochronem ma lądować z prędkością pionową rzędu 5 m/s (10 razy mniejszą), natychmiast otrzymamy, że b ~ 39 (tzn. wzrasta 100 razy); jeśli lą dować (albo raczej wodować), z taką prędkością miałaby kapsuła o masie rzędu 1000 kg (np. lądownik statku kosmicznego), b wzrasta więc 100 -krotnie, do war tości b ~ 390. To oczywiście oznacza odpowiedni wzrost powierzchni spadochro nów - o czym w następnych rozdziałach. ■ Działo przeciwlotnicze Flak 41 kal. 88 mm, wyprowadzone do użytku w roku 1941 i wykorzystywane jeszcze przez wiele lat po II wojnie światowej, nadaje pociskowi o masie 9.4 kg prędkość 1000 m/s; przy strzale pionowo osiągalny był pułap 10675 m [26], W ynik ten jest odtwarzany dość dokładnie przez nasz prosty model (33,34) dla b ~ 0.00114. Układ równań ruchu w przestrzeni 2-wymiarowej dla ciała o stałej lub (liniowo) zmien nej masie, w ramach przyjętego modelu oporu powietrza, przyjmie więc postać, we współrzędnych kartezjańskich:
Dla ruchu w dół: (35) r2(t) = ym - (ln\v0/p*sinh(q*t) - cosh(q*t)\)/s (36) v/t) - - [v02*cosh(q*t) - (smh(q*t)}/p]/(p*[v02*sinh(g*t) - (cosh(q*t))/pJ) sinh, cosh to sinus i cosinus hiperboliczny; niektóre kompilatory mają te funkcje w swo ich bibliotekach matematycznych, ale w razie ich braku łatwo je obliczyć: (37) sinh(x) = (e* -e~x)/2,
1.7. Ruch w powietrzu: najprostsze modelowanie oporu ośrodka
cosh(x) = (e? + e~x)/2.
Teoretyczną granicą prędkości spadania będzie: (38) v2as = - l/p. Dość paskudne są te wzory, ale ich spełnienie przez rozwiązania numeryczne będzie su rowym testem stosowalności przedstawionych tu metod całkowania układu równań róż niczkowych do opisu ruchu z oporem proporcjonalnym do V2. Odpowiednie wzory dla przypadku oporu proporcjonalnego do v są znacznie prostsze (zob. np. [25]). Kilka przykładów jak ten bardzo uproszczony model oporu powietrza pracuje: ■ Dla skoczka spadochronowego przy wysokościach rzędu 1000 m n.p.m. (nad po ziomem morza) wiadomo, że graniczna prędkość spadku swobodnego (bez spa dochronu) jest rzędu v “s ~ 50 m/s; przy masie skoczka rzędu 100 kg otrzymujemy
(39) a/t) = [c*w - b*\/(t)] /m(t)*v/t)/v(t) a/t) =[c*w- b*v*(t)]/m(t)*v/t)/v(t) - g v = 4 (v/ + v/) Znak pierwszego członu na prawych stronach jest dodatni, bo jest to przyspiesze nie zwiększające prędkość, wynikające z ubytku masy, a wektor w ma stałą długość w i zwrot przeciwny do wektora prędkości v; drugi człon jest ujemny, bo jest to hamo wanie ruchu, także współliniowe z wektorem v. Oczywiście wektor prędkości v stale zmienia kierunek i długość w trakcie ruchu, stąd składowe wersora prędkości v/v wy stępujące po sumie składników skalarnych są funkcjami czasu (wersor danego wektora to wektor o takim samym kierunku i zwrocie co ten wektor, ale o długości 1); wektor g skierowany jest oczywiście zawsze w dół, stąd -g. Na razie dosyć teorii, możemy przystąpić do budowania programu komputerowego, któ ry będzie realizował przedstawiony tu model matematyczny naszego problemu.
1.8. Przystępujemy do zabawy z komputerem - zaczynamy programować
1.8. Przystępujemy do zabawy z komputerem - zaczynamy programować Program, który umożliwi nam proponowane zabawy, a jednocześnie posłuży do prze testowania stosowalności algorytmów Eulera i Verleta do skutecznego rozwiązywania układów równań różniczkowych (39), zapiszemy w wybranych trzech językach progra mowania. We Wstępie zaznaczyłem, że nie zakładam, iż czytelnik tej książki posiada praktyczną umiejętność programowania w jednym z używanych tu języków. Dlatego mam nadzieję, że czytelnicy wybaczą mi wprowadzone przy każdej wersji językowej podstawowe uwagi o pisaniu programów w tych językach - dla tych, którzy danego ję zyka nie znają. Przy okazji warto też ustalić sprawy terminologiczne. Każdy program lub podprogram (segment programowy) posiada część deklaracyjną, która określa „na czym będziemy działać”, a polecenia występujące w tej części są nazywane „instrukcjami biernymi” dyrektywami, wykonywanymi w trakcie przygotowania tego segmentu przez kompu ter; oraz część operacyjną, której polecenia - „instrukcje czynne”, czyli po prostu in strukcje, wykonywane są w trakcie realizacji algorytmu; nad tym wszystkim czuwa system operacyjny, którego polecenia to komendy. Część deklaracyjną i operacyjną do brze jest zawsze rozdzielić pustą linią lub odpowiednim komentarzem. Pascal jest językiem hierarchicznym: program zawiera jako swoje elementy wewnętrz ne wszystkie potrzebne podprogramy i jest kompilowany jako całość, proces konsoli dacji (ilinking) nie jest jawny, w jego wyniku otrzymuje się gotowy moduł wykonaw czy: nazwa.exe w systemie Windows (np. Turbo-Pascal, Delphi, Lazarus) lub nazwa.* (z atrybutem x) w systemie UNIX/LINUX (np. kompilator FPC). Fortran i C to języki modularne: program powstaje jako kolekcja segmentów, wśród których tylko jeden jest „programem głównym” (z domyślną nazwą main), pozostałe są podprogramami. Każdy segment może być odrębnie kompilowany do postaci kodu po średniego {object code)', następnie kolekcja kodów pośrednich jest konsolidowana {link, „linkowanie”) z segmentem głównym, tworząc program wykonawczy. Tu od razu po zwolę sobie na uwagę techniczno-typograficzną: Fortran, C, system operacyjny typu UNIX - to najbardziej uniwersalne (szczególnie dla „obliczeniowca”) systemy pracy z komputerem; jeśli chcemy być możliwie niezależni, bez kłopotów i przeróbek urucha miać swoje programy na różnych komputerach - lepiej nie wiązać się z żadnym rozbu dowanym środowiskiem uruchomieniowym, z obudowami typu „borlandowskiego” itp. Jestem zwolennikiem „środków ubogich” w pisaniu programów, dzięki temu wiem dość dokładnie, co tak naprawdę dzieje się z moim programem i co mam z nim zrobić w no wym miejscu działalności, co w mojej karierze naukowej zdarzało się wielokrotnie. Tak też uczyłem moich studentów: niech każdy używa takiego edytora, w którym czuje się
27
najlepiej, i pisze uważnie - nie będzie miał problemów z mało przyjazną diagnostyką błędów.
1.8.1. Wybór struktur danych Mamy w naszych wzorach dość dużo parametrów fizycznych, „materiałowych”, któ re z abstrakcyjnych równań matematyki czynią równania modelujące pewną rzeczy wistość materialną: przyspieszenie grawitacyjne g; masy: początkowa ms i końcowa mp (por. (24)); prędkość gazów napędowych w i prędkość spalania c (25,39); efektywny współczynnik oporu b (29); a ponadto kilka stałych użytecznych wielkości wynikają cych z tych parametrów. Dla sprawnego przekazywania tych parametrów między seg mentami programowymi najlepiej jest zgromadzić je w pewnej strukturze danych. W budowanych w tej części książki programach o charakterze testowym użyta będzie najprostsza struktura umożliwiająca gromadzenie danych różnych typów - w takim sensie struktura heterogeniczna, która w Pascalu nazywa się rekordem (record), w C - strukturą (struct), w F9x - typem pochodnym {derived type) definiowanym w ra mach modułu (module). Dla uproszczenia wszystkie je będę tu nazywał „strukturami rekordowymi” (stąd fr J w nazwach programów). Alternatywą byłoby użycie do tego celu obiektów (object) lub klas (class), wraz z mechanizmem konstruktorów (con structor), destruktorów (destructor) i innych metod własnych obiektu/klasy. Mamy tu jednak do czynienia z bardzo prostymi zagadnieniami programistycznymi, które nie wymagają stosowania wielopoziomowych struktur danych, mechanizmów dziedzicze nia czy wskaźników lub struktur dynamicznych. Niektóre z tych elementów programo wania obiektowego użyte zostaną w części II.
P-I. Pascal: program Pr ev test.dpr
29
P-I. Pascal: program Pr_ev_test.dpr Przystępujemy do pisania programu jako aplikacji konsolowej w Delphi. Jeśli ktoś z Delphi nie korzystał - rada, jak zacząć: klikamy na odpowiednią ikonę na ekranie i idziemy przez ciąg zakładek: Plik | nowy | inny | aplikacja konsolowa
Otrzymamy w wyniku standardowy „szablon”
program Projectl; {$APPTYPE CONSOLE) uses SysUtils; begin { TODO -oUser -cConsole Main : Insert code here } end.
Najlepiej od razu przezwać go (plik | zapisz jako) Pr_ev_test: jesteśmy gotowi do pisania.
Na początku programu w Pascalu musi wystąpić preambuła, w której określimy, po słowie kluczowym program, nazwę programu, w tym wypadku, zgodnie z omówioną wyżej konwencją nazewniczą, Pr_ev_test. Przypomnijmy, że nasze programy pascalowe nie mogą być skompilowane w Turbo-Pascalu (TP); dla wyraźnego zaznaczenia tego faktu używane będą komentarze w formie //komentarz do końca linii, która w TP nie jest dopuszczalna. Nazwa programu może być także nazwą pliku źródłowego (w Delphi (DP): Pr_ev_test.dpr - od Delphiproject, w Lazarusie (LP) *.lpr - od Lazarus project) - tak jest najpraktyczniej, choć nie jest to konieczne. Listing Pr-prolog: program Pr_ev_test; {$APPTYPE CONSOLE) uses SysUtils;
const max=10; type T_FL=double; P_T_Al=array [l..max] of T_FL; P_T_PAR= record neq:integer; // liczba równań l_rak,l_op:boolean; // true: rakieta, opor włączony g,sm,fm,w,c,b,f_rak,am,t,deg:T_FL; { g[m/A2]: przyspieszenie grawitacyjne; sm,fm[kg]: masa początkowa, końcowa; w[m/s]: prędkość gazów napędowych; c[kg/s]: prędkość spalania paliwa; opor -b*vA2, b: efektywny współczynnik oporu; f_rak=w*c[N]: silą ciągu rakiety; am=sm-c*t[kg]: masa chwilowa; t[s]: czas) end; P_T_START= record isel,nrec:integer; // selektor przypadku, parametr zapisu na plik beta,h,x0,y0,v0x,v0y:T_FL; // parametry numeryczne i dane startowe l_elr,l_ver,l_up,l_dn:boolean; // flagi nfl:TextFile; end; P_T_E RR_ANA L= record ro,x_anal,y_anal,vx_anal,vy_anal, erx,ery,evx,evy:T_FL // rozwiązania analityczne i błędy end;
Po każdym fragmencie programu, w ramach „elementarza” języka, przedstawione bę dzie objaśnienie użytych konstrukcji językowych: * dyrektywa kompilatora {$APPTYPE CONSOLE) mówi, że jest to „aplikacja konso lowa”, to znaczy, że nie będzie tu wykorzystywane środowisko graficzne, w Del phi naturalne; dyrektywa uses informuje kompilator, które biblioteki systemo we zostaną włączone do programu: obie te dyrektywy są generowane przez DP lub LP automatycznie, w wersji dla FPC/UNIX należy je usunąć (lepiej: zakomentować);
30
I. TR O C H Ę PR O ST EJ F IZ Y K I, PROSTE PROGR A M OW A N IE
•
const max definiuje stałą dostępną w całym programie (globalną);
■ typ globalny (type): T_FL=double - oznacza, że wszystkie zmienne tego typu będą „podwójnej precyzji” (dokładność 15 pozycji dziesiętnych). Tu uwaga: w stosowanej tu wersji DP/LP typy real i double są identycznymi 8-bajtowymi (8B) typam i zmiennopozycyjnymi, zgodnymi ze standardem IEEE 754 [16]; inny typ zmiennopozycyjny zgodny z tym standardem to single (4B, dokład ność 5 pozycji, „pojedyncza” precyzja). Warto tu zauważyć, że znany z TP typ real (6B = IB cecha + 5B m antysa, dokładność 11 pozycji dziesiętnych) może być także używany w DP/LP, jednak pod nazwa real48, budowa wewnętrzna i arytm etyka tego typu nie jest zgodna ze standardem IEEE; ■ konstruktor statycznego typu tablicowego jest w Pascalu postaci: -
T_tablicowy=array [początek..koniec] of T_elementów,
-
początek i koniec są literałami wybranego typu porządkowego; tablica wieloindeksowa linearyzowana jest (tzn. alokowana w pamięci) w leksykalnej (alfabetycznej) kolejności zespołu indeksów, w szczególności tablica 2-ideksowa zapisywana jest wierszami;
■ w budowanych w tej części programach wszystkie typy i zmienne tablicowe 1-in deksowe m ają nazwy zakończone ‘1’; zatem P_T_A1 definiuje tablice 1-indeksowe o elementach numerowanych od 1 do max, zawierające wartości typu T_FL; ■ w strukturze rekordowej P_T_PAR (typu record) zgromadzone są wszystkie wspomniane wyżej param etry fizyczne zagadnienia, ale także włączone tu zo stały: liczba równań neq, zmienne logiczne (typ boolean) l_ ra k - wskazująca, czy mamy do czynienia z rakietą (wówczas masa am będzie zmienna z czasem, wg (24)), i l_op - wskazująca, czy włączony będzie opór powietrza; ze względu na możliwą zmianę masy do struktury tej został także włączony czas t; ■ komentarze mogą być też postaci { ... }, (czyli w formie takiej jak w TP) - jest to praktyczniejsze niż / / ... (w TP niedopuszczalne), wtedy gdy komentarz obejmuje więcej linii; komentarze w wersjach P są w języku polskim. Typ globalny P_T_START gromadzi jako strukturę rekordową wszystkie parametry okre ślane w procedurze startowej i nie zmieniane dalej w trakcie obliczeń: położenie i pręd kość na starcie, które znajdujemy także w rozwiązaniach analitycznych (18-20, 26-28, 30-38): x0, y0, v0x, v0y; ponadto zmienne logiczne i całkowite określające sto sowane algorytmy, oraz parametry dotyczące zapisu wyników na plik. Typ globalny P_T_ERR_ANAL gromadzi w formie struktury rekordowej wszystkie dane o różnicach (bezwzględnych i względnych - procentowych) m iędzy rozwiązaniami numerycznymi i analitycznymi; wykorzystywany będzie na bieżąco w procesie rozwiązywania układu
P-I. Pascal: program Pr ev test.dpr
31
równań i raportowania wyników. Wszystkie te typy danych w ystąpią pod analogiczny mi nazwami we wszystkich wersjach językowych, chociaż szczegółowa postać ich defi nicji będzie w każdym języku specyficzna.
P-I.l. Inicjowanie struktur danych Skoro mamy już zdefiniowane struktury rekordowe, trzeba nadać wartości ich elemen tom. W obiektach lub klasach służą do tego specjalne procedury, zwane konstrukto rami (constructor). Ponieważ jednak tu ograniczyliśmy się do struktur rekordowych, nadanie wartości początkowych zrealizowane zostanie w postaci procedur (procedu re) w DP/LP, funkcji typu void w C++ i procedur zwanych w Fortranie subrutynami (subroutine) w F9x. Listing Pr-init procedure P_par_init(var PAR:P_T_PAR); var p:T_FL; begin with PAR do begin neq:=2; deg:=pi/180; write('przyspieszenie grawitacyjne[m/sA2] (>0; 0-9.81): '); readln(g); if g0.0; if (fmsm) then fm:=sm; l_rak:=(fm>0.0) and (fm', arctan(g/sqrt(p*p-1.0))/deg:6:l) end; until f_rak/sm>g; end; repeat write('siła oporu typu -b*vA2; podaj b[kg/m](>=0.0): '); readln(b); until b>=0.0; if b>0.0 then begin l_op:=true; writeln('opor: -',0:10:6,'* vA2') end end end; // P_par_init
Kilka dalszych zasad zapisu kodu źródłowego w Pascalu:
P-I. Pascal: program Pr ev test.dpr
33
sekwencja_instrukcji wykonana zostanie 1 raz, i ewentualnie powtarzana bę dzie tak długo, aż warunek_logiczny przyjmie wartość true;
■ istnieją też pętle sterowane „z góry”: while warunek_logiczny do begin sekwencja_instrukcji end;
najpierw następuje sprawdzenie, czy warunek_logiczny ma wartość true, je śli tak - sekwencja_instrukcji będzie wykonana i nastąpi powrót do badania warunku_logicznego;
■ w obu rodzajach pętli sekwenc ja_instrukc ji musi wpływać na wartość warunku_logicznego; jeśli tak nie jest, mamy do czynienienia z błędem logicznym w programowaniu, który jednak nie jest błędem składniowym - nie jest wykry wany przez kompilator (choć dobry kompilator powinien zasygnalizować w ta kim wypadku ostrzeżenie); wprowadzenie takich pętli, umożliwiających w bar dzo dużym stopniu programowanie bez stosowania skoków jawnych (instrukcji goto...) było istotnym krokiem w rozwoju koncepcji programowania struktu ralnego; starsze jeżyki (np. Algol, F77A) nie oferują takich konstrukcji - nie są w tym sensie strukturalne;
■ spacje (poza napisami c tekst ‘), wcięcia, przejścia do nowej linii - są przez kompilator ignorowane;
* operator koniunkcji (iloczynu logicznego: jedno i drugie) ma postać and, alter natywy (sumy logicznej: jedno lub drugie) or;
■ separatorem instrukcji jest średnik, end lub else; przed else nie wolno stawiać średnika;
■ selektorem elementu struktury rekordowej jest: zmienna_rekordowa.pole (tu byłoby np. PAR.g); jednak w Pascalu można opuścić wspólną część „adresów” pól, grupując odwołania do pól rekordu w instrukcji grupującej: with zm iennarekordowa do jest to nadzwyczaj wygodne i tak właśnie postąpiliśmy, umieszczając całe ciało procedury Pr_par_init wewnątrz instrukcji with PAR do begin ... end; nieste ty, możliwości takiej nie ma w innych językach, trzeba tam stosować odwołania do elementów struktury, podając także ich pełną lokalizację (tzw. nazwy kwali fikowane);
■ sekwencję instrukcji, która ma być traktowana jako całość (instrukcja złożona), ograniczamy nawiasami operacyjnymi begin ... end lub repeat ... until; " selekcja 2 -wartościowa ma postać: if warunekJLogiczny then sekwencja_true [else sekwencja_false]; (część z else jest opcjonalna);
■ zabezpieczenie sensowności podawanych danych zrealizowane zostało w pę tlach sterowanych „z dołu”: repeat sekwencja_instrukcji until warunekJLogiczny
■ ponieważ elementy struktury PAR przyjmują w tej procedurze wartości, parametr ten musi wystąpić na liście nazw/referencji (var PAR); ■ zgodnie z przyjętą tu konwencją typograficzną parametr formalny PAR pisany jest wielkimi literami, chociaż dla kompilatorów Pascala nie ma to znaczenia.
34
I. TROCHĘ PROSTEJ FIZYKI, PROSTE PROGRAMOWANIE
Wystąpiły tu także procedury czytania z klawiatury i pisania na ekran, czyli procedury komunikacji z „konsolą” (we/wy, i/o - input/output). W Pascalu są one postaci: ■ write(lista_parametrów) - wypisuje wartości parametrów z listy (napis: ftekstJ to także wartość - typu string) na ekran i zatrzymuje kursor ekranowy
na końcu linii; ■ writeln(lista...) (skrót od write linę) działa analogicznie, ale kursor przechodzi
do nowej linii; ■ element listy_wyjścia postaci: zmienna:w[:d] oznacza sformatowane wypisa nie wartości zmiennej, na polu o szerokości w (width) znaków; jeśli wystąpi także :d, to część po kropce dziesiętnej zajmie d (decimal) znaków; ■ readln(lista parametrów) - (skrót od read line) czyta wartości podawane na klawiaturę i nadaje je kolejno parametrom na liście; podawane wartości trzeba roz dzielać spacjami, a całość czytania zakończyć przejściem do nowej linii (ENTER).
P-I.2. Realizacja wzorów na przyspieszenie Wartości parametrów potrzebne do obliczeń zostały określone, możemy więc zaprogra mować wzory na przyspieszenie (39). Przyspieszenie jest wektorem, podobnie jak po łożenie i prędkość: do zapisu tych wektorów użyjemy tablic typu P_T_A1, o elementach numerowanych od 1 do neq. Składowe wektora wodzącego r zapisane będą w tablicy Rl, prędkości v w VI, natomiast w Pascalu wartością funkcji będzie tablica Pr_accl: w P wartością funkcji może być bowiem struktura danych. Dlatego Pr_accl jest funkcją typu P_T_A1; jednak w treści tej funkcji działać będziemy na tablicy roboczej al, w prze ciwnym razie niektóre elementy zapisu zinterpretowane zostałyby jako odwołania rekurencyjne do funkcji Pr_accl. Całe ciało funkcji objęte jest instrukcją grupującą with PAR do, dzięki temu odwołania do elementów rekordu PAR nie muszą być kwalifikowane: Listing Pr accel function P_accl(var PAR:P_T_PARjvar R1,V1:P_T_A1):P_T_A1; { PAR - struktura przekazująca parametry modelu Rl - wektor położeń[m] VI - wektor predkosci[m/s] wartością funkcji jest wektor przyspieszenia[m/sA2]} var v,zx,zy:T_FL; al:P_T_Al;
P-I. Pascal: program Pr ev test.dpr
35
begin with PAR do begin al[l]:=0.0; // ax = 0 al[2]: = -g; // ay = -g am:=sm; // dla rzutu swobodnego masa aktualna = masa startowa
Tu kończy się realizacja wzorów (17) - dla rzutu swobodnego; dalsza część reali zowana jest tylko w wypadku rakiety i/lub ruchu z oporem: if (l_rak) or (l_op) then begin v:=sqrt(sqr(Vl[l])+sqr(Vl[2])); // prędkości zx:=Vl[l]/v; zy:=Vl[2]/v; // wersory prędkości if l_rak then begin am:=sm-c*t; // aktualna masa if am>=fm then begin // dodajemy przyspieszenie silnika rakiety al[l]:=al[l]+f_rak/am*zx; al[2]:=al[2]+f_rak/am*zy end else begin writelnCKONIEC PRACY SILNIKA'); l_rak:=false end end else am:=fm; // masa aktualna := masa końcowa
Dla rakiety obliczona została aktualna masa am (24) i wzory (25) lub pierwsze człony wzorów (39); dalsza część realizowana jest w wypadku włączenia oporu ośrodka (29): if l_op then begin // dodajemy opóźnienie przez hamowanie v:=b/am*sqr(v); al[l]:=al[l]-v*zx; al[2]:=al[2]-v*zy end end end; P_accl:=al //pracowaliśmy na kopii, żeby uniknąć wywołania rekursywnego end; // Paccl
36
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
Dalsze wykorzystane tu elementy Pascala: ■ wszystkie parametry formalne podprogramów m uszą być wyspecyfikowane w nagłówku, ale parametry tego samego typu można zgromadzić w postaci listy; lista otwierana przez var specyfikuje parametry wywoływane przez nazwę (re ferencję); ■ tu trzeba zrobić obszerniejszą uwagę dotyczącą generalnie mechanizmów komu nikacji nadprogram O podprogram, nie tylko w Pascalu, bo problem ten w y stępuje w każdym języku programowania: mechanizmy te można określić jako: „przez wartość” lub „przez referencję/nazwę/adres” (te trzy określenia są w za jem nie równoważne, tu będę używał „przez referencję”): -
parametr formalny podprogramu wywoływany/przekazywany przez refe rencję (jeszcze prościej: który jest referencją) jest w P poprzedzony kw alifi katorem var (w C: &), wskazującym, że odpowiednim parametrem aktual nym musi być zmienna nadprogramu; param etr taki nie posiada alokacji w podprogramie; istotą mechanizmu „przez referencję” jest przekazywanie adresu zmiennej nadprogramu; jeśli w ynik działania podprogramu ma być przekazany do nadprogramu za pośrednictwem param etru formalnego, to musi być on referencją;
-
parametr formalny podprogramu wywoływany/przekazywany „przez war tość” otrzymuje alokację w podprogramie - zostaje wewnętrznie zadekla rowany w jego treści, wartość odpowiadającego mu parametru aktualnego zostaje przed rozpoczęciem realizacji części operacyjnej podprogramu obli czona i wpisana do tej wewnętrznej kopii; po zakończeniu działania podpro gramu parametr taki przestaje istnieć, żadna wartość nie może być przez nie go przekazana do nadprogramu (w F jest trochę inaczej, zob. F-I.); ze względu na tę wewnętrzną alokację param etry strukturalne (tablice, rekordy itp.) nie powinny być przekazywane przez wartość (chociaż w P jest to możliwe);
■ indeksy tablic ujęte są w nawiasy kwadratowe [ ... ], dla tablic wieloindeksowych można je agregować w postaci listy: [il,i2 ,i3 ,...]; ■ podstawienie struktura_l:=struktura_p jest w P możliwe, jeśli obie struktury są tego samego typu; ■ w P nie ma operatora potęgowania, ale jest funkcja podnoszenia do kwadratu: sqr(...), która została tu użyta (nie mylić z sqrt(...) - square root, pierwiastek kwadratowy, tak samo w C i F); w F operatorem potęgowania jest **, w C do p o tęgowania służy funkcja pow(..) (odpower).
P-I. Pascal: program Pr ev test.dpr
37
W nazwach procedur obliczających wektor przyspieszeń opuszczamy cr ’, ponieważ kod źródłowy w żadnym z użytych tu języków nie zależy od tego, czy parametr formalny PAR jest rekordem, obiektem czy klasą.
P-I.3. Integratory Eulera i Verleta Parametry fizyczne potrzebne do obliczeń zostały wczytane, algorytmy realizujące wzo ry na przyspieszenie są zaprogramowane, możemy więc napisać podprogramy realizują ce procesy całkowania (czyli tzw. integratory) Eulera - wzory (5) i (10) i Verleta - wzory (14), (15), (5). Integrator Eulera, na podstawie wartości współrzędnych w chwili t , za pisanych w wektorze R0LD1 i prędkości zapisanych w wektorze V0LD1, oblicza najpierw składowe prędkości w chwili t+h wg (5) i zapisuje je w VNEW1, a następnie nowe położe nia RNEW1 wg (10), z ewentualnym ważeniem udziału starej (V0LD1) i nowej (VNEW1) pręd kości wg wartości param etru BETA. Dla metody Verleta wektor R0LDER1 opisuje położenie dla czasu t-h; najpierw realizowany jest wzór (14) z ewentualnym parametrem tłum ią cym BETA, następnie ze wzoru (15) obliczamy v(t) i zapisujemy w V0LD1, a ze wzoru (5) - „nową” prędkość, którą zapisujemy w VNEW1. Na przyjętym tu poziomie implementacji podprogramy te m uszą być procedurami - podprogramami niefunkcyjnymi (w F - seg mentami typu subroutine; w C - funkcjami o wartościach void), ponieważ w ich wyni ku powstają dwie nowe struktury danych - wektory RNEW1 i VNEW1 (gdyby powstawała tylko jedna nowa struktura - mogłyby być to (w P i F) funkcje o wartościach tablico wych). Musimy jednak znać przyspieszenia: są one zapisane w wektorze ACC1, który ob liczany jest w procedurach nielokalnych P_accl, C_accl, F_accl, odpowiednio: Listing P-ev procedurę P_e_step(var PAR:P_T_PAR; var ACCljROLD1,RNEW1,V0LD1,VNEW1:P_T_A1;H,BETA:T_FL); // krok Eulera: Rnew=r(t+h),Rold=r(t),Vnew=v(t+h),Vold=v(t) var k:integer; begin ACC1:=P_accl(PAR,ROLDljV0LD1); // przyspieszenie określone przez R0LD1, V0LD1 for k:=l to PAR.neq do begin VNEWl[k]:=VOLDl[k]+ACCl[k]*H; // nowa prędkość RNEWl[k]:=R0LD1[k]+(BETA*VOLDl[k]+(l.0-BETA)*VNEWl[k])*H +0.5*ACC1[k]*H*H // nowe położenie jako ważona suma starej i nowej prędkości end; end; // P_e_step
38
1. TROCHĘ PROSTEJ FIZYKI, PROSTE PROGRAMOWANIE
procedurę P_v_step(var PAR:P_T_PAR;var ACCl.ROLDl^RNEWl.ROLDERl, VOLDł,VNEW1:P_T_A1;H,BETA:T_FL); {krok Verleta: Rnew=r(t+h)JRold=r(t)JRolder=r(t-h)JVold=v(t); algorytm Verleta startuje w 2. kroku; najpierw trzeba wykonać krok Eulera} var k:integer; begin ACCl:=P_accl(PAR,ROLDl,VOLDl); // przyspieszenie określone przez R0LD1, V0LD1 for k:=l to PAR.neq do begin RNEWl[k]:=(1.0+BETA)*ROLDl[k]-BETA*ROLDERl[k]+ACCl[k]*H*H; // nowe położenie VOLDl[k]:=0.5*(RNEWl[k]-ROLDERlfk])/H; // stara prędkość VNEWl[k]:=VOLDl[k]+ACCl[k]*H // nowa prędkość end; end; // P_v_step
* struktura PAR jest parametrem formalnym obu procedur wyłącznie dlatego, że jej elementy - wartości parametrów fizycznych - muszą być przekazane do funkcji P_accl: algorytmy Eulera i Verleta od niej nie zależą; musi wystąpić ona na liście referencji, bo jej elementy am, t ulegają zmianie w każdym kroku obliczeń; ■ wszystkie tablice zostały także umieszczone na liście referencji, mimo iż R0LD1, V0LD1 nie ulegają zmianie w procedurze; wywoływanie wszelkich struktur przez nazwę/referencję jest dobrą zasadą w Pascalu; ■ wystąpiła tu po raz pierwszy pętla for ... - najstarsza konstrukcja iteracyjna we wszystkich językach programowania; w P ma ona ogólną postać: for zmienna_kontrolna:=wartosc_poczatkowa to/downto wartosc_koncowa do cialo_pętli; zmienna kontrolna, wartosc_poczatkowa, wartosc_koncowa muszą być tego samego typu porządkowego; użycie to oznacza pojedynczą inkrementację (krok 0 jedną pozycję w prawo w zbiorze wartości, tu: + 1) zmiennej_kontrolnej, uży cie downto - dekrementację (tu byłby to krok: -1); cialo_petli wykonane zosta nie dla kolejnych wartości zmiennej_kontrolnej, ale nie musi od niej zależeć,
1(w P) nie może jej modyfikować; pętla taka jest „sterowana z góry” i ma z góry znaną liczbę kroków, w przeciwieństwie do pętli typu repeat ... until ..., while ... do ..., w których liczba powtórzeń nie jest z góry znana;
P-I. Pascal: program Pr_ev_test.dpr
39
■ w ciągu deklaracyjnym podprogramów procedury te muszą być umieszczone po P_accl (ale niekoniecznie natychmiast) ze względu na hierarchiczny charakter ję zyka.
P-I.4. Wybór warunków początkowych, czyli w co się bawić... Czas zdecydować, co właściwie chcemy policzyć. Możemy: 1. Porzucać sobie czymś (masa nieistotna) zakładając, że opór powietrza nie wystę puje (np. na Księżycu...) - sprawdzić spełnienie wzorów (18-20); 2. Rzucić czymś lekkim lub ciężkim (masa istotna) pionowo w górę, uwzględniając opór powietrza - sprawdzić, jak spełnione są wzory (30-34); 3. Zrzucić coś (też masywnego) pionowo w dół i zobaczyć, czy spełnione są wzory (35,36) i czy opór powietrza istotnie spowoduje, że prędkość spadania ustabilizu je się wg wzoru (38); 4. Wystrzelić pionowo w górę (i w próżni... - np. na Księżycu) jakąś rakietkę i zo baczyć, czy zachowa się tak, jak przewidują to wzory (24-28); 5. Wreszcie możemy rzucić jakieś ciało lub wystrzelić jakąś rakietę pod dowolnym kątem, uwzględniając opór powietrza lub nie - tyle że nie będziemy mieli żad nych analitycznych rozwiązań dla takiego ogólnego przypadku. Dodatkowo do każdej z tych „zabaw” możemy zaprosić integrator Eulera lub Verleta. Zauważmy, że zabawy nr 1 i 5 to ruch w dwóch wymiarach (2D) - zmieniać się będzie położenie i prędkość w poziomie i w pionie; natomiast zabawy nr 2, 3 i 4 to ruchy jed nowymiarowe (ID) - zmienia się tylko położenie i prędkość w pionie. Po wszechstron nym przetestowaniu dokładności naszych metod obliczeniowych w ramach zabaw 1-4 możemy wybrać sobie najbardziej naszym zdaniem wiarygodny sposób postępowania i trzymać się go w najbardziej ogólnej „zabawie” nr 5, do której nasz program w istocie mógłby być ograniczony; no ale wtedy nie wiedzielibyśmy, na ile jest on wiarygodny. Od razu jednak warto zauważyć, że ograniczenie badania stosowalności metod Eule ra czy Verleta do zadania typu 1 nie da nam rzetelnej odpowiedzi co do wiarygodności tych metod, bo w tym przypadku przyspieszenie, zgodnie z (16, 17), jest stałe - nie za leży od położenia ani prędkości, a więc i wynik numeryczny powinien być bardzo bli ski rozwiązaniom analitycznym (18-20). Bardziej globalnym testem poprawności jest w tym wypadku sprawdzenie, na ile dokładnie sprawdza się znane ze szkoły stwierdze nie, że zasięg rzutu pod kątem a i 90-a jest taki sam.
40
I. TR O C H Ę PR O ST EJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
O tym, w co się w konkretnym przebiegu naszego programu zabawimy, zdecydują wa runki początkowe, do których określenia najlepiej jest napisać odpowiednie procedury. Listing P-start procedure P_start_init(var START:P_T_START;var PAR:P_T_PAR; var R01,V01:P_T_A1); var z:char; r:T_FL; begin with START,PAR do begin write('metoda: Eulera(e) czy Verleta(v)?; inaczej - KONIEC ZABAWY: '); readln(z); if ( z o ' e 1) and (zo'v') then halt; l_elr:=z='e'; l_ver:=z='v'; repeat write('parametr mieszania z przedziału 1-0 (1: "czysty" E lub V): '); readln(beta) until (beta=0.0); repeat write('krok całkowania h (>0): '); readln(h) until h>0.0;
Po wyborze metody Eulera lub Verleta i określeniu parametrów numerycznych h, beta, przechodzimy do wyboru położenia startowego x0, y0, czyli rgp rg2, we wzo rach (18,2 7,31,35) oraz startowej prędkości v i kątajej nachylenia a do poziomu, skąd wyznaczyć możemy startowe składowe prędkości: v0x, v0y czyli v01, va2, we wzorach (18, 19, 20, 28, 32, 36); podanie zerowej prędkości startowej powoduje ustawienie jej na 0.001 m/s - jest to konieczne dla uniknięcia ewentualnego dzie lenia przez zero: writeln('P0DA3 WARUNKI POCZĄTKOWE '); write('położenie x, y [m]: '); readln(x0,y0); rol[l]:=x0; rol[2]:=y0; write('prędkość v [m/s], kat [deg]: '); readln(v0x,v0y); if v0x=0.0 then v0x:=0.001; l_up:=v0y=90.0; // true: pionowo w górę l_dn:=v0y=-90.0; // true: pionowo w doi v0y:=v0y*par.deg; // deg -> rad vol[l]:=v0x*cos(v0y); vol[2]:=v0x*sin(v0y); v0x:=vol[l]; v0y:=vol[2]; writeln('składowe prędkości [m/s]; vx : ',vol[l]:15:6, ' vy:',vol[2]:15:6);
P-I. Pascal: program P r e v te st.d p r
41
Na podstawie tych danych nastąpi teraz wybór jednego z pięciu omówionych wy żej zadań: isel będzie selektorem użytym dalej w wielu miejscach w instruk cjach przełącznika case ... o f do wyboru fragmentów programu realizujących odpowiednie algorytmy i drukujących odpowiednie komentarze; wartości selekto ra odpowiadają dokładnie numerom poszczególnych przypadków: isel:=5; if (not(l_rak)) and (not(l_op)) then isel:=l; // rzut swobodny if (not l_rak) and l_op then // rzut z oporem, ale nie rakieta if l_up then isel:=2 // rzut pionowo w górę, z oporem else if l_dn then isel:=3; // spadanie pionowo w doi, z oporem if (not l_op) and l_rak and l_up then isel:=4; //rakieta w gore,bez oporu r:=b/sm; case isel of 1: writeln('zasięg poziomy, wysokosc [m]: ', 2*v0x*v0y/g:15:6,v0y*v0y/(2*g):15:6); 2: writeln('rzut pionowo w gore z oporem, zasięg [m], czas [s]; ', 0.5*ln(abs(1.0+r*sqr(v0y)/g))/r:10:2, arctan(v0y*sqrt(r/g))/sqrt(r*g):10:3); 3: writeln('spadanie pionowe z oporem, prędkość graniczna [m/s]: ', -sqrt(g/r):15:6); 4: writeln('rakieta pionowo w gore, bez oporu'); 5: writeln('rzut/rakieta, z oporem lub bez') end; // case if l_ver then writeln(100*beta:5:l,'% Verlet + ',100*(l-beta):5:1,'% tłumienia'); if l_elr then writeln('Euler: ',beta*100:5:l,'% void + ',(l-beta)*100:5:l,'% vnew');
N a koniec następuje pytanie, co który krok iteracyjny (nrec) zapisać wyniki na zewnętrzny plik znakowy w celu wykorzystania ich np. w zewnętrznym progra mie graficznym do uzyskania wykresów (choćby - eksport do Excela): podanie 0 oznacza bez zapisu na plik, 1 - zapis każdego kroku, ale dla celów graficznych wystarczy zapisywać np. co 10 krok. Jeśli nrec>0, następuje związanie (AssignFile; m ożna też pisać assignfile) pliku logicznego nfl (typu TextFile; można też pisać textf ile lub po prostu text) z jednym z czterech możliwych plików fizycznych o nazwach zaczynających się od Pr_. . . : e w nazwie ozna cza metodę Eulera, v - metodę Verleta, ld - przypadki 1-wymiarowe (2, 3,4), 2d - przypadki 2-wymiarowe (1 ,5 ); na końcu następuje otwarcie pliku do zapisu (rewrite(nfl)).
42
1. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
repeat write('zapis na plik co nrec krokow (nrec>=0, 0-bez zapisu): '); readln(nrec) until nrec>=0; if nrec>0 then begin case isel of 2,3,4: if l_elr then AssignFile(nfl,'Pr_eld.dat') else AssignFile(nfl,'Pr_vld.dat'); else if l_elr then AssignFile(nfl,'Pr_e2d.dat') else AssignFile(nfl,'Pr_v2d.dat') end; // case rewrite(nfl); end end end; // P_start_init
jak zwykle korzystamy z Pascalowej „sztuczki” bardzo usprawniającej zapis: grupujemy wszystkie odwołania do elementów struktur PAR i START we wspólnej instrukcji grupującej: with PAR, START do ; jest to jednak możliwe tylko pod warunkiem, że wszystkie nazwy elementów tych struktur są różne, tzn. nazwa elementu może być jednoznacznie przyporządkowana do nazwy zmiennej struk turalnej: ale o to zadbaliśmy definiując te struktury; instrukcja halt oznacza w P natychmiastowe zakończenie programu - rozw iąza nie stosowane rutynowo w sytuacjach awaryjnych; czytanie szeregu danych zabezpieczone jest pętlami repeat ... u n til ... w celu wykluczenia bezsensownych wartości; instrukcja przełącznika (selekcji wielowartościowej) m a w P postać: case selektor of lista_wartosci_selektora_l: sekwencja_l; lista_wartosci_selektora_2: sekwencja_2;
43
P-I. Pascal: program Pr ev test.dpr
poszczególne sekwencje ... przełącznika w P są rozłączne: zakończenie wyselek cjonowanej sekwencji ... kończy całą instrukcję.
P-I. 5. Analiza błędów numerycznych Dla przypadków 1-4 dysponujemy rozwiązaniami analitycznymi, możemy więc ocenić, jak dokładne są algorytmy Eulera i Verleta. W każdym z tych przypadków wzory ana lityczne są inne, dlatego logiczne będzie sformułowanie czterech odrębnych procedur badania tej dokładności, o nazwach P_errX_anal, X=l,2,3,4 (odpowiednio do przy padku); w innych wersjach językowych procedury te nazywać się będą odpowiednio F_...
Lokalną - tzn. dla danej chwili t - m iarą dokładności są oczywiście błędy bezwzględ ne, zdefiniowane tu jako różnice wartości numerycznych (m m ) i analitycznych (anal): (40) Ar/t) = r ”um(t) - r “nai(t), j = l . .. k ; r ana,(t) dane jest jednym ze wzorów (18,31,35,27), odpowiednio, dla przypadków 1-4 (41) A v/t) = v ”um(t) - v “nal(t), j = l...k; v anal(t) dane jest jednym ze wzorów (18,32,36,28), odpowiednio, dla przypadków 1-4 Błędy te przekazywane b ędą przez pola struktury EVR typu P_T_ERR_ANAL: ery, evy dla położenia i prędkości w pionie, dla wszystkich przypadków - bo w każdym z nich zachodzi ruch w pionie, oraz odpowiednio erx,evx tylko dla przypadku 1 - bo tylko w tym przypadku ruch zachodzi także poziomo. Jako wartości numeryczne przyjmuje my w tych obliczeniach wartości „nowe”, zapisane w tablicach RNEW1, VNEW1; to wyni ka ze struktury programu zarządzającego (zob. niżej), bo analiza wykonywana jest po kroku całkowania. Rozwiązania analityczne m amy prawo uważać za „dokładne”, zatem błędy względne to stosunki błędów bezwzględnych do odpowiednich rozwiązań analitycznych wyrażone w procentach wartości analitycznej (dlatego mnożenie przez 100.0):
[else sekwencja_else] end; selektor musi być typu porządkowego, lista_wartosci_... to ciąg wartości se
lektora rozdzielonych przecinkam i (w szczególności - pojedyncza wartość) albo przedział określony okrojeniem: wartosc_poczatkowa..wartosc_koncowa (.. jest w P operatorem przedziału/okrojenia), element z e ls e nie musi występować;
(42) Are,r/t) = Ar/t) / r f al(t) *100.0,
j = l...k
Arelv/t) = A v / t) / v “na,(t) *100.0, Błędy te przekazywane będą przez pola struktury EVR typu P_T_ERR_ANAL: y_anal, vy_anal dla położenia i prędkości w pionie, dla wszystkich przypadków - bo w każ
44
I. TR O C H Ę PROSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
dym z nich zachodzi ruch w pionie, oraz odpowiednio x_anal,vx_anal tylko dla przy padku 1 - bo tylko w tym przypadku ruch zachodzi także poziomo. Dodatkowo, jeśli błąd względny położenia lub prędkości przekroczy ±10%, param etry formalne l_er lub l_ev (odpowiednio) przyjm ą wartości true. W arto jednak zauważyć że tam, gdzie roz wiązania analityczne przyjm ują (ze swej natury) wartości bliskie zera, formuły (42) nie są użyteczną m iarą dokładności. Ponownie korzystamy w wersji P z instrukcji grupującej, całe ciało każdej z procedur zamykamy wewnątrz instrukcji with PAR, START, EVR do ... Listing P-anal procedure P_errl_anal(var PAR:P_T_PAR;var START:P_T_START; var ERV:P_T_ERR_ANAL;va r RNl,VNl:P_T_Al;var L_ER,L_EV:boolean);
P-I. Pascal: program P r e v te st.d p r
45
y_anal:=y0+ln(abs(v0y*sqrt(ro/g)*sn+cs))/ro; vy_anal:=a*(v0y*cs-a*sn)/(v0y*sn+a*cs); ery:=RNl[2]-y_anal; // błędy położenia if y_anal0.0 then y_anal:=ery/y_anal*100 else y_anal:=0.0; evy:=VNl[2]-vy_anal; // błędy prędkości if vy_anal0.0 then vy_anal:=evy/vy_anal*100 else vy_anal:=0.0; L_ER:=abs(y_anal)>10.0; L_EV:=abs(vy_anal)>10.0; end end; // P_err2_anal procedure P_err3_anal(var PAR:P_T_PAR;var START:P_T_START; var ERV:P_T_ERR_ANAL;var RNl,VNl:P_T_Al;var L_ER,L_EV:boolean); var a,sn,cs:T_FL;
Wykorzystujemy tu wzory (30,35, 36): Wykorzystujemy tu wzory (18): begin with PAR,START,ERV do begin x_anal:=x0+v0x*t; y_anal:=y0+v0y*t-0.5*g*t*t; vx_anal:=v0x; vy_anal:=v0y-g*t; erx:=RNl[l]-x_anal; ery:=RNl[2]-y_anal; // błędy absolutne if x_anal0.0 then x_anal:=erx/x_anal*100 else x_anal:=0.0; //błędy ... if y_anal0.0 then y_anal:=ery/y_anal*100 else y_anal:=0.0; //..procent L_ER:=(abs(x_anal)>10.0) or (abs(y_anal)>10.0); evy:=VNl[2]-vy_anal; // błąd absolutny if vy_anal0.0 then vy_anal:=evy/vy_anal*100 else vy_anal:=0.0; L_EV:=abs(vy_anal)>10.0; end end; // P_errl_anal procedure P_err2_anal(var PAR:P_T_PAR;var START:P_T_START; var ERV:P_T_ERR_ANAL;var RNl,VNl:P_T_Al;var L_ER,L_EV:boolean); var a,cs,sn:T_FL;
begin with PAR,START,ERV do begin a:=t*sqrt(ro*g); ery:=exp(-a); evy:=exp(a); sn:=0.5*(evy-ery); cs:=0.5*(evy+ery); //sinus,cosinus hiperboliczny y_anal:=y0-ln(abs(v0y*sqrt(ro/g)*sn-cs))/ro; a:=sqrt(g/ro); vy_anal:=-a*(v0y*cs-a*sn)/(v0y*sn-a*cs); ery:=RN1[2]-y_anal; // błędy położenia if y_anal0.0 then y_anal:=ery/y_anal*100 else y_anal:=0.0; evy:=VNl[2]-vy_anal; // błędy prędkości if vy_anal0.0 then vy_anal:=evy/vy_anal*100 else vy_anal:=0.0; L_ER:=abs(y_anal)>10.0; L_EV:=abs(vy_anal)>10.0; end end; // P_err3_anal procedure P_err4_anal(var PAR:P_T_PAR;var START:P_T_START; var ERV:P_T_ERR_ANAL;var RNl,VNl:P_T_Al;var L_ER,L_EV:boolean);
Wykorzystujemy tu wzory (26-28): Wykorzystujemy tu wzory (30-32): begin with PAR,START,ERV do begin a:=t*sqrt(ro*g); cs:=cos(a); sn:=sin(a); a:=sqrt(g/ro);
begin with PAR,START,ERV do begin y_anal:=-0.5*g*t*t+sm*w/c*((1.0-c*t/sm)*(ln(abs(1.0-c*t/sm))-1.0)+1.0); vy_anal:=-g*t-w*ln(abs(1.0-c*t/sm)); ery:=RNl[2]-y_anal; // błędy położenia
46
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE P R OGR A M OW A N IE
if y_anal0.0 then y_anal:=ery/y_anal*100 else y_anal:=0.0; evy:=VNl[2]-vy_anal; // błędy prędkości if vy_anal0.0 then vy_anal:=evy/vy_anal*100 else vy_anal:=0.0; L_ER:=abs(y_anal)>10.0; L_EV:=abs(vy_anal)>10.0; end end; // P_err4_anal
Na koniec pomocnicze procedury P_headers (i odpowiednio C_headers, F_headers), których celem jest wypisanie na ekran nagłówków tabelek, raportujących przebieg pro cesów całkowania; kolumny dx, dy podają błędy bezwzględne położenia w poziomie i pionie (40), dy% - błędy względne położenia w pionie (42), dvx, dvy, dvy% - analo gicznie dla prędkości (41), v to prędkość całkowita, v/M - prędkość w skali Macha. Listing P-headers procedurę P_headers(ISEL:integer); begin case ISEL of // nagłówki l:writeln('krok 11 ......-x-1 dx--1------- y-1 dy- -1 — vy- |-dvy )--v------'); 2,3:writeln('--krok— t ........ y ........ dy......... dy%...... vy-— dvy........dvy%— v/M--'); -dy........ dy%.....-vy4:writeln('-krok--t masa------- y -dvy.........dvy%--vy/M'); y -------- vx— 5:writeln('— krok— t ........ masa.....--x..... --vy........ v v/M-'); end; // case end; // P headers
P-I. Pascal: program Pr_ev_test.dpr
■ wywoływanie w pętli odpowiednich procedur P/C/F_e_step lub P/C/F_v_step i wypisywania na ekran protokołu z procesu całkowania; ■ podejmowanie decyzji o kontynuacji lub zakończeniu procesu całkowania. W wersji P, ze względu na hierarchiczny charakter języka, program zarządzający nie jest odrębnym segmentem, jest to po prostu część operacyjna programu Pr_ev_test. Listing Pr-driver // Pr_ev_driver var lin,k:integer; v,vm,tp:T_FL; l_fl:boolean; z:char; par:P_T_PAR; start:P_T_START; erv:P_T_ERR_ANAL; al,roi,rnl,vol,vnl,rool:P_T_A1; begin with par,start,erv do begin vm:=340.0; // prędkość Macha P_par_init(par); // parametry równań zainicjowane P_start_init(start,par,roi,vol);//parametry numeryczne,start ustalone P_headers(isel); // nagłówki protokołu iteracji t:=0.0; // inicjacja czasu if l_ver then // przed startem metody Verleta: begin
Przed startem metody Yerleta trzeba wykonać 1 krok metody Eulera:
P-L6 . Program zarządzający Mamy już wszystkie potrzebne procedury, czas więc napisać dla nich program zarzą dzający, „główny”, czyli driver. Zadaniem takiego programu będzie: ■ określenie parametrów fizycznych badanego układu —aktywacja odpowiedniej procedury P/C/F_par_init; ■ określenie metody całkowania i jej parametrów: wybór integratora Eulera lub Verleta, parametrów h, beta; wybór warunków początkowych - aktywacja od powiedniej procedury P/C/F_start_init;
47
t:=t+h; // jeden krok Eulera P_e_step(par,al,rol,rnl,vol,vnl,h,1.0); rool:=rol; // rool = r(t-h) rol:=rnl; // roi = r(t) vol:=vnł; // vol = v(t+h) end; k:=0; lin:=l; tp:=0.0; z:='t'; l_fl:=true; l_er:=false; l_ev:=false; // flagi, czujniki repeat // główna pętla integracji - symulacji
48
I. TR O C H Ę PROSTEJ F IZ Y K I, PROSTE P R OGR A M OW A N IE
t:=t+h; // krok czasowy inc(k); // numer kroku if l_ver then P_v_step(par,al,roi,rnl,rool,vol,vnl,h,beta); if l_elr then P_e_step(par,al,rol,rnl,vol,vnl,h,beta);
Krok Eulera lub Verleta wykonany: rnl, vnl zawierają teraz numeryczne osza cowania położenia i prędkości dla czasu t+h, v jest prędkością całkowitą. v:=sqrt(sqr(vnl[l])+sqr(vnl[2]>); // aktualna prędkość ro:=b/am; case isel of 1: begin // rzut ukośny, bez oporu i napędu P_errl_anal(par,start,erv,rnl,vnl,l_er,l_ev); writeln(k:4,t+tp:7:2,rnl[l]:9:1,' ',erx:10,rnl[2] :9:1, ’ ',ery:10,vnl[2]:9:1,' ',evy:10,v:8:l); end; 2: begin // rzut pionowo w gore, z oporem P_err2_anal(par,start,erv,rnl,vnl,l_er,l_ev); writeln(k:5,t:7:2,rnl[2]:9:1,' ',ery:12,y_anal:8:2, vnl[2]:9:l,' ',evy:12,vy_anal:8:2,' ’,v/vm:6:3); if vnl[2]=40 then // po 40 liniach na ekranie ... begin write('liczyc dalej? (t/n): '); readln(z); if z='t' then begin lin:=0; P_headers(isel) end end; until zo ' t ' ; // koniec pętli iteracyjnej if nrec>0 then close(nfl) // zamknięcie pliku wynikowego end end.
Pr-prolog Pr-init Pr-accel P-ev P-start P-anal P-headers Pr-driver otrzymujemy całość programu jako plik Pr_ev_test.dpr (w DP) lub *.lpr (w LP). Kom pilacja i konsolidacja programu w Delphi (DP) następuje poprzez wybór odpowiedniej opcji w menu; podobnie jest z kompilatorem FPC w systemie Lazarus (LP); utworzo ny zostanie automatycznie plik Pr_ev_test.exe. Ale jak skompilować program w FPC w oparciu o kompilatory GNU w UNIXie? Komenda aktywująca te kompilatory ma standaryzowaną postać: compiler source.ext [ source ...][ object_lib][-o exec.ext]
gdzie: ■
compiler - nazwa kompilatora, w tym w ypadku fpc;
■
sou rce.ext - nazwa pliku zawierającego kod źródłowy, wraz z rozszerzeniem;
elementy ujęte w nawiasy [...] są opcjonalne: ■
[ object_lib] - biblioteka/archiwum kodów pośrednich {object code), którą chcemy włączyć do procesu konsolidacji;
■
-o oznacza: kompiluj i następnie konsoliduj;
[ exec.ext] - wynikowy kod wykonawczy, otrzymuje automatycznie UNIXowy atrybut x {executable)', jeśli pom inięta - nazw ą domyślną będzie a.out (nie bezpieczne, bo już istniejący plik a.out z poprzedniej kompilacji może zostać nadpisany bez ostrzeżenia!). A więc dla kompilacji naszego programu w FPC użyjemy komendy np.: fpc Pr_ev_test.pas -o Pr_ev_test.xp
52
I. TR O C H Ę PROSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
Dobrym zwyczajem (ale nie obowiązkiem!) jest nazywanie pliku pośredniego lub w y konawczego nazw ą taką sam ą jak plik źródłowy, ale oczywiście z innym rozszerzeniem. W mojej całkowicie prywatnej konwencji użytej wyżej rozszerzenie xp oznacza: pro gram wykonawczy (x) kompilowany w fpc.
C-I. C++: program Cr_ev_test.cc Dla programów w wersji C++ używać będziemy kompilatora GNU C++ (lub g++) i jego standardowych bibliotek na platformie systemowej U N IX/LIN UX lub na nakładce Cygwin dla Windows; warto dodać, że system Cygwin i kompilatory GNU są dostępne bezpłatnie. Kolejność omawiania poszczególnych elementów programu w C ++jest identyczna z ko lejnością dla wersji P w rozdziale P-I. Zamieszczone tam obszerne wprowadzenia do kolejnych listingów nie będą tu powtarzane, dlatego bardzo wskazane jest zapoznać się z tym i wprowadzeniami nawet gdy nie interesuje nas wersja P. Preambuła programu zapisanego w pliku Cr_ev_test.cc będzie następująca: Listing Cr-prolog #include #include ttinclude const int max=10 using namespace std; typedef double T_FL; typedef T_FL C_T_Al[max+l]; // element [0] not used! typedef struct { int neq; // number of equations bool l_rak,l_op; // true: rocket, drag on T_FL g,sm,fm,w,c,b,f_rak,am,t,deg; g [m/A2]: gravitational acceleration; // sm,fm [kg]: starting mass, final mass; // w [m/s]: rocket gases speed; // c [kg/s]: fuel burning speed; // drag: -b*vA2, b [kg/m]: effective drag coefficient; // f_rak=w*c [N]: rocket engine force; // am=sm-c*t [kg]: actual mass; // t [s]: time // } C_T_PAR; typedef struct { int isel,nrec; // case selector, file recording parameter T_FL beta,h,x0,y0,v0x,v0y; // numerical & starting data bool l_elr,l_ver,l_up,l_dn; // flags FILE *nfl; // pointer to logical file } C_T_START;
54
I. T R O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
typedef struct { T_FL ro,x_anal,y_anal,vx_anal,vy_anal,erx,ery,evx,evy; > C_T_ERR_ANAL; // analytical values & errors
Podobnie jak w wersji P, po każdym listingu nastąpi krótkie omówienie użytych struk tur językowych - nasz „elementarz” C++: ■ dyrektywy ttinclude włączająbiblioteki obsługujące strumienie wejścia/wyjścia (iostream, stdio.h) i funkcje m atematyczne (math.h); włączony też został do stęp do standardowej przestrzeni nazw (namespace std); ■ zdefiniowana została stała max o zasięgu globalnym (tzn. obowiązująca we wszyst kich segmentach w kompilowanym pliku) i te same dwa typy (T_FL, C_T_A1) co w wersji P. Pamiętajmy jednak, że w C indeksowanie tablicy zawsze biegnie od 0, a więc elementy tablic typu C_T_A1 m ają indeksy [0 1 ... ]. W tym pro gramie, dla zachowania możliwie dużego stopnia kompatybilności z wersjami P i F, elementy [0] nie b ędą wykorzystywane, stąd w definicji typu C_T_Al[max+l]; ■ struktura rekordowa C_T_PAR ma tutaj typ struct; zgromadzone są w niej wszystkie potrzebne param etry fizyczne zagadnienia, ale także włączone tu zo stały: liczba równań neq, zmienne logiczne (typ bool) l_rak - wskazująca, czy mamy do czynienia z rakietą (wówczas masa am będzie zmienna z czasem, wg (24)), i l_op - wskazująca, czy włączony będzie opór powietrza; ze względu na możliwą zmianę masy do struktury tej został także włączony czas t. Typy struk turalne C_T_START i C_T_ERR_ANAL m ają konstrukcje i zastosowania analogiczne jak w wersji P;
55
C-I. C++: program Cr_ev_test.cc
PAR.neq=2; PAR.deg=M_PI/180.0; do { cout « "przyspieszenie grawitacyjne [m/sA2] (>0; 0 - 9.81): cin >> PAR.g; } while (PAR.g0; 0 - 9.81): 501 format(a,$) 601 read(*,*,err=601) PAR%g if (PAR%g',atan(PAR%g/sqrt(p*p-ld0))/PAR%deg endif if (PAR%f_rak/PAR%sm>PAR%g) exit enddo endif 604 print 501,'silą oporu typu -b*vA2; podaj b ([kg/m] (>=0.0): read(*,*,err=604) PAR%b if (PAR%b0d0) then PAR%l_op=.true. write(*,'(a,fl0.6,a)') 'opor: -',PAR%b,'* vA2' endif end subroutine F_par_init
Dalsze elementy F9x, które tu zostały użyte: ■
subroutine nazwa_sub(lista_parametrow_formalnych) otwiera procedurę -
podprogram (segment) niefunkcyjny (w żargonie F - subrutynę); lista_... może być pusta, wtedy i nawiasy są niepotrzebne; aktywacja procedury następuje w in strukcji procedury: c a li nazwa_sub(lista_parametrów_aktualnych); ■ wszystkie parametry formalne podprogramów m uszą być wyspecyfikowane w części deklaracyjnej podprogramu (nie w nagłówku - jak w P i C); ■ spacje (poza napisami 't e k s t r) i w cięcia są przez kompilator ignorowane; ■ (9) instrukcję kończy średnik lub przejście do nowej linii; ten drugi sposób jest zalecany szczególnie w instrukcjach strukturalnych, a we wcześniejszych wer sjach Fortranu był to sposób jedyny;
F-I. F9x: program Fr_ev_test.f90
79
(7) operatory relacji m ają postać analogiczną do znanej z P i C (w nawiasach operatory obowiązujące w F77A i wszystkich wcześniejszych wersjach F, także akceptowane w F9x): < (.lt.); = (.ge.)j > (.gt.) ** jest operatorem potęgowania (wykładnik może być zmiennopozycyjny, ale wówczas podstawa musi być dodatnia); selekcja 2-wartościowa ma postać: if (warunek_logiczny) then sekwencjajtrue [else sekwencja_false] endif (część z else jest opcjonalna, ale końcowe endif - obligatoryjne); przejścia do
nowych linii (obligatoryjne w F77) m ogą być zastąpione średnikami - nie jest to zalecane, bo zmniejsza czytelność kodu; warunek_logiczny zawsze występuje w nawiasach; dopuszczalna jest także wersja uproszczona, istniejąca we wcześniejszych wer sjach Fortranu: if (warunek_logiczny) instrukcja; operator koniunkcji (iloczynu logicznego) ma postać .and., alternatywy (sumy logicznej) .or.; (7) pętla sterowana „z góry” ma postać: do while (warunek_logiczny) treść_pętli
enddo (przejścia do nowych linii można zastąpić średnikami); treść pętli będzie reali zowana, jeśli warunek_logiczny ma wartość .true. - tak jak w P; wartości i li terowe operatory logiczne w F zawsze ograniczane są kropkami; (7) pętlę taką można zrealizować także w postaci: do if (warunek_logiczny) exit treść_pętli enddo
(przejścia do nowych linii można zastąpić średnikami); jeśli warunek_logiczny ma wartość .true. - treść tak zbudowanej pętli nie będzie realizowana (odwrot nie do P);
80
I. TR O C H Ę PR O ST EJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
■ (7) pętle sterow ane „z dołu”, które, tak jak w innych językach, wykonywane są przynajmniej 1 raz, realizuje się jako:
F-I. F9x: program Fr_ev_test.f90
81
objaśnione w m iarę pojawiania się, warto zauważyć analogie kodów formato wych w C i F;
do treść_pętli if (warunek_logiczny) exit enddo
(przejścia do nowych linii można zastąpić średnikami); jeśli warunek_logiczny m a wartość .true., nastąpi wyjście z pętli (jak w P); ■ (9) selektorem elementu struktury rekordowej jest: zmienna_rekordowa%pole (tu np. PAR%g); jednak nie m ożna tu (jak w P) opuścić wspólnej części „adresu” pola, wszystkie odwołania do elementów struktury m uszą być jawnie kw alifi kowane - jak w C; ■ każda struktura danych jako param etr formalny (tu: PAR) jest automatycznie w y woływana przez referencję. ■ zgodnie z przyjętą tu konwencją typograficzną param etr formalny PAR pisany jest wielkimi literami, chociaż dla kompilatorów Fortranu nie m a to znaczenia; ■ (9) procedura F_par_init jest w ew nętrznym elementem m odułu Fr_types, dla tego kończąca j ą dyrektywa jest postaci: end subroutine F_par_init;
read formatowanie,lista_wejścia: pobiera wartości podawane na klawiatu rę, interpretuje je w sposób określony przez formatowanie; podawane wartości trzeba rozdzielać spacjami, całość czytania zakończyć przejściem do nowej linii (ENTER); read(kanal,formatowanie,err=etykieta) lista_wejścia - pobiera wartości podawane do kanału (* to kanał domyślny - klawiatura), interpretuje je w spo sób określony przez formatowanie: * to formatowanie domyślne, sterowane ty pem elementu na liście; podawane wartości trzeba rozdzielać spacjami, całość czytania zakończyć przejściem do nowej linii (ENTER); jeśli podane zostanie co kolwiek niezgodnego ze składnią czytanego elementu, sterowanie zostanie prze kazane do etykiety określonej przez err (jest to bardzo typowa w Fortranie pę tla zabezpieczająca poprawność form alną wczytywanych danych).
F-I.2. Realizacja wzorów na przyspieszenie W F9x, podobnie jak w Pascalu, tablica (i każda inna struktura) może być bezpośrednio wartością funkcji; dlatego funkcja F_accl jest tablicą o wartościach typu real*8 (tzn. podwójnej precyzji - jak typ T_FL w P i C).
■ komentarze w wersjach F, podobnie jak w C, są w języku angielskim. Listing Fr-accel Użyte tu procedury komunikacji z konsolą (we/wy): ■ print formatowanie, lista_wy jścia - wypisuje wartości elementów listy na ekran, w sposób określony przez formatowanie; ■ formatowanie może mieć jedną z trzech postaci: - * - domyślne, sterowane danymi; - e ty k - nr etykiety dyrektywy format; -
f(lancuch_formatujący)J;
■ write(kanal,formatowanie)lista_wyjścia - przesyła wartości elementów z listy do wskazanego kanału: * to kanał domyślny - ekran; każda operacja we/wy zasadniczo odnosi się do nowego „rekordu”, w tym w ypadku nowej linii; formatowanie oznaczone jako r(a,$)J oznacza: wypisz tekst (kod formatowy a) i zatrzymaj kursor na końcu linii (kod formatowy $); inne kody formatowe będą
function F_accl(PAR,Rl,VI) implicit none ! PAR - structure containing parametrs ! Rl - position vector [m] ! VI - velocity vector [m/s] ! F_accl contains accelaration vector [m/sA2] type(F_T_PAR) PAR real*8 F_accl(max),Rl(*),Vl(*),al(max),v,zx,zy al(l)=0.0 al(2)=-PAR%g PAR%am=PAR%sm
! ax = 0 ! ay = -g
Tu kończy się realizacja wzom (17) - dla rzutu swobodnego; dalsza część realizo wana jest tylko w wypadku rakiety i/lub mchu z oporem:
82
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
if (PAR%l_rak.o r .PAR%l_op) then v=sqrt(Vl(l)**2+Vl(2)**2); ! velocity zx=Vl(l)/v; zy=Vl(2)/v; ! velocity versors if (PAR%l_rak) then PAR%am=PAR%sm-PAR%c*PAR%t; ! actual mass if (PAR%am>=PAR%fm) then ! add rocket accelaration: al(1)=al(1)+PAR%f_rak/PAR%am*zxj al(2)=al(2)+PAR%f_rak/PAR%am*zy else print *, 'KONIEC PRACY SILNIKA' PAR%l_rak=.false, endif else PAR%am=PAR%fm ! actual mass = final mass endif
Dla rakiety obliczona została aktualna masa am (24) i wzory (25) lub pierwsze człony wzorów (39); dalsza część realizowana jest w wypadku włączenia oporu ośrodka (29): if (PAR%l_op) then ! subtract deceleration due to drag v=v*v*PAR%b/PAR%am al(l)=al(l)-v*zx; al(2)=al(2)-v*zy endif endif F_accl=al ! al was a working copy end function F_accl
Dalsze wykorzystane tu elementy F9x: ■
function nazwa_fun(lista_parametrow_formalnych) otwiera funkcję - pod program (segment) funkcyjny; nazwa_f un (nazewnik funkcyjny) otrzymuje war tość (musi zostać podstawiona w treści funkcji); lista_... może być pusta, w te dy m usząjednak wystąpić puste nawiasy; aktywacja funkcji następuje w ramach obliczania wartości jakiegoś wyrażenia, np. ... 5*nazwa_fun(lista_parametrow_aktualnych) + ...; w F9x w artością funkcji
może być struktura danych (podobnie jak w P); ■ typ wartości funkcji może być określony w części deklaracyjnej, tak jak typy pa rametrów formalnych (nie w nagłówku - jak w P i C), albo jako typ function nazwa_fun(lista_parametrow ... );
F-I. F9x: program Fr_ev_test.f90
83
wszystkie param etry formalne typu strukturalnego są automatycznie wywoły wane przez referencje; deklaracja tablicy statycznej (tzn. takiej, której liczba elementów zostaje ustalo na przed rozpoczęciem wykonywania programu) - homogenicznej, indeksowanej struktury elementów typu typ ma postać: typ nazwa_tab([wp_l:]wk_l,[wp_2:]wk_2, ... )
lub typ nazwajtab dimension nazwa_tab([wp_l:]wk_l,[wp_2:]wk_2, ... )
pary: wp_j:wk_j określają wartość początkową (wp) i końcową (wk) j-tego in deksu, liczba elementów j-tego zakresu wynosi więc wk-wp+1; wartości począt kowe nie m uszą być podawane, wartością domyślną jest 1; zakresy indeksów tablicy, która jest parametrem formalnym podprogramu, m ogą być także para metrami formalnymi tego podprogramu, w tym zakres ostatniego indeksu może być określony jako *, co oznacza indeksowanie od 1 do tylu, ile będzie określone dla odpowiedniego param etru aktualnego; indeksy w wywołaniu tablicy (selektor tablicowy) ujęte są w nawiasy okrągłe ( ... ), dla tablic wieloindeksowych można je agregować w postaci listy: (11,12,13,...); linearyzacja (alokacja w pamięci) tablicy wieloindeksowej jest w Fortranie „od zawsze” antyleksykalna (anty-alfabetyczna): w szczególno ści tablica 2-indeksowa linearyzowana jest kolumnami, w przeciwieństwie do wszystkich innych języków, gdzie mamy linearyzację wierszami; (9) podstawienie struktura_l=struktura_p jest możliwe, jeśli obie struktury są tego samego typu; (9) funkcja F_accl jest wewnętrznym elementem modułu Fr_types, dlatego kończąca j ą dyrektywa jest postaci: end function F_accl.
84
I. TR O C H Ę PROSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
F-I. F9x: program Fr_ev_test.f90
85
F-I.3. Wybór warunków początkowych Przystępujemy do określenia wartości początkowych dla położeń i prędkości: Listing F-start subroutine F_start_init(START,PAR,ROI,V01) implicit none type(F_T_PAR) PAR type(F_T_START) START character z real*8 R01(*),V01(*),r 601 print 501, & 'metoda: Eulera (e) czy Verleta (v)?; inaczej - KONIEC ZABAWY: 501 format(a,$) read(*,*,err=601) z if (z/='e'.and.z/='v') stop 'KONIEC ZABAWY ...' START%l_elr=z=='e '; START%l_ver=z=='v '; 602 print 501, & 'parametr mieszania z przedziału 1 - 0 (1: "czysty" E lub V): read(*,*,err=602) START%beta if (START%betald0) goto 602 603 print 501,'krok całkowania h (>0): ' read(*,*,err=603) START%h if (START%h=0, 0-bez zapisu): read (*,*., err=609) START%nrec if (START%nrec0) then START%nfl=10 select case (START%isel) case (2:4) if (START%l_ver) then open(unit=START%nfl,file='Fr_vld.dat',err=999, & status='unknown',form='formatted') else open(unit=START%nfl,file='Fr_eld.da t ',err=999, & status='unknown',form='formatted') endif case default if (START%l_ver) then open(unit=START%nf1,file='Fr_v2d.d at',err=999, & status='unknown', form='formatted') else open(unit=START%nfl,file=‘Fr_e2d.dat',err=999, & status='unknown',form='formatted') endif end select endif return 999 stop 'BLAD OTWARCIA PLIKU' end subroutine F_start_init
F-I. F9x: program Fr_ev_test.f90
87
■ (9) odwołania do wszystkich elementów struktur PAR i START m uszą być kwalifi kowane; *
instrukcja stop ['komunikat1] oznacza w F natychmiastowe zakończenie pro gramu i opcjonalne wypisanie komunikatu na konsoli - rozwiązanie stosowane rutynowo w sytuacjach awaryjnych;
■ czytanie szeregu danych zabezpieczone jest jaw nym skokiem goto ... z powro tem do instrukcji czytania, w celu wymuszenia sensownych i formalnie popraw nych wartości; ■ wystąpiła tu wielowartościowa („kaskadowa”) instrukcja if postaci: if (warunek_l) then sekwencja_l else if (warunek_2) then sekwencja_2 else if ... [else sekwencja_else] endif
(przejścia do nowej linii, jak zawsze, można zastąpić średnikami); końcowy ele ment z else może nie występować; ■ (7) instrukcja przełącznika (selekcji wielowartościowej) ma w F postać: select case (selektor) case (zakres_l) sekwencja_l case (zakres_2) sekwencja_2 [case default sekwencja_default] end select
(przejścia do nowej linii można zastąpić średnikami); selektor musi być typu porządkowego, każdy zakres może być jednej z 4 postaci: -
wartość
-
wartość_p: tzn.: zaczynając od wartości_p
:wartość_k
tzn.: do w arto ści_ k włącznie tzn.: od wartości_p do wartości_k
wartość_p:wartość_k
I. T R O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
88
element z default nie musi występować; poszczególne sekwencje ... przełączni rozłączne: zakończenie wyselekcjonowanej sekwencji ... kończy całą
ka w F są
instrukcję (tak jak w P, ale odwrotnie do C); ■ (9) subrutyna F_start_init jest wewnętrznym elementem m odułu Fr_types, dlatego kończąca ją dyrektywa jest postaci: end subroutine F_start_init.
F-I. F9x: program Fr_ev_test.f90
implicit none type(F_T_PAR) PAR type(F_T_START) START type(F_T_ERR_ANAL) ERV real*8 RNl(*),VNl(*),a,cs,sn logical L_ER,L_EV
Wykorzystujemy tu wzory (30-32):
F-I.4. Analiza błędów numerycznych Listing F-anal subroutine F_errl_anal(PAR,START,ERV,RN1,VN1,L_ER, L_EV) implicit none
type(F_T_PAR) PAR type(F_T_START) START type(F_T_ERR_ANAL) ERV real*8 RN1(*),VN1(*) logical L_ER,L_EV
Wykorzystujemy tu wzory (18): ERV%X_anal=START%x0+START%v0X* PAR%t ERV%y anal=START%y0+START%v0y* PAR%t-0.5*PAR%g*PAR%t ** 2 ERV%vx anal=START%v0x; ERV%vy_ana1=START%v0y-PAR%g*PAR%t; ERV%erx=RNl(l)-erv%x_anal; ERV%ery=RNl(2)-ERV%y_anal; ¡absolute errors if (ERV%x_anal/=0d0) then; ERV%x_anal=ERV%erx/ERV%x_anal*100; else; ERV%x anal=0d0; endif ! relative horizontal error
Taki „skondensowany” zapis instrukcji if ... then ...(użyty tu przykładowo) jest oczywiście poprawny, ale niezbyt czytelny, więc lepiej go unikać. if ( E RV%y_anal/=0d0) then; E R V % y_ an al =E RV %e ry /E RV %y _a na l*1 00 ; else; E R V% Y_ anal=0d0; endif ! rela ti ve v e r t ic al error L E R = ( a b s ( E R V % x _ a n a l ) > 1 0 d 0 ) .or.(abs(ERV%y_anal)>10d0) E R V % e v y = V N 1 (2)-ER V % v y _ a n a 1 ! ab so lu te v e rt ic al speed er ro r if (ERV%vy anal/=0d0) then; E R V% vy _a na l= ER V % e v y / E R V % v y _ a n a l * 1 0 0
else; ER V% vy _anal=0d0; endif ! re la ti ve v e rt ic al speed error L_ E V = (a b s (ER V% vy _a na l) >1 0d 0) ; end subroutine F_ er rl _a na l
subroutine F_err2_anal(PAR,START,ERV,RN1,VN1,L_ER,L_EV)
a=PAR%t*sqrt(ERV%ro*PAR%g); cs=cos(a);sn=sin(a); a=sqrt(PAR%g/ERV%ro); ERV%y_anal=START%y0+& log(abs(START%v0y*sqrt(ERV%ro/PAR%g)*sn+cs))/ERV%ro; E RV%vy_an al=a *(START%v0y* cs-a * s n)/(START%v0y* sn+a * c s); ERV%ery=RNl(2)-ERV%y_anal; ! vertical position error if (ERV%y_anal/=0) then ! position relative error ERV%y_anal=ERV%ery/ERV%y_anal*100; else; ERV%y_anal=0d0; endif ERV%evy=VN1(2)-ERV%vy_ana1; ! vertical speed errors if (ERV%vy_anal/=0d0) then ERV%vy_anal=ERV%evy/ERV%vy_anal*100; else; ERV%vy_anal=0d0; endif L_ER=abs(ERV%y_anal)>10.0; L_EV=abs(ERV%vy_anal)>10.0; end subroutine F_err2_anal subroutine F_err3_anal(PAR,START,ERV,RN1,VN1,L_ER,L_EV) implicit none type(F_T_PAR) PAR type(F_T_START) START type(F_T_ERR_ANAL) ERV real*8 RNl(*),VNl(*),a,cs,sn logical L_ER,L_EV
W ykorzystujemy tu wzory (30, 35, 36): a=PAR%t*sqrt(ERV%ro*PAR%g); ERV%ery=exp(-a); ERV%evy=exp(a); sn=0.5*(ERV%evy-ERV%ery); cs=0.5*(ERV%evy+ERV%ery); !sin,cos hyperbolic ERV%y_anal=START%y0-log(abs(START%v0y*sqrt(ERV%ro/PAR%g)*sn-cs>)/ERV%ro; a=sqrt(PAR%g/ERV%ro); ERV%vy_anal=-a*(START%v0y*cs-a*sn)/(START%v0y*sn-a*cs); ERV%ery=RNl(2)-ERV%y_anal; ! position errors if (ERV%y_anal/=0d0) then ERV%y_anal=ERV%ery/ERV%y_anal*100; else; ERV%y_anal=0d0; endif ERV%evy=VNl(2)-ERV%vy_anal; ! speed errors if (ERV%vy_anal/=0d0) then
90
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
F-I. F9x: program Fr_ev_test.f90
91
Listing F-ev ERV%vy_ana1=ERV%evy/ERV%vy_an a1*100; else; ERV%vy_anal=0d0; endif L_ER=abs(ERV%y_anal)>10d0; L_EV=abs(ERV%vy_anal)>10d0; end subroutine F_err3_anal subroutine F_err4_anal(PAR,START , ERV,RN1,VN1,L_ER,L_EV) implicit none type(F_T_PAR) PAR type(F_T_START) START type(F_T_ERR_ANAL) ERV real*8 RN1(*),VN1(*) logical L_ER,L_EV
Wykorzystujemy tu wzory (26-28): ERV%y_anal=-0.5*PAR%g*PAR%t**2+PAR%sm*PAR%w/PAR%c*((ld0& PAR%c *PAR%t/PAR%sm)*(log(abs(ld0-PAR%c *PAR%t/PAR%sm))-ld0)+ld0); ERV%vy_a n a1=-PAR%g* PAR%t-PAR%w*log(a b s(ld0-PAR%c *PAR%t/PAR%stn)); ERV%ery=RNl(2)-ERV%y_anal; ! position errors if (ERV%y_anal/=0d0) then ERV%y_ana1=ERV%ery/ERV%y_ana1*100; else; ERV%y_anal=0d0; endif ERV%evy=VNl(2)-ERV%vy_ana1; ! speed errors if (ERV%vy_anal/=0d0) then ERV%vy_ana1=ERV%evy/ERV%vy_ana1*100; else; ERV%vy_anal=0d0; endif L_ER=abs(ERV%y_anal)>10.0; L_EV=abs(ERV%vy_anal)>10.0; end subroutine F_err4_anal end module Fr_types
•
(9) wszystkie cztery powyższe subrutyny F_errX_anal są wewnętrznymi ele mentami modułu Frjtypes, dlatego kończące je dyrektywy są postaci: end subroutine F_errX_anal;
■ (9) tu kończy się definiowanie m odułu Fr types, dlatego występuje dyrektywa zamykająca ten moduł: end module Fr_types.
F-I.5. Integratory Eulera i Yerleta Integratory Eulera i Verleta, ze względu na ich ogólność i m erytoryczną niezależ ność od użytych struktur rekordowych, nie zostały umieszczone wewnątrz modułu Frjtypes; dlatego w tekście źródłowym programu listing ten należy włączyć za dyrek tyw ą kończącą ten moduł.
subroutine F_e_step(PAR,ACCl,ROLDlJRNEWlJVOLDlJVNEWl,HJBETA); use Frjtypes implicit none ! Euler step: Rnew=r(t+h),Rold=r(t),Vnew=v(t+h),Vold=v(t) type(F_T_PAR) PAR real*8 ACCl(max),ROLDl(*),RNEWl(*)JVOLDl(*),VNEW1(*),H,BETA integer k ACCl=F_accl(PAR,ROLD1,VOLD1); ! acceleration defined by R0LD1,V0LD1 do k=l,PAR%neq VNEWl(k)=VOLDl(k)+ACCl(k)*H; ! new velocity RNEWl(k)=ROLDl(k)+(BETA*VOLDl(k)+(ld0-BETA)*VNEWl(k))*H & +0.5*ACCl(k)*H*H ! new positon by weighted sum of old and new velocities enddo end subroutine F_e_step subroutine F_V_Step(PAR,ACC1,ROLD1,RNEW1,ROLDER1,VOLD1,VNEW1,H,BETA); use Fr_types implicit none ! Verlet step: Rnew=r(t+h),Rold=r(t),Rolder=r(t-h)JVold=v(t) 1 Verlet starts in 2dn step; first do single Euler step type(F_T_PAR) PAR real*8 &
ACCl(max), R0LD1(*), RNEW1(*)j R0LDER1(*), VOLDl(*), VNEW1(*),H,BETA integer k ACCl=F_accl(PAR,ROLD1,VOLD1) ¡acceleration defined by ROLD1, VOLD1 do k=l,PAR%neq RNEWl(k)=(ld0+BETA)*ROLDl(k)-BETA*ROLDERl(k)+ACCl(k)*H*H; ! new position, with a damping possibility VOLDl(k)=0.5d0*(RNEWl(k)-ROLDERl(k))/H ! old velocity VNEWl(k)=VOLDl(k)+ACCl(k)*H ! new velocity enddo end subroutine F_v_step
■
(9) użycie dyrektywy use Frjtypes oznacza, że wszystkie elementy modułu Frjtypes stają się dostępne w tych procedurach - w ten sposób włączony zostaje
w F9x m echanizm dziedziczenia, znany z koncepcji klas; ■ struktura PAR jest parametrem formalnym obu procedur wyłącznie dlatego, że jej elementy - wartości parametrów fizycznych - m uszą być przekazane do funkcji
92
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
F_accl: algorytmy Eulera i Verleta od niej nie zależą; wszystkie formalne para metry procedur o charakterze strukturalnym w F9x są automatycznie w yw oły wane przez referencję; ■
(7) odpowiednikiem pętli for jest w F9x konstrukcja: do zmienna_kontrolna=początek,koniec[,krok] ciało pętli enddo
(przejścia do nowej linii m ogą być zastąpione średnikami); zmienna kontrolna w F9x musi być typu porządkowego, domyślnym krokiem jest 1;
F-I. F9x: program Fr_ev_test.f90
F - I .6 .
Program zarządzający
Domyślną nazw ą programu zarządzającego jest w Fortranie „od zawsze” main (głów ny), dlatego jest tak też „od zawsze” w C. Jednak w F istnieje też „od dawna” dyrekty wa program, która umożliwia nadanie programowi zarządzającemu wybranej nazwy, i warto z tego korzystać. Segment typu program może w kompilowanym wspólnie pliku segmentów być tylko jeden, inaczej nie będzie możliwa konsolidacja - utworzenie pro gramu wykonawczego. Plik, w którym program zarządzający będzie zapisany, nazywa się u nas Fr_ev_test.f90. Listing Fr-driver
dopuszczalna jest także konstrukcja należąca do standardu F77A: do etykieta [,] zmienna_kontrolna=początek,koniec[Jkrok] ciało pętli etykieta
continue
zmienna_kontrolna w F77 nie musi być typu porządkowego (niezalecane!), do myślnym krokiem jest 1.
Listing F-headers subroutine F_headers(ISEL) implicit none integer ISEL select case (ISEL) ! headers: case (1) print *,'krok— t--....... x dx .............y -----dy............ ',8 'vy dvy........ v — ' case (2:3) print * j ' -krok---t........ -y dy-----------dy%....... v y ...... ',& 'dvy dvy%--v/M-' case (4) print * / k r ok---t m ----------- y ------ dy....... dy%------- & vy— ■j ■-dvy--..... dvy%-v/M--' case (5) print *,' — krok— t --------masa --x...... — y ------- vx--------',& •Vy ----- v-..... V /M - ' end select end subroutine F headers
93
program Fr_ev_test; use Fr_types ! Fr_ev_test driver implicit none integer lin,k real*8 VjVm,tp logical ^ f l j ^ e r j ^ e v character z type(F_T_PAR) par type(F_T_START) start type(F_T_ERR_ANAL) erv real*8,dimension(max) : : al,rol,rnl,voljVnl,rool par%t=0d0 ! time init vm=340.0d0 ! Mach speed call F_par_init(par) ! parameters initialized call F_start_init(start,parJrol,vol) ! start initialized if (start%l_ver) then ! before Verlet ...
Przed startem metody Verleta trzeba wykonać 1 krok metody Eulera: par%t=par%t+start%hj ! ... single Euler step cali F_e_step(parjal,roi,rnl,vol,vnl,start%h,ld0) ; rool=rol; ! rool = r(t-h) rol=rnl; ! roi = r(t) vol=vnl; ! vol = v(t+h) endif k=0; lin=l; tp=0d0 z='t'; l_fl=.true.; l_er=.false.; l_ev=.false. ! flags ... ! main integration loop do while (z= = 't ') par%t=par%t+start%h; k=k+l ! step number
! time increment
94
I. TR OC HĘ PROSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
if (start%l_ver) cali & F_v_step(par,al,roi,rnl,rool,vol,vnl,start%h,start%beta) if (start%l_elr) cali & F_e_step(par,al,roi,rnl,vol,vnl,start%h,start%beta)
Krok Eulera lub Verleta wykonany: rnl, vnl zawierają teraz numeryczne oszaco wania położenia i prędkości dla czasu t+h, v jest prędkością całkowitą. v=sqrt(vnl(l)**2+vnl(2)**2) ! actual speed erv%ro=par%b/par%am ; select case (start%isel) case (1) ! free move call F_errl_anal(par,start,erv,rnl,vnl,l_er,l_ev) write(*,'(i4 ,f7 .2 ,fl 0 .1 ,gl0 .2 ,fl 0 .1 ,gl 0 .2 ,fl0 .1 ,gl 0 .2 ,f8 .1 ) ')& k,par%t+tp,rnl(l),erv%erx,rnl(2),erv%ery,vnl(2),erv%evy,v case (2) ! vertically up, with drag call F_err2_anal(par,start,erv,rnl,vnl,l_er,l_ev) write(*,'(i5,f7.2,fl0.1,gl2.3,f8.2,fl0.1,gl2.3,f8.2,f7.3)') & k,par%t,rnl(2),erv%ery,erv%y_anal, & vnl(2),erv%evy,erv%vy_anal,v/vm if (vnl(2) old vectors endif if (start%l_elr) then rol=rnl; vol=vnl; ! new vectosr -> old vectors endif if (l_er) then ! when position error > 10%: print *,'BLAD POŁOŻENIA > 10%' l_er=.false.; lin=lin+l endif if (l_ev) then ! when speed error > 10% print *, 'BLAD PRĘDKOŚCI > 10%' l_ev=.false.; lin=lin+l endif if (lin>=40) then ! after 40 lines on screen ... 604 print '(a , $ ) ' , 'liczyc dalej? (t/n): ' read(*J*Jerr=604) z if (z=='t') then lin=0; call F_headers(start%isel) endif endif enddo if (start%nrec>0) close(start%nflJstatus='keep',err=997) Stop 'KONIEC ZABAWY 998 Stop 'BLAD ZAPISU DO PLIKU 2D' 997 Stop 'BLAD ZAMKNIĘCIA PLIKU' 996 Stop 'BLAD ZAPISU DO PLIKU ID' end
■
[k]Iw - k to opcjonalny współczynnik krotności, stała całkowita >1, jeśli nie występuje, to kod stosuje się jednokrotnie I - przesyłana jest wartość typu integer, na w (yvidth) pozycjach;
■
[k]Fw.d - przesyłana jest wartość zmiennopozycyjna (F - floating), na w pozy cjach, w tym d (idecimal) pozycji po kropce dziesiętnej; [k]Ew.d lub [kjDw.d - przesyłana jest wartość zmiennopozycyjna w formacie wykładniczym (E - exponential), na w pozycjach, w tym d {decimal) pozycji po kropce dziesiętnej + 4 pozycje na w ykładnik (D stosuje się dla wartości podwój nej precyzji, ale nie jest to obligatoryjne);
■
[k]Gw.d - (general) działa tak jak F, ale jeśli transmitowana wartość nie może być tak sformatowana, automatycznie przełącza się na format E/D;
■ A[w] - {alphanumeric) przesyłanie znaków/napisów, ewentualnie na w pozycjach; ■
[k]L[w] —{logical) przesyłanie wartości logicznych, jako symboli T lub F, ewentualnie na w pozycjach.
To nie są wszystkie kody formatowe, tylko te najważniejsze, pełny przegląd zob. np. [9-15]; symbole I F E W G A L można pisać także m ałymi literami (jak w naszym pro gramie); analogie z kodami formatowania używanymi w C są ewidentne. W Fortranie „od zawsze” poświęcano dużo uwagi na staranne formatowanie wydruków, tworzenie ładnie zredagowanych tabel itp.; dlatego Fortran jest „bezlitosny”: jeśli dru kowana wartość nie mieści się w zadeklarowanym polu o szerokości w, otrzymuje się w gwiazdek (***** ..). Łącząc wszystkie powyższe listingi: Fr-prolog Fr-init Fr-accel F-ev F-start F-anal F-headers Fr-driver
98
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
F-I. F9x: program Fr_ev_test.f90
99
otrzymujemy całość programu jako plik Fr_ev_test.f90. Jak skompilować programy w F9x w oparciu o kompilatory GNU? Komenda aktywująca kompilatory ma jed n ą z dwóch postaci:
gfortran Fr_ev_test.f90 -o Fr_ev_test.x9g
compiler source.ext [ source ...]-c -o [ o b je ct.ex t]
f77 F7_ev_test.f -o F7_ev_test.x7
lub
Możemy jednak postąpić inaczej, i w dużych programach pisanych w językach modular nych jest to praktyka bardzo częsta:
a dla programu w F77 użylibyśmy komendy np.:
compiler source.ext [ source ...][ o b je ct_ lib ][-o exec.ext]
- każdy z wymienionych segmentów skompilować osobno do kodu pośredniego, np.: gdzie: gfortran Fr_prolog.f90 -c -o Fr_prolog.o9g
■ compiler - nazwa kompilatora: gfortran, g95, ifo r t,... dla F9x; f77, g77 lub gfortran dla F77; ■ source.ext - nazwa pliku zawierającego kod źródłowy (pojedynczy segment, kolekcję segmentów, cały program), w raz z rozszerzeniem: rozszerzenie .f jest obligatoryjne dla F77 (automatycznie włącza „sztywny” format zapisu F77), roz szerzenie .f 90 jest „najbezpieczniejsze” dla F9x, dla kompilatora ifort jest obli gatoryjne; ■ elementy ujęte w [...] są (jak zwykle) opcjonalne: ■ [ source . . . ] - ewentualne dalsze pliki źródłowe, które chcemy wspólnie kompi lować;
i zapisać go do naszego prywatnego (np. dedykowanego do tego projektu) archiwum kodów pośrednich, np. standardową UN IXową funkcją ar: ar -rv Fr_test.l9g Fr_prolog.o9g
W ażna uwaga: jeśli nasze archiwum ma być użyte w procesie konsolidacji, wszystkie potrzebne do tego celu kody pośrednie muszą być wygenerowane tym samym kompi latorem: tu kody te i archiwum oznaczone zostało w rozszerzeniu jako 9g, tzn. (w mojej prywatnej konwencji) kompilowane gfortranem. A więc powyższa komenda oznacza: zapisz plik Fr_prolog.o9g do archiwum Fr_test.l9g; kod r (replace), znaczy: jeśli taki plik ju ż tam jest, to go zastąp; kod v (yerbose) oznacza: wypisz komunikat o operacji.
■ -c oznacza: tylko kompiluj, nie konsoliduj (ma zastosowanie do podprogramów);
Tak możemy postąpić z każdym wymienionym wyżej segmentem, oprócz ostatnie go - programu zarządzającego (który zapiszemy wówczas pod nazw ą na przykład Fr_ ev_ test .f90). Ostatnia komenda będzie wówczas postaci:
■ -o [ o b je ct.ex t] - nazwa pliku z kodem pośrednim, powstającego w w yniku kompilacji; jeśli pom inięta - nazw ą będzie a.out (niebezpieczne, bo plik a.out z poprzedniej kompilacji - o ile istnieje - zostanie nadpisany bez ostrzeżenia!);
gfortran Fr_ev_test.f90 Fr_test.l9g -o Fr_ev_test.x9g
■ [ object_lib] - biblioteka/archiwum kodów pośrednich (. Pisząc: set grid spowodujemy, że na wykresie pojawi się siatka współrzędnych, wyskalowana automatycznie do potrzeb konkretnego przypadku. Polecenie tworzenia wykresu ekranowego z pliku dane ma ogólną postać:
plot ,'Pr_e2d_a.dat, u 2:3,JPr_e2d_b.datJ u 2:3
Tor dla kąta a wystąpi jako ciąg czerwonych +, dla kąta P jako ciąg zielonych X; teraz, jeśli jest to przypadek 1 (rzut bez oporu), możemy przekonać się naocznie, na ile zasięg rzutów dla kątów a i 90-a jest w naszych obliczeniach identyczny. Każdy wykres zo stanie automatycznie wyskalowany, tak żeby wszystkie użyte do niego punkty były wi doczne, ale to oznacza, że skale osi poziomej i pionowej będą na ogół różne (i to czasa mi bardzo), dlatego warto zawsze nałożyć na wykres siatkę (set grid); proporcje okna można też zmieniać odpowiednimi poleceniami, lub prościej - przeciągając jedną z kra wędzi pionowych kursorem. W wypadku wykresów ID (przypadki 2, 3, 4) interesujące będą zależności: położenia (tzn. wysokości) od czasu r/t) (u 1:2), prędkości od czasu v /t) (u 1:5), prędkości od po łożenia v2(r) (u 2:5), błędu bezwzględnego położenia od położenia A r /r J (u 2:4) itp.
plot JdaneJ using kol_a:kol_b [with lines s]
(używać m ożna zarówno apostrofów, jak i cudzysłowów), co można skrócić do: plot fdaneJ u kol_a:kol_b [w 1 s]
Utworzony zostanie wykres danych z kolumny kol_b jako funkcji danych z kolumny kol_a; jeśli włączymy opcjonalne param etry [...], będzie to wykres liniowy (linia łam a na), w przeciwnym razie - punktowy; param etr s wybiera tu rodzaj znacznika i kolor li nii, np. ‘1’ to linia ciągła czerwona, znacznik +; ‘2 ’ to linia ciągła zielona, znacznik X; ‘-1’ to linia czarna ciągła; ‘0’ oznacza linię czarną kropkowaną itp. - warto zajrzeć do cytowanych wyżej źródeł, wybór opcji graficznych jest duży. Wykres punktowy jest dla nas bardziej informatywny niż liniowy, bo lepiej pokazuje poszczególne kroki całkowa nia. Otrzym ać można także wykres w postaci pliku w wybranym formacie graficznym, zob. np. [15], także Z -l.l. W przypadkach 2D (1, 5) możemy w szczególności otrzymać punktowy wykres toru (czyli zależność y =f(x) lub, w naszej notacji, r2 = f(r))\ gdy wyniki obliczeń zapisane sąnp. w pliku Pr_e2d.dat, użyjemy komendy:
Z -I.l. Zabawa nr 1 - rzut ukośny bez oporu Boisko do piłki nożnej ma długość 105 m, piłka ma wystandaryzowanąm asę ok 0.43 kg i średnicę ok 22 cm (te dane są w tej chwili nieistotne, ale wkrótce przydadzą się). Przypuśćmy, że opór powietrza nie istnieje (np. gramy na Księżycu); zadajmy pytanie: jak ą prędkość trzeba nadać piłce, żeby zasięg takiego „rzutu” pod kątem 20° był rzędu 100 m? Może to przesada: kto kopie piłkę aż tak daleko? Ale pamiętam (pewnie nie tyl ko ja) przypadek, na jakim ś bardzo ważnym meczu, gdy bram karz jednej drużyny tak wykopał piłkę, że trafiła ona prosto do bram ki drugiej drużyny, ku absolutnemu zdu mieniu jej bram karza (gol został uznany); a więc nie jest to przypadek czysto akade micki. Dialog z programem przedstawiony jest poniżej w Listingu Z -I.la. Wybieramy kolejno: ■ przyspieszenie grawitacyjne: 0 (czyli 9.81 m/s2 - a więc jednak gramy na Ziemi) ■ masa piłki: 0.43 kg; m asa końcowa: 0, czyli taka, jak początkowa (to nie rakieta...) ■ współczynnik oporu b: 0 - bez oporu
102
I. TR O C H Ę PROSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
103
Z-I. Kilka testów - przystępujemy do zabawy
■ metoda: Eulera 25 26 27 28 29
■ parametr „mieszania” /?: 1 - „czysty” Euler ■ krok całkowania h: 0.1 s
45.9 47.7 49.6 51.4 53.2
-0.28E-13 -0.36E-13 -0.43E-13 -0.43E-13 -0.50E-13
39 1.95 71.6 -0.57E-13 liczyc dalej? (t/n): t
■ położenie początkowe x, y: 0 0 ■ prędkość początkowa i kąt rzutu: 39.067 m/s (tę wartość można z góry oszacować ze wzoru (20), 20° ■ zapis na plik: co 5. krok Program podaje teoretyczne wartości: zasięgu poziomego (100.00 m) i wysokości (9.10 m). Następnie wypisuje nagłówek - znaczenie poszczególnych kolumn (ich układ jest tu trochę inny niż na pliku) i kolejne kroki procesu całkowania. Kolumny x, y, vy to oczywiście położenie i prędkość (składowa pozioma, vx, jest w tym przypadku sta ła), odpowiednie błędy bezwzględne dx, dy, dvy i prędkość całkowitą v. Po wypisaniu 40 linii możemy przyjrzeć się temu protokółowi i zdecydować, czy chcemy kontynu ować obliczenia: jeśli tak (t) - ponownie wypisane zostają nagłówki kolumn. Osiągnię cie poziomu startu zostaje zasygnalizowane komunikatem. W Listingu Z-I-la (odpo wiedzi na klawiaturę pogrubione) dla oszczędności pokazane jest tylko po kilka kroków: na starcie, w rejonie maksimum i na końcu. Listing Z-I-la przyspieszen i e g r a w i t a c y j n e [m/sA 2] (>0; 0 - 9.81): 0 masa początkową, końcowa [kg] (=0.0): 0 metoda: Eulera (e) czy V e r l e t a (v)?; inaczej - K O NIEC ZABAWY: e p arametr mie s z a n i a z prz e d z i a ł u 1 - 0 (1: "czysty" E l ub V): 1 krok całkowania h (>0): .05 poda: warunki początkowe położenie x, y [ m ] : 0 0 prędkość v [m/s], kat [ d e g ] : 39.067 20 s kładowe prę d k o ś c i [m/s]; vx: 3 6 . 7 1 1 0 vy: 1 3.3617 1: zasięg poziomy, w y s o k o s c [m]: 10 0 . 0 0 4 3 9 .0996 z apis na plik co n rec k r o k o w (nrec>=0, 0 - b e z zapisu): 5 Euler: 100. % vo i d + 0. % v new d v y .......... k ro k — t ---------- x ---- d x - .............. -y ---dy- — ...........v y 1 0.05 1.8 0 .0 0 .7 0 . 0 12.9 0.0 2 0.10 3 .7 0 . 0 1.3 0 . 0 1 2 . 4 -0.18 E - 1 4 3 0.15 5 .5 -0.89 E - 1 5 1 .9 -0.22 E - 1 5 11.9 -0 . 1 8 E - 1 4
1.25 1.30 1.35 1.40 1.45
40 41
2.00 2.05
73.4 -0.43E-13 75.3 -0.43E-13
54 2.70 55 2.75 POZIOM S TARTU 56 2.80 57 2.85
99.1 0.99E-13 101.0 0 . 11E-12 OSIĄG N I Ę T Y 102.8 0.13E-12 104.6 0 . 1 4E-12
9.0 9.1 9.1 9.1 9.1
-0.89E-14 -0.71E-14 -0.71E-14 -0.89E-14 -0.71E-14
1.1 0.6 0.1 -0.4 -0.9
-0.27E-14 -0.23E-14 -0.19E-14 -0.15E-14 -0.11E-14
36.7 36.7 36.7 36.7 36.7
7.4 -0.71E-14
-5.8
0.36E-14
37.2
y uy 7.1 -0.80E-14 6.8 -0.89E-14
-6.3 -6.7
0.18E-14 0.0
37.2 37.3
0.3 -0.41E-13 -0.3 -0.45E-13
-13.1 -0.30E-13 -13.6 -0.34E-13
39.0 39.2
-1.0 -0.50E-13 -1.8 -0.54E-13
-14.1 -0.36E-13 -14.6 -0.37E-13
39.3 39.5
Dokonując interpolacji liniowej m iędzy krokami 54 i 55, uzyskamy praktyczne odtwo rzenie teoretycznej wartości zasięgu poziomego (program zrobiłby to za nas, gdybyśmy wybrali znacznie gęstszy krok całkowania, np. h = 0.001 s); wysokość maksimum toru (kroki 27, 28) też jest dobrze odtwarzana. Błędy absolutne są na poziomie numerycznej dokładności podwójnej precyzji - nie przekraczają 10'13, dokładniej po prostu już być nie może. Zobaczmy jeszcze jak wygląda zapis na pliku (co 5. krok całkowania): Listing Z-I-la-Fr_e2d_zll_20.dat 9.1777 18.355 27.533 36.711 45.889 55.066 64.244 73.422 82.600 91.777 100.96
3.0339 5.4546 7.2622 8.4567 9.0381 9.0063 8 .3614 7 .1034 5.2323 2.7480 0.34938
36.71 36.71 36.71 36.71 36.71 36.71 36.71 36.71 36.71 36.71 36.71
10.909 8.4567 6.0042 3.5517 1.0992 -1.3533 -3.8058 -6.2583 -8.7108 -11.163 -13.616
38.3 37.7 37.2 36.9 36.7 36.7 36.9 37.2 37.7 38.4 39.2
16.6 13.0 9.3 5.5 1.7 - 2.1 -5.9 -9.7 -13.3 -16.9 -20.3
W przypadkach 2-wymiarowych (2D) w poszczególnych kolumnach pliku mamy: — v— 38.9 38.7 3 8.6
kolumna 1 czas t [s] 2,3 współrzędne rp r2 (czyli x, y) [m] 4,5 składowe prędkości vp v2 (vx, vy) [m/s]
104 6 7
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
105
Z-I. Kilka testów - przystępujemy do zabawy
w poziomie są rzędu 10"13, w pionie rzędu 10'11, i tyle samo dla składowej pionowej pręd kości. Obejrzyjmy też zapis na pliku (co 3. krok całkowania, tylko początek, rejon mak simum i koniec):
prędkość całkowita v [m/s] kąt nachylenia toru-do osi poziomej [deg]
Sprawdźmy jeszcze, m etodą Verleta, czy nasza piłka kopnięta z jednakow ą siłą pod ką tem 70° (czyli 90-20) także przeleci założone ok. 100 m: Listing Z -I-lb przyspieszen i e g r a w i t a c y j n e [m/sA2] (>0; 0 - 9.81): 0 masa początkową, k o ńcowa [kg] (=0.0): 0 metoda: Eulera (e) czy V e r l e t a (v)?; inaczej - K O NIEC ZABAWY: v pa rametr mie s z a n i a z p r z e d z i a ł u 1 - 0 (1: "czysty" E lub V): 1 krok ca łkowania h (>0): .05 PODAJ WARUNK I POCZĄT K O W E położenie x, y [m]: 0 0 prędkość v [m/s], kat [deg]: 39.067 70 składowe prę d kości [m/s]; vx: 1 3 . 36 1 7 vy: 36.7 1 1 0 1: zasięg poziomy, w y s o k o s c [ m ] : 10 0 . 0 0 4 3 68.6 8 9 9 zapis na plik c o nrec k r o k o w (nrec>=0, 0 - b e z zapisu): 3 100. % t/erlet + 0. % tłu m i e n i a - dvy--..... krok35.7 0.0 3.6 0.0 1.3 0.0 0.10 1 35.2 -0.71E-14 5.4 -0.89E-15 2.0 0.0 2 0.15 34.7 0.0 7.1 -0.89E-15 2.7 -0.44E-15 3 0.20 34.3 0.0 8.9 0.0 3.3 -0.44E-15 4 0.25
Listing Z-I-lb-Fr_v2d_zll_70.dat 0.200 0 . 350 0.500
2.6723 4.6766 6.6809
7 .1460 12.248 17.129
13.362 1 3.362 1 3.362
34.749 33.277 31.806
37.2 35.9 34.5
69.0 68.1 67.2
3.350 3.500 3.650 3.800 3.950 4. 100 4. 250
4 4.762 4 6.766 4 8.770 50.774 52.779 54.783 56.787
67.935 68.402 68.648 68.673 68.478 68.062 67.425
1 3.362 13.362 13.362 13.362 13.362 13.362 13.362
3.8475 2.3760 0.90447 -0.56703 -2.0385 -3.5100 -4.9815
13.9 13.6 13.4 13.4 13.5 13.8 14.3
16.1 10.1 3.9 -2.4 -8.7 -14.7 -20.4
7.100 7.250 7.400 7.550
94.868 96.872 98.877 100.88
13.387 8.3355 3 .0634 -2.4294
13.362 13.362 13.362 13.362
-32.940 -34.412 -35.883 -37.355
35.5 36.9 38.3 39.7
-67.9 -68.8 -69.6 -70.3
38.1 37.7 37.2 36.8
26.7 -0.75E-13 2.00 39 liczyc dalej? (t/n): t
53.8
0 . 38E-12
17.1
0.10E-11
21.7
40
2.05
2 7.4 -0.75E-13
54.6
0.43 E -1 2
16.6
0.11E-11
21.3
72 73 74 75 76
3.65 3.70 3.75 3.80 3.85
48.8 49 . 4 50.1 50.8 51.4
0.0 0.0 0 . 71E-14 0.71E - 1 4 0.71E - 1 4
68.6 68.7 68.7 68.7 68.6
0. 36E-11 0.38E-11 0 . 39E-11 0.41E-11 0 .42E-11
0.9 0.4 -0.1 -0.6 -1.1
0. 28E-11 0.28E-11 0.29E-11 0.29E-11 0.30E-11
13.4 1 3.4 1 3.4 1 3.4 1 3.4
98.2 -0.14E-12 7.35 146 98.9 -0.14E-12 7.40 147 99.5 -0.16E-12 7.45 148 100.2 -0.16E-12 149 7.50 POZIOM STARTU OSIĄGN I Ę T Y 100.9 -0.16E-12 150 7.55 101.5 -0.16E-12 7.60 151
4.8 3.1 1.3 -0.6
0.21E - 1 0 0.21E - 1 0 0.22E - 1 0 0 . 22E-10
-35.4 -35.9 -36.4 -36.9
0.58E-11 0.58E-11 0.58E-11 0.58E-11
37.8 38.3 38.8 39.2
-2.4 -4.3
0 . 22E-10 0 .23 E - 1 0
- 37.4 -37.8
0.58E-11 0.58E-11
39.7 40.1 r1/x [m]
Dokonując interpolacji liniowej między krokam i 148 i 149, uzyskamy praktyczne od tworzenie teoretycznej wartości zasięgu poziomego, wysokość maksimum toru (kroki 73, 74) też jest dobrze odtwarzana. Błędy absolutne też są bardzo małe: dla położenia
Rys. 1. Rzut ukośny bez oporu, v = 39.067 m/s: Skumulowany wykres torów dla rzutów pod ką tami 20° i 70°.
106
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
Dla tego pierwszego wykresu zacytuję komplet komend gnu p lo ta: > set terminal xll # wybór terminala do prezentacji grafiki na ekranie > set grid # aktywacja siatki na wykresie > set xlabel 'rl/x [m]J # opis osi poziomej > set ylabel 'r2/y [m]J # opis osi pionowej > set xrange [0:102] # ograniczenie zakresu osi poziomej > plot 'Fr_e2d_zll_20.dat’ u 2:3 title rkat 20’ w lp It -1 lw 1 pt 4 ps 2, 'Fr_v2d_zll_70.dat’ u 2:3 title 'kat 70’ w lp It -1 lw 1 pt 12 ps 2 # powstanie obraz na ekranie > set terminal pdf # wyjście na plik graficzny formatu pdf > set output 'pcfl_rysl.pdf’ # grafika zapisana zostanie w pliku pcfl_rysl.pdf > replot # powtórzenie wykresu i skierowanie go na zdefiniowany output > unset output # „odłączenie" uprzednio zdefiniowanego outputu > set terminal xll # powrót do terminala standardowego
W komendzie plot użyte zostały następujące skróty: u - using; w - with; lp - linepoints (tzn. linia, na niej zaznaczone punkty); lt - linetypes (typ linii: 1 - czarna ciągła); lw - linwidth (0 - najcieńsza; w for macie p df niedostępna); pt - pointtypes (rodzaj punktu); ps - pointsize
Przegląd dostępnych w używanej implementacji GNU Plota elementów graficznych otrzymamy pisząc komendę > test; informację o dostępnych poleceniach otrzym a my po komendzie: > show polecenie. Ciąg komend bywa skomplikowany, szczegól nie przy wykresach skumulowanych. Wszystkie komendy, od początku konfigurowa nia środowiska graficznego (tu > set terminal ... ), do ostatnio wykonanej komendy > plot, można zachować w pliku kom endą > save 'nazwa’; w razie potrzeby można je załadować kom endą > load 'nazwa’; odpowiednia komenda plot (bez „obudowy” zmiennymi środowiska graficznego) będzie pokazywana przy każdym wykresie. Wy konanie komendy >load wygeneruje w odpowiedzi odpowiednią grafikę na ekranie; je śli chcemy dokonać jakichś zmian, możemy plik komend wyedytować dowolnym edyto rem znakowym. Jeśli grafika m a być wydrukowana, zalecanym formatem w komendzie > set output jest bardzo rozpowszechniony pdf (portable data file).
Z-1.2. Zabawa nr 2 - wytaczamy działo Jeśli chcemy otrzymać rzetelny test dla rzutu pionowo w górę z oporem, kopanie pił ki nam nie wystarczy, musimy użyć działa, i to przeciwlotniczego, bo tylko takie m ogą strzelać pod kątem 90°. Wybieramy szwajcarskie działko Oerlikon GDF-001 kał. 35 mm,
107
Z-I. Kilka testów - przystępujemy do zabawy
należące do najlepszych w swojej klasie, używane aktualnie w różnych armiach. Pocisk o masie 0.55 kg wyrzucany jest z prędkością 1175 m/s i osiąga pułap rzędu 4000 m [26], Nasza „zabawa” będzie tym razem polegała na dobraniu takiego współczynnika oporu aerodynamicznego b, który w ramach przyjętego tu modelu numerycznego umożliwi odtworzenie tych danych. W yniki, które na pewno można jeszcze nieco polepszyć, uzy skane m etodą Eulera, są następujące: Listing Z-I-2 p r z y s p ieszenie g r awitacyjne [m/sA 2] (>0; 0 - 9.81): 0 masa początkową, końcowa [kg] (=0.0): .000294 opor: - 0.000294* vA 2 metoda: Eulera (e) czy Ve r l e t a (v)?; inaczej - KONIEC ZABAWY: e parametr m i eszania z prze d z i a ł u 1 - 0 ( 1 : “czysty" E lub V): 1 krok całkowania h (>0): .03 P0DA3 WARUNKI POCZĄTKOWE położenie x, y [ m ] : 0 0 prędkość v [m/s], kat [ d e g ] : 1175 90 składowe prędkości [m/s]; vx: 0.0000 vy: 1175.0000 2: pionowo w gore z oporem, zasięg [m], czas [s ] : 4042.5981 zapis na plik co nrec k rokow (nrec>=0, 0-bez zapisu): 10 Euler: 100. % void + 0. % vne w - k r o k---t ...... - — y - - - ....... d y ----- -----dy%-- ----- v y ------d v y ----- — 1 2 3 4
0.03 0.06 0.09 0.12
34.9 69.2 102.8 135.8
-0.417E-02 -0.203E-01 -0.474E-01 -0.843E-01
-0.01 -0.03 -0.05 -0.06
1152.6 1131.0 1110.2 1090.1
-0.415 -0.792 -1.13 -1.45
20.1065
dvy%-0.04 -0.07 -0.10 -0.13
--V/M3.390 3.326 3.265 3.206
1.958 -0.62 -0.37 665.8 -4.16 39 1.17 1022.0 -3.81 liczyc dalej? (t/n): t -k r o k — t ---------- y --------- d y ------ ----- dy%-- ----- v y .......d v y ---- — dvy%- --v/M658.4 -4.15 -0.63 1.937 40 1.20 1041.9 -3.94 -0.38 -0.74 -0.74
1.123 1.115
-0.641 -0.641
-9.80 -10.26
0.017 0.016
5.3
-0.641
-10.77
0.016
-0.73
0.3
-0.640
-67.60
0.001
-0.73
0.0
-0.640
-98.07
0.000
-0.73
-0.3
-0.640
178.57
0.001
1981.0 1992.5
-10.6 -10.7
-0.53 -0.53
381.9 379.3
6 48 1 9.44 4 022.5 649 19.47 4 022.6 BLAD PRĘDKOŚCI > 10% 650 19.50 4 022.8 BLAD PRĘDKOŚCI > 10%
-29.0 -29.1
-0.72 -0.72
5.9 5.6
-29.1
-0.72
402 4 . 2 -29.4 > 10% 4 024.2 -29.4 > 10% 4 024.2 -29.4 Z OPOREM:
103 104
3.09 3.12
667 20.01 BLAD PRĘDKOŚCI 668 2 0 .04 BLAD P R ĘDKOŚCI 669 20.07 D A L E 3 SPADANIE
-2.86 -2.84
108
I. TR O C H Ę P R OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
109
Z-I. Kilka testów - przystępujemy do zabawy
W przypadkach jednowymiarowych (ID) w poszczególnych kolumnach pliku mamy: kolumna 1 czas t [s] 2 wysokość r2 [m] 3 błąd bezwzględny wysokości (40) [m] 4 błąd względny wysokości (42) [%] 5 prędkość pionowa v2 [m/s] 6 błąd bezwzględny prędkości (41) [m/s] 7 błąd względny prędkości (42) [%] Użyta wartość b prowadzi do zasięgu pionowego 4024 m, przy teoretycznym zasięgu 4043 m. Ważniejsze jednak jest śledzenie błędów: błąd absolutny położenia wzrasta od poziomu 0.004 m na początku do 29 m na końcu, w maksimum toru, po 668 krokach całkowania; dla prędkości utrzymuje się praktycznie stale błąd rzędu 0.5 m/s. Błędy względne są poniżej 0.7%, tylko w rejonie maksimum toru, gdy prędkość spada do zera, błąd prędkości oczywiście chwilowo wzrasta (Listing Z-I.2-Fr_eld_zl2.dat, co 10. krok; Rys. 2). Dokładności te można polepszyć, stosując gęstszy krok całkowania, np. h = 0.01, lub stosując metodę Verleta; sprawdzenie zostawmy dociekliwym. Po osiągnięciu maksimum obliczenia automatycznie przełączają się na przypadek spad ku z oporem - czyli zabawę nr. 3.
t[s]
Rys. 2. Skumulowany wykres wysokości r2 i prędkości pionowej v2jako funkcji czasu. Oś piono wa jest w skali logarytmicznej (logarytmy dziesiętne) - umożliwia to przedstawianie obu wielko ści na jednym wykresie (w tym celu przed komendą > plot należy użyć: > set logscale y). Generowanie wykresu: p lo t 'p lik ' u 1:2 title ’r 2 /y ( t ) ' w lp lt -1 lw 1 pt 4 ps 2,'plik' u 1:5 title 'v 2 /v y (t> ' w lp lt -1 lw 1 pt 12 ps 2
Listing Z-I-2-Fr_eld_zl2.dat w pn. I.7., współczynnik oporu aerodynamicznego dla takiej prędkości asymptotycznej musi, przy wadze skoczka 100 kg, wynosić 0.39. No to spadamy... Listing Z-I-3
Z-I.3. Zabawa nr 3 - skaczemy, bez spadochronu Teraz wykonamy skok z opóźnionym otwarciem spadochronu z wysokości 2000 m, wie rząc, że po dość długim spadaniu swobodnym opór powietrza ustabilizuje prędkość spa dania na poziomie ok 50 m/s i że spadochron jednak otworzy się. Jak wspomnieliśmy
p r z y spieszenie g r awitacyjne [m/sA 2] (>0; 0 - 9.81): 0 masa początkową, końcowa [kg] (=0.0): .39 opor: - 0.390000* v A 2 metoda: Eulera (e) czy V e r leta (v)?; inaczej - KONIEC ZABAWY: v parametr mieszania z przedziału 1 - 0 (1: "czysty" E lub V): 1 krok całkowania h (>0): .05 PODA! WARUNKI POCZĄTKOWE położenie x, y [ m ] : 0 2000 prędkość v [m/s], kat [deg]: 0 -90 składowe prędkości [m/s]; vx: 0.0000 vy: -0.0010 -50.1536 3: p i o nowo w doi z oporem, prędkość g raniczna [m/s]: 5 zapis na plik co nrec k r o k o w (nrec>=0, 0-bez zapisu):
110
I. TR O C H Ę PROSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
100. Verle t + 0. % t ł u m i e n i a -krok 1 0.10 2000.0 -0.785E-06 POZIOM STARTU OSI Ą G N I Ę T Y 2 0.15 1999.9 -0.176E-05 3 0.20 1999.8 -0.313E-05 4 0.25 1999.7 -0.487E-05 5 0.30 1999.6 -0.700E-05
____dy%-- ----v y--0.00 - 0.548E-04 -1.0
0.01
0.003
-0.00 -0.00 -0.00 -0.00
-1.5 -2.0 -2.5 -2.9
-0.938E-04 - 0.133E-03 - 0.171E-03 - 0.210E-03
0.01 0.01 0.01 0.01
0 .004 0.006 0.007 0.009
-0.277E-03 -0.278E-03 -0.278E-03
-0.00 -0.00 -0.00
-24.6 -25.0 -25.4
-0.121E-02 -0.120E-02 -0.120E-02
0.00 0.00 0.00
0.072 0 .074 0 .075
315 15.80 1384.8 0 . 545E-02 316 15.85 1382.3 0 . 546E-02 317 15.90 1379.8 0 . 547E-02 318 15.95 1377.3 0 . 547E-02 319 16.00 1374.8 0 . 548E-02 liczyc dalej? (t/n): t 1 y---krok 320 16.05 1372.3 0.549E-02
0 .00 0.00 0 .00 0 .00 0 .00
-49.9 -50.0 -50.0 -50.0 -50.0
0.106E-03 0 . 105E-03 0 . 103E-03 0.102E-03 0.101E-03
-0.00 -0.00 -0.00 -0.00 -0.00
0.147 0 .147 0.147 0 .147 0 .147
54 55 56
2.75 2.80 2.85
1 964.6 1963.3 1962.1
- --
358 359 360 361
17.95 18.00 18.05 18.10
1277.2 1274.7 1272.2 1269.7
...... -dy-- -dy%-0.566E-02 0.567E-02 0.567E-02 0 . 567E-02
0.00
---- vy--50.0
0.00 0 .00 0.00 0.00
-50.1 -50.1 -50.1 -50.1
111
Z-I. Kilka testów - przystępujemy do zabawy
17.550 17.800 18.050 18.300 18.550 1 8.800 1 9.050
1297. 1285. 1272. 1260. 1247. 1235.
1222.
.5634E-02 .5653E-02 .5671E-02 .5687E-02 .5702E-02 .5716E-02 .5728E-02
0.4343E-03 0.4400E-03 0.4457E-03 0.4514E-03 0.4572E-03 0.4629E-03 0.4687E-03
-50.05 -50.06 -50.07 -50.08 -50.08 -50.09 -50.10
0 . 6 503E-04 0 . 6 047E-04 0 . 5619E-04 0 .5219E-04 0 .4844E-04 0 .4494E-04 0 .41 6 8 E - 0 4
-0.1299E-03 -0.1208E-03 -0.1122E-03 -0.1042E-03 -0.9673E-04 -0.8973E-04 -0.8319E-04
-- -- dvy 0.992 E - 0 4
dvy%- --V/M-0.00 0 .147
0. 579E-04 0 . 570E-04 0 . 562E-04 0 . 554E-04
-0.00 -0.00 -0.00 -0.00
0 .147 0 .147 0 .147 0 .147
Na początku spadania prędkość bardzo szybko rośnie: połowę prędkości asymptotycznej osiągamy już po ok. 3 s i niecałych 40 m. Potem wzrost jest bardzo powolny, prędkość spadania 50 m/s osiągamy na poziomie ok. 1380 m po ok. 16 s spadania; spadać istotnie szybciej już nie będziemy. Błędy absolutne położenia i prędkości są znikome, a błędy względne, na poziomie 10'4%, zobaczyć można tylko na pliku (co 5. krok, Rys. 3):
wysokosc r2/h [m]
Rys. 3. Spadanie pionowo z oporem: błędy absolutne położenia i prędkości w funkcji wysokości
(tzn. drogi r2); ruch rozpoczyna się na wysokości 2000 m, więc skalę osi poziomej trzeba było od wrócić, w tym wypadku poleceniem > set x r ange [2000:1200] (metoda Verleta).
Listing Z-I-3-Fr_vld_zl3.dat
1999. 1997.
•7000E-05 .2 3 0 9 E - 0 4 •4742E-04
•3501E-06 ■1155E-05 ■2375E-05
-2.941 -5.376 -7.786
.2 100E-03 .3982E-03 •5750E-03
•7141E-02 . 7 407E-02 •7386E-02
3.050
1969. 1963. 1957.
.2 6 9 2 E - 0 3 . 2775E-03 .2731E-03
•1367E-04 .1414E-04 •1396E-04
-23.13 -25.02 -26.81
•1211E-02 •1203E-02 .1173E-02
•5235E-02 •4808E-02 .4375E-02
16.550 16.800 17.050 17.300
1347. 1335. 1322. 1310.
.55 42E-02 .5568 E - 0 2 .5592 E - 0 2 .5614 E - 0 2
.4114E-03 •4171E-03 •4229E-03 •4286E-03
-50.00 -50.01 -50.03 -50.04
.8641E-04 .80 5 7 E - 0 4 .75 0 7 E - 0 4 •6990E-04
•1728E-03 .1611E-03 ■1501E-03 •1397E-03
Generowanie wykresu: plot 'plik' pt 4 ps 2,'plik' u 2:6 title 'blad
u 2:3 title 'blad położenia' w lp prędkości' w lp lt -1 lw 1 pt 12
lt ps
-1 2
lw
1
Z-I.4. Zabawa nr 4 - skąd tu wziąć rakietę... Jeśli chcemy przetestować, jak nasz program opisuje rakiety, trzeba dysponować moż liwie szczegółowymi danymi technicznymi jakiegoś konkretnego modelu. A o to w od niesieniu do realnych i aktualnie używanych rakiet raczej trudno - dane takie są naj częściej utajnione. Pozostaje więc odwołać się do konstrukcji historycznej, ale szeroko opisanej - do rakiety V2. Obszerne omówienia szczegółów technicznych tej rakiety
112
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
można znaleźć w Internecie, np. [29]. Nie są one jednak wolne od pewnych rozbieżno ści, stąd podane niżej przedziały wartości. Dane, które b ędą dla nas użyteczne, to:
zapis na plik co nrec krokow (nrec>=0, 0 -bez zapisu): Euler: 100., % void + 0. % vne w Huy u ______ ___dy%-
10
_
* masa początkowa ms : 12500-13000 kg, ■ masa końcowa mf : 4500-5000 kg, ■ prędkość spalania - wydatek m asy c: 125 - 130 kg/s: to oznacza czas pracy silni ka tk ok 60 s, ■ siła ciągu c*w: od 245 kN na starcie do 712 kN w maksimum, co oznacza prze dział prędkości gazów napędowych w od ok. 1900 do 5700 m/s; przedział ten jest bardzo szeroki i stanowi to najsłabszy punkt w naszym modelu zakładającym stałą siłę ciągu silnika rakiety. Manipulując stałą prędkością gazów w (czyli stałą siłą ciągu silnika) spróbujemy z grub sza odtworzyć następujące dane: ■ przyspieszenie startowe ok. 1 G (ponad przyspieszenie ziemskie, tzn. całkowite ok 2 G), * przekroczenie bariery dźwięku po 25-30 s, ■ maksymalna prędkość - na końcu pracy silnika: 1340-1600 m/s. Wybieramy jakiś sensowny zestaw danych i odpalamy rakietę: Listing Z-I-4 przyspieszen i e g r a w i t a c y j n e [m/sA 2] (>0; 0 - 9.81): 0 masa początkową, k ońcowa [kg] ( 7 9.6 siła oporu t ypu - b*vA 2; podaj b ([kg/m] (>=0.0): 0 metoda: Eulera (e) c z y V e r l e t a (v)?; inaczej - KON I E C ZABAWY: e parametr m i e s z a n i a z p r z e d z i a ł u 1 - 0 (1: "czysty" E l ub V): 1 krok całkowa n i a h (>0): .05 PODAJ WARUNKI POCZĄT K O W E położenie x, y [ m ] : 0 0 prędkość v [m/s], kat [ d e g ] : 0 90 składowe p r ę d k o ś c i [m/s]; vx: 0 . 0 0 0 0 vy: 0.0 0 1 0 4 : rakieta p i o n o w o w gore, bez o poru
113
Z-I. Kilka testów - przystępujemy do zabawy
2.058
1 2 3
0.05 0.10 0.15
12993.8 12987.5 12981.
0.0 0.1 0.1
286 287 288
14.30 14.35 14.40
11212.5 11206.2 11200.0
1163.8 1172.4 1180.9
507 508 509 510 511
25.35 25.40 25.45 25.50 25.55
9831.2 9825.0 9818.7 9812.5 9806.2
3940.5 3957.4 3974.4 3991.4 4008.5
0.58E-04 0.13E-03 0.21E-03
0.45 0.25 0.18
0.5 1.0 1.6
0.12E-02 0.15E-02 0.17E-02
0.24 0.14 0.11
0.002 0.003 0.005
0.56 0.57 0.57
0.05 0.05 0.05
170.4 171.1 171.8
0.81E-01 0.82E-01 0.82E-01
0.05 0.05 0.05
0.501 0.503 0.505
1.9 1.9 1.9 1.9 1.9
0.05 0.05 0.05 0.05 0.05
338.2 339.0 339.9 340.7 341.6
0.16 0.16 0.16 0.17 0.17
0.05 0.05 0.05 0.05 0.05
0.995 0.997 1.000 1.002 1.005
1373.1 1375.3 1377.4
0.80 0.81 0.81
0.06 0.06 0.06
4.039 4.045 4.051
5018.8 33857.6 1277 63.85 1278 63.90 5012.5 33926.3 1279 63.95 5006.3 33995.2 liczyc dale;i? (t/n): t k ro k ---- 1 — -.....m ........ y— 1280 64.00 5000.0 34064.1 KONIEC PRACY SILNIKA 4993.8 34133.1 1281 64.05 DALEJ RZUT SWOBODNY:
18. 18. 18.
1282 1283 1284
34202.0 0.0 34270.9 0.0 34339.8 -0.73E-■11
--
64.10 64.15 64.20
0.0 0.0 0.0
0.88E-09 0.89E-09 0.89E-09
0.05 0.05 0.05
d y ----- — dy%vy—— d v y ........ dvy%- — -v/M18. 0.05 1379.5 0.81 0.06 4.057 18.
0.05
1379.1 -1.8
1378.6 1378.1 1377.6
-0.13
0.0 -0.23E-12 -0.23E-12
4.056
1378.6 1378.1 1377.6
Nie wymagajmy zbyt wiele, pamiętajmy, że w ramach użytego modelu nie ma oporu powietrza, a rakieta wznosi się pionowo; w rzeczywistości po pionowym starcie jej tor był pochylany aerodynamicznie, tak że w momencie wygaśnięcia napędu odchylenie od pionu wynosiło ok. 50°. W yniki są sensowne: przy sile ciągu 262.5 kN i przyspiesze niu startowym rzędu 1 G (ponad przyspieszenie ziemskie), połowa prędkości dźwię ku jest osiągnięta po 14 s (krok 286), a bariera dźwięku zostaje przekroczona po 25.5 s (krok 509), prędkość końcowa to 1379 m/s, silnik pracował 64 s, osiągnięta wysokość ok. 34 km (Rys. 4). Błąd absolutny położenia po 1280 krokach to 18 m, prędkości 1.8 m/s (Rys. 5), błędy względne są znikome.
114
I. T R O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
115
Z-I. Kilka testów - przystępujemy do zabawy
Z-I.5. Zabawa nr 5 - wracamy na boisko Wracamy do przykładu Z -I.l, czyli na boisko futbolowe, ale tym razem gramy napraw dę na Ziemi, czyli powietrze stawia opór naszej piłce. Znowu zapytamy: jak kopnąć pił kę tak, żeby przeleciała ok 100 m? Mamy formalnie 3 param etry do manipulowania: prędkość i kąt rzutu, no i w spółczynnik oporu aerodynamicznego b. Nawiążemy do poprzedniego przykładu i przyjmiemy kąt rzutu 20°; dla współczynnika b przyjmiemy 0.0037: to nie jest wartość w zięta „z sufitu”, można ją oszacować, a jak - dowiemy się w pn. I I.l. Pozostaje więc tylko „zabawa” z dobraniem prędkości. Tym razem nasz dia log z komputerem będzie wyglądał następująco: Listing Z-I-5a
t[s]
Rys. 4. Rakieta V2, ruch pionowo bez oporu: wysokość r2i prędkość pionowa v2jako funkcje cza su (skala logarytmiczna). Generowanie wykresu: plot 'plik' u 1:2 title 'wysokosc r2(t)' w lp lt -1 lw 1 pt 4 ps 2,'plik' u 1:5 title 'prędkość v2(t)' w lp lt -1 lw 1 pt 12 ps 2
p r z yspieszenie g r a w i tacyjne [m/sA 2] (>0; 0 - 9.81): 0 masa początkową, końcowa [kg] (=0.0): .0037 opor: - 0.003700* v A 2 metoda: Eulera (e) czy V e r leta (v)?; inaczej - KONIEC ZABAWY: e p arametr mieszania z przedziału 1 - 0 (1: "czysty" E lub V): 1 krok całkowania h (>0): .02 P0DA3 WARUNKI POCZĄTKOWE położenie x, y [■]: e 0 prędkość v [m/s], kat [ d e g ] : 55 20 składowe prędkości [m/s]; vx: 51.6831 vy: 18.8111 5 : rzut/rakieta, z opor e m lub bez z apis na plik co nrec k r okow (nrec>=0, 0-bez zapisu): 5 Euler: 100. % vold + 0. % vnew --- v y ---— krok — t--------- m a s a ---..... y — 0.02 0.4 1.0 0.4 51.2 18.4 1 2 0.04 0 .4 2.0 0.7 50.7 18.1 3 0.06 0.4 3.1 1.1 50.2 17.7 4 0.08 0 .4 4.1 1.4 49.8 17.3
:
70 71 72 73
0.4 0.4 0.4 0.4
152 3.04 0.4 153 3.06 0.4 154 3.08 0.4 155 3.10 0.4 POZIOM S TARTU O SIĄ G N I Ę T Y 156 3.12 0.4
wysokosc r2 [m]
Rys. 5. Rakieta V2, ruch pionowo bez oporu: błędy absolutne (abserr) wysokości r2 i prędkości v2 jako funkcje wysokości - czyli drogi (skala logarytmiczna). Generowanie wykresu: plot 'plik' u 2:3 title 'abserr r2' w lp lt ps 2,'plik' u 2:6 title 'abserr v2' w lp lt -1 lw 1 pt 12 ps 2
1.40 1.42 1.44 1.46
-1
lw
1
pt
4
54.4 53.8 53.3 52.7
—
v/M0.160 0.158 0.157 0.155
55.7 56.3 57.0 57.6
12.0 12.0 12.0 12.0
31.5 31.3 31.1 31.0
0.3 0.1 -0.1 -0.3
31.5 31.3 31.1 31.0
0.093 0.092 0.092 0.091
98.2 98.6 99.1 99.5
0.8 0.5 0.3 -0.0
21.4 21.3 21.2 21.1
-13.3 -13.4 -13.5 -13.7
25.2 25.2 25.2 25.2
0.074 0.074 0.074 0.074
99.9
-0.3
21.0
-13.8
25.2
0.074
116
I. TR O C H Ę PR OSTEJ F IZ Y K I, PROSTE PR OGR A M OW A N IE
117
Z-I. Kilka testów - przystępujemy do zabawy
Listing Z-I-5a-Fr_e2d_zl5a.dat 5 .0489 9.8739 14 .495
1 .7891 3 .4025 4.8514
49.332 47.200 45.256
1 6.992 15.294 13. 6 9 9
52.2 49.6 47.3
1 9.0 1 8.0 16.8
5 5.709 58. 8 1 3 61. 8 3 5
12. 0 2 8 12. 0 1 2 11.900
31. 4 5 6 30.623 29.832
0.33270 - 0 .64671 - 1 .6009
31.5 3 0.6 29.9
0 .6 -1.2 -3.1
95.170 97.353 99.489
2.5 7 3 7 1 .3115 -0.2076 8 E - 0 1
22.065 21.590 21.127
- 1 2.266 -12.975 - 1 3.669
25.2 25.2 2 5.2
-29.1 -31.0 -32.9
236 4.74 0.4 237 4.7 6 0.4 238 4.7 8 0.4 239 4.8 0 0.4 liczyc dalej? (t/n): t — kr o k ---- 1 --------- m a s a — 240 4. 8 2 0.4 P OZIOM STARTU O S I Ą GNIĘTY 241 4.84 0.4
100.0 100.3 100.5 100.8
101.1 101.3
1.3 0.9 0.5 0.1 ----- y---0.3 -0.7
-19.9 -20.0 -20.1 -20.2
23.8 23.8 23.9 24.0
0 .070 0.070 0 .070 0.071
12.8
v y -----20.4
24.0
--V/M0.071
12.7
-20.5
24.1
0.071
13.0 12.9 12.9 12.8 —
Kopnąć piłkę w taki sposób, żeby nabrała prędkości 55 m/s, na pewno nie jest łatwo. A osiągnięcie takiego samego zasięgu przy kącie 70° jest zupełnie nierealne. Zapytajmy raczej: przy jakiej najmniejszej prędkości i jakim kącie piłka jednak przeleci ok. 100 m? Po paru eksperymentach w takiej 2-wymiarowej przestrzeni parametrów startowych wynik może być następujący: prędkość 47 m/s, kąt 40°; może ktoś znajdzie mniejszą prędkość i inny kąt gwarantujące zasięg 100 m? Listing Z-I-5b p rzyspieszen i e g r a w i t a c y j n e [m/sA 2] (>0; 0 - 9.81): 0 masa początkową, k ońcowa [kg] (=0.0): .0037 opor: - 0.003 7 0 0 * v A 2 metoda: Eulera (e) c zy V e r l e t a (v)?; inaczej - KON I E C ZABAWY: parametr m ie s z a n i a z prz e d z i a ł u 1 - 0 (1: "czysty" E l ub V): krok całkowania h (>0): .02 PODA] WARUNK I POCZĄ T K O W E położenie x, y [ m ] : 0 0 prędkość v [m/s], kat [deg]: 47 40 s kładowe prę d k o ś c i [m/s]; vx: 36.0 0 4 1 vy: 30.2 1 1 0 5 : rzut/rakieta, z o p o r e m lub bez zapis na pli k c o n r e c k r o k o w (nrec>=0, 0 - b e z zapisu): 5 100. % Ver l e t + 0. % tł u m i e n i a — kro k — t ......— m a s a - ...... x -----------y --------v x -------- vy-1.2 35.4 29.3 2.1 1.8 35.2 28.9 2.3 34.9 2.16 2.18
2.20 2.22 2.24
57.2 57.6 58.0 58.4 58.8
28.8 28.8 28.8 28.8 28.8
20.5 20.5 2 0.4 20.3 20.3
-0.2 -0.4
r1/x[m]
Rys. 6. Rzut ukośny z oporem, b = 0.0037: dwa układy danych v, kąt. G e n e r o w a n i e w y k r e s u : plot 'plikjL' u 2:3 title 'v=55,kat=20' w lp lt -1 lw 1 pt 4 ps 2,'plik_2' u 2:3 title 'v=47,kat=40' w lp lt -1 lw 1 pt 12 ps 2
.0 .5 .0 20.5 20.5 2 0.4 20.3 20.3
V/M0. 1 3 5 0.134 0.132
II. Jeszcze trochę prostej fizyki, elementy programowania obiektowego
11.1. O drobina term odynam iki Przyjrzyjmy się trochę dokładniej, co kryje w sobie „efektywny współczynnik oporu aerodynamicznego” b. Można przedstawić go w postaci: (43)
b = y2 * d * S * c x
d (jak density) to gęstość ośrodka, S to „powierzchnia odniesienia” [30], dla ruchu w ga zie jest to najczęściej powierzchnia przekroju ciała płaszczyzną prostopadłą do kierun ku ruchu, co jest intuicyjnie zrozumiałe; ale dla ruchu w cieczy jest to raczej cała po wierzchnia „ocierająca” się o ciecz. cx to bezwymiarowy „współczynnik oporu kształtu” w kierunku ruchu (w aerodynamice lotniczej używa się także współczynników c - dla oporu bocznego, cz - dla ruchu w pionie). W spółczynnik cx jest dobrze znany m iłośni kom samochodów (szczególnie szybkich) - stanowi istotną charakterystykę karoserii sa mochodowych, dla których m a wartość w przedziale 0.2-0.3. W spółczynnik cx dla dane go kształtu ciała i kierunku jego ruchu nie jest stały: jest funkcją tzw. liczby Reynoldsa, bezwymiarowej wielkości wiążącej lepkość ośrodka i prędkość ruchu, a bardziej gene ralnie - charakteryzującej ruch: od laminarnego - mówiąc poglądowo „płynnego”, niewywołującego zawirowań, do turbulentnego - „burzliwego”, wzbudzającego w ośrodku wiry. Obszerny przegląd wartości współczynników c. można znaleźć np. w [30], Tabela 8.1. Wracając do przykładu Z-I.5, czyli naszej piłki w powietrzu: gęstość powietrza d w warunkach normalnych to ok. 1.3 kg/m3, S to powierzchnia koła o średnicy piłki (0.22 m), cx ~ 0.15 [30], co wg wzoru (43) daje wartość b = 0.0037 kg/m, którą użyliśmy w ob liczeniach w Z-I.5. Teraz robi się ciekawiej: gęstość powietrza zależy od wysokości n.p.m. i od temperatury bezwzględnej (T, mierzonej w kelwinach K), która też zależy od wysokości; a wysokość jest istotną w spółrzędną w badanych tu ruchach. Z kolei cx dla większych prędkości za leży bardzo istotnie od prędkości w skali Macha (dlatego na naszych protokołach ekra nowych występuje kolumna v/M), a jednostka Macha (czyli prędkość dźwięku) zależy
120
II. EL EM EN TY PR OGR A M OW A N IA O B IEKTOW EGO
od gęstości ośrodka (im gęstość mniejsza, tym wolniej przekazywane jest w nim zabu rzenie mechaniczne, czyli w szczególności dźwięk), a ta oczywiście zależy od tem pera tury. Widzimy więc, że jeśli w badanym ruchu następuje istotna zmiana wysokości, albo prędkość zbliża się nawet tylko do połowy jednostki Macha, współczynnik b nie może być uważany za stały; to oczywiście nie dotyczy piłki na boisku, ale skoku z 1000 m, a tym bardziej pocisku lub rakiety - oczywiście dotyczy. Spróbujmy te zależności upo rządkować.
121
II.l. Odrobina termodynamiki
(47)
b = Vi * D * p 0 * exp(-e(h) * h)/T(h) * S * c/v J T (h )))
Namnożyło się tu zależności funkcyjnych, bo jak ju ż wspomniano „stała” s ma „prze działową” zależność od wysokości h, a temperatura T jest silnie zależna od h, tym sa m ym i prędkość w skali M acha vM- też zależy od wysokości. Jak te zależności od wy sokości wyglądają? Tabela 1. Ciśnienie jako funkcja wysokości n.p.m. [31]
11.1.1. Równanie Clapeyrona i wzór barometryczny To powinno być znane z fizyki szkolnej: równanie Clapeyrona, sformułowane w roku 1834, wiąże ze sobą ciśnienie, objętość i temperaturę gazu doskonałego w stanie rów nowagi termodynamicznej; tu uwaga: od tej chwili zawsze mamy na myśli temperaturę bezwzględną (T), 0 [K] = -273.15 stopni Celsjusza [C]: (44) p * V = n*R*T p to ciśnienie gazu mierzone w paskalach, 1 Pa = N/m2, V - objętość [m3]; zatem iloczyn p * V ma wym iar pracy/energii: N*m = J (dżul); T to tem peratura [K], n - liczba moli gazu w objętości V,R = 8.3144621 [J/mol*K] jest uniwersalną stałą gazową. Ze szkoły także może zapamiętaliśmy, że ciśnienie p maleje wykładniczo z wysokością h - jest to treść tzw. wzoru barometrycznego: (45) p = p 0 * exp(-E*h) p 0to ciśnienie na poziomie odniesienia (najczęściej - morza: standardowo to 1013.25 hPa; ale to nie jest takie oczywiste: w rejonie Bałtyku, który jest morzem o minimalnych pływach, 0 n.p.m. wyznaczane jest w Kronsztadzie, koło St. Petersburga); exp(-e*h) to funkcja wykładnicza e~ ,!'h, h [m] to wysokość n.p.m., stała e jest w yznaczana z pom ia rów i jest stała tylko w pewnych przedziałach. Pamiętając, że gęstość d to stosunek masy do objętości (wymiar [kg/m3]) i wykorzystu jąc równanie Clapeyrona dla 1 mola gazu, otrzymamy: (46) d = M/R * p 0 * exp(-e*h)/T = D *pg*exp(-e*h)/T M to stała masa 1 mola powietrza, 0.02884 kg (uwaga: powietrze nie jest gazem dosko nałym, mamy tu więc element upraszczający w tym modelu), D jest zatem nową stałą, D = 0.003468654 kg*K/J. Wzór na b (43) przyjmie teraz postać:
wysokość [km]
ciśnienie [hPa]
0 5 10 15 20 25 30 40 50 60 70 80 90 100 200
1013.25 540.48 265.00 121.12 55.29 25.27 11.85 2.99 0.87 0.25 0.06 0.01 0.00135 0.000213 0.00000162
Logarytmując wzór barometryczny (45), otrzymamy liniową zależność logarytmu ci śnienia od wysokości: (48) ln(p(h)) = ln (p j - e*h = P ,+ P * h Biorąc po 2 sąsiadujące punkty z Tabeli 1, możemy dla każdego przedziału h wyznaczyć jednoznacznie stałe Pp P2; tak właśnie postąpimy, budując program. Jak pokazuje Tabela 1, ciśnienie bardzo szybko maleje z wysokością; do zobrazowa nia takiej zależności najlepiej jest wybrać reprezentacje logarytmiczną ciśnienia, jak na Rys. 7. Gdyby w całym przedziale wysokości obowiązywały takie same wartości P,, P2 (48), otrzymalibyśmy linię prostą. Widać jednak, że w różnych przedziałach wysokości param etry te nieco różnią się, więc linia jest nieco łamana.
122
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
II.l. Odrobina termodynamiki
123
Listing P-ph-fun function P_ph_fun(H,H0,P0:T_FL):T_FL; // ciśnienie [hPa] jako funkcja wysoksci H[m], przy zalozeniu ze na // wysokości H0[m] ciśnienie wynosi P0[mbar=hPa], dla modelowej // atmosfery standardowej; // dane dla ciśnienia (ptab [mbar]) w funkcji wysokości (htab [km]) // dla atmosfery standardowej, wg Encyklopedii Fizyki I str. 263 type T_C15=array [1..15] of T_FL; // typ lokalny const htab:T_C15=(0e0,5e0,10e0,15e0,20e0,25e0,30e0,40e0,50e0,60e0, 70e0,80e0,90e0,100e0,200e0);
Mamy tu po raz pierwszy w P użyte tabele stałych: definiowanie ich w sekcji const i metodę ich wypełniania w postaci listy wartości (wart_l, wart_2, ...). const ptab:T_C15=(1013.25e0,540.48e0,265e0,121.12e0,55.29e0,25.27e0, 11.85e0,2.99e0,0.87e0,0.25e0,0.06e0,0.01e0,1.35e-3,2.13e-4,1.62e-6); h [km]
To są te same dane co w Tabeli 1. Rys. 7. Logarytm naturalny ciśnieniap [hPa] jako funkcja wysokości h [km]; por. Tabela 1.
Już na tym etapie uważam za celowe ilustrowanie rozważanych tu zależności term ody namicznych ich implementacjami programowymi: tu - funkcjami pascalowym i (w roz działach C -II i F -II są ich odpowiedniki w C++ i F9x), których użyjemy, budując p o szerzony program w następnym rozdziale. Przypom nę więc nasze zasady typografii: ■ nazwa każdego segmentu programowego w Pascalu zaczyna się od P (lub D; w C++ od C, w F9x od F); ■ nazwy typów danych zawierają w sobie T_ i pisane są wielkimi literami: T_FL to przyjęty typ zmiennopozycyjny; ■ param etry formalne podprogramów pisane są wielkim i literami; ■ pozostałe nazwy zmiennych (prostych i strukturalnych) pisane są małymi literami.
var i:integer; hi:T_FL; eps:T_C15; begin if P0=0e0 then P0:=ptab[l]; eps[l]:=ln(ptab[2]/P0)/(htab[2]-H0); // punkt (H0,P0) jest zadany for i:=l to 14 do eps[i]:=ln(ptab[i+l]/ptab[i])/(htab[i+l]-htab[i]);
Wyznaczone zostały wartości s dla wzorów (45-48) w poszczególnych przedziałach. hi:=H/1000e0; // m => km if (hi=htab[i]) and (hi 1,EM/d2r:7:2); end end; // P_v3_ztv
Każdy algorytm sterowania ma jednak sens tylko pod warunkiem , że istnieje jego wy konawca - pracujący silnik. A ponadto nie jest oczywiste, w którym momencie lotu na leży algorytm ten włączyć; ten czas trzeba w naszym programie określić w danych (pa ram etr tster).
144
II. E L EM EN TY PR OGR A M OW A N IA OBIEK T O W EG O
II.4. Wprowadzenie do rozszerzonych programów
145
11.4.4. Jeśli param etry ruchu zmieniają się gwałtownie: przełączanie logistyczne
W pewnych sytuacjach dla przejścia między dwoma stanami używać jednak będziemy interpolacji liniowej:
Gdy mamy do czynienia z rzutam i czy nawet z rakietami, przebiegi czasowe położenia r(t) i prędkości v(t) są w m iarę „gładkie”, więc stosowane tu integratory Eulera i Verleta zachowują się stabilnie i wiarygodnie, jak pokazują przykłady w rozdziale Z-I. Inaczej jest w wypadku skoku ze spadochronem: w momencie otwierania spadochronu następu je gwałtowna zmiana oporu i wynikająca z niej zmiana prędkości, a tego żaden prosty integrator nie lubi. Zresztąproces otwierania spadochronu, chociaż tylko kilkusekundo wy, nie jest jednak bezczasowy (tak jak wszelkie realne procesy w naturze), nie m oże my zakładać, że odbywa się on w ramach pojedynczego kroku całkowania. Mamy tu do czynienia z sytuacją przełączania pom iędzy dwoma w yraźnie odróżnialnymi stanami; typową funkcją używ aną do opisu takiego przełączenia jest znana ze statystyki krzywa logistyczna, o ogólnej postaci:
Listing P-wa-fun
(67) l(x;a,b,c) = a/(l+b*exp(-c*x)); a, b, c > 0 Dla x => -oo człon wykładniczy w mianowniku zmierza do co, więc l(-) = 0; dla x => oo granicą jest wartość a: jest to więc przełączenie m iędzy stanami 0 i a. Param etr b de cyduje o symetrii przełączenia: dla 6 = 1 przełącznik jest „symetryczny”, jego charak terystyka jest jednakowa „w górę” i „w dół"; c decyduje o „stromości” krzywej, czyli o szybkości przełączenia i zakresie zmienności x, w którym w ystąpią wartości pośred nie między 0 i a. Tego rodzaju funkcje przełączające stosowane są szeroko m iędzy in nymi w modelowaniu sieci neuronowych. Funkcję tak ą zaadoptujemy do opisu szyb kiej, ale jednak „gładkiej” zmiany w spółczynnika oporu cx i powierzchni poprzecznej s z w kilkusekundowym przedziale czasu wokół m omentu otwierania spadochronu t : (68) l ( t ; t / loJ hlgh,c) =fhw + (f,mh- f j / ( l + e x p (-c * (t-tf) ) ; f Ugh > f low > 0, O
0
W przedziale t wokół t , którego szerokość zależy od wartości c, nastąpi przejście od sta nu f lowdo f higk; dla t = t funkcja m a wartość średnią / 2 *(fhw + f hjgJ Listing P-log-fun function P_log_fun(XJX0 iFL,FH,C:T_FL):T_FL; // funkcja logistyczna przelaczajaca miedzy stanami FL, FH w punkcie X0 begin result:=FL+(FH-FL)/(le0+exp(-C*(X-X0))); end; // P_log_fun
function P_wa_fun(H,HLJHHJAVL,AVH:T_FL):T_FL; // liniowa interpolacja w H pomiędzy punktami (HL,AVL) i (HH,AVH) begin result:=AVL+(AVH-AVL)*(H-HL)/(HH-HL); end; // P_wa_fun
II.4.5.
Protokoły ekranowe, pliki z wynikami
Programy, które tu zbudujemy, umożliwiają określenie położenia celu dla pocisku lub rakiety (ale nie dla skoku ze spadochronem), a ponadto cel może poruszać się, jednak tylko poziomo i ze stałą prędkością. Dlatego protokół ekranowy może mieć różną po stać: zawsze listowane są czas; współrzędne E, N, U [m] punktu i składowe jego pręd kości vE, vjip vu [m/s]. Jeśli cel nie jest określony, pokazywane są ponadto: prędkość cał kowita v [m/s], prędkość w skali M acha vM, aktualny współczynnik oporu cx, aktualne opóźnienie (deceleracja) w jednostkach G; pamiętamy oczywiście, że jednostka Macha zmienia się z tem peraturą (czyli z wysokością), cx jest funkcją vw a wartość G zmie nia się z wysokością. Ten sposób prezentacji umożliwia szczegółową bieżącą analizę ruchu; jest także stosowany dla skoków spadochronowych. Jeśli cel został określony, poza współrzędnym i i prędkościami punktu pokazywane są: składowe E, N odległości [m] punktu, oraz azymut (w stopniach [deg], i tysięcznych [tys] - zob. dalej, Z-II.2) od startu do aktualnego położenia celu (ruchomego), lub do położenia punktu (cel nieru chomy); umożliwia to oszacowanie poprawek uwzględniających przyspieszenie Coriolisa i ruch celu. Więcej wyników otrzymujemy w pliku, tworzonym gdy w danych podamy parametr nrec > 0: dane zapisywane będą co nrec kroków. Można oczywiście zapisywać każdy krok (nrec=l), ale dla celów ewentualnej prezentacji graficznej (a po to tworzymy plik) nie jest to wskazane, bo punkty na wykresie będą za gęsto, wykres będzie mało czytel ny; praktyczniej jest zapisać tylko co 10., co 20. krok, a nawet jeszcze rzadziej. Jeżeli grafika jest przeznaczona do druku z opcją (w GNU Plocie) lp (linepoints) do brze jest dokonać oszacowania: punkty wykresu powinny być nie gęściej niż co 2-3 mi limetry: nrec*h to odstęp czasowy sąsiednich punktów. Jeśli oś zmiennej niezależnej wykresu (tzn. oś pozioma, niekoniecznie musi to być czas) będzie miała w druku np. 15 cm, to mamy do dyspozycji 50-75 punktów; stąd łatwo obliczymy, jakie powinno być
146
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
nrec. Jeśli tylko niektóre rejony wykresu są dla nas ważne, celowe jest gęste tablicowa nie (małe nrec), a następnie użycie do wykresu tylko zdefiniowanego przez > set xrange [dolna_granica:gorna_granica]
zakresu zmiennej niezależnej (podobnie ograniczyć możemy zakres wartości wykreśla nych funkcji: > set yrange [ ... ] ), albo sporządzenie kopii oryginalnego pliku, za wierającej tylko interesujący nas przedział danych. Na pliku zapisywane są zawsze w poszczególnych kolumnach: K olum na
(sugestie użycia w GNU Plot, u s using)
1 2 3-5 6
-
7-9
-
10 11
-
12 13 14 15
-
16 17
-
nr. kroku całkowania; czas [s]; współrzędne E, N, U punktu [m]; (np. E(t): u 2:3 itp. d laN , U) rzut toru na płaszczyznę (E,N) dEN[m], czyli odległość punktu m ierzo na w poziomie (np. tor U(dEN)\ u 6:5) składowe E, N, U prędkości punktu [m/s]; (np. vE(t)\ u 2:7, vE(E): u 3:7) prędkość pozioma [m/s], tzn. rzut prędkości na płaszczyznę EN kąt toru w stosunku do poziomu [deg]: dodatni - wznoszenie, ujemny - opadanie; (np. kąt(dEN)\ u 6:11) prędkość całkowita [m/s]; (np. v(t): u 2:12) prędkość w skali Macha [Ma]; (np. vM(U)\ u 5:13) aktualna wartość cx; (np. c/vM )\ u 13:14) deceleracja (tzn. opór ośrodka) [m/s2] (np. deceleracja(U): u 5:15) (na protokole ekranowym mamy decelerację w jednostkach G); tem peratura w skali Celsjusza; (np. temperatura(U): u 5:16) gęstość powietrza [kg/m3] (np. gęstość(U): u 5:17)
Tak zbudowane są pliki o nazwach: (P/D/C/F)v3(e/v).dat, zapisywane gdy cel nie jest określony (e/v w zależności od metody: Eulera lub Verleta); dodatkowo w kol. 18. zapi sywana jest masa punktu [kg] (ma to znaczenie dla rakiety). Jeśli cel (t - target) jest określony, dodatkowo zapisywane są w plikach (P/D/C/F)v3(e/v)t.dat:
18-20 21
-
składowe E, N, U wektora punkt - > cel [m]; odległość punkt - cel [m] (np. odległość P-C(t): u 2:21)
II.4. Wprowadzenie do rozszerzonych programów
147
Jeśli cel jest ruchomy, nazwy plików to (P/D/C/F)v3(e/v)tv.dat (tv - target variable), sens kolumn 18-21 nie zmienia się, otrzymujemy ponadto: 22
-
23-24
-
najmniejszą (czyli prostopadłą) odległość punktu od toru celu [m] (< kol. 21) współrzędne E, N celu [m]; wysokość U celu jest stała
Po tych ogólnych przygotowaniach możemy przejść do budowania kolejnych wersji na szego programu.
P-II. Obiektowo w Pascalu
P-II. O biektow o w P ascalu P-II.1. FPC/Lazarus: program Pv3_prog.lpr Struktura
programów P/D/C/Fv3_prog jest nieco odmienna od programów P/C/Fr_ev_test. Najpierw nomenklatura: nasze wektory działają w standardowej prze
strzeni 3-wymiarowej ENU, ale w wersjach P, C nie są zdefiniowane jako tablice: ich składowe skalarne m ają w programie nazwy: xe, yn, zu (żeby łatwiej skojarzyć ozna czenia współrzędnych E, N, U ze standardowymi x, y, z); w wersji F są to tablice. N azwa każdego elementu wektorowego (obiektu, metody) kończy się na ..._v3. Nazw y wszyst kich segmentów programowych zaczynają się oczywiście od P/D/C/F...; tam, gdzie uży wane będą wektory, a w ynik będzie skalarem - od P/D/C/Fv3_...; jeśli w ynikiem działa nia funkcji jest wektor - od P/D/C/F_v3_...; jeśli wykorzystywane są tylko składowe E, N - od P/D/C/Fv2_... Jeszcze raz przypomnijmy, że wielkimi literami pisane są nazwy typów (klasy to też typy - klasowe) i parametrów formalnych, pozostałe nazwy pisane są małymi literami: kompilatory P i F tego nie odróżniają, w C ma to znaczenie! A więc - do dzieła! Jeśli pracujemy na kompilatorze FPC pod systemem UNIX, sprawa jest prosta: uruchamiamy nasz ulubiony edytor znakowy, otwieramy nowy plik o nazwie np. Pv3_prog.pas i zaczynamy po prostu pisać. Kompilacja i konsolidacja opisana jest na końcu pn. P-I.6. Ale z Lazarusem już tak prosto nie jest, to systemem bardziej roz budowany niż Delphi, oferujący wiele opcji. Do naszych celów najlepiej wybrać opcje tworzenia programu wg ciągu zakładek: Plik | nowy | Projekt | Program
Lepiej nie wybierać zakładki Console Application, bo otrzymamy od razu dość roz budowaną hierarchię klas, zupełnie nam niepotrzebną. Otrzymamy standardowy sza blon postaci:
149
{$R *.res} begin end.
Komendy preprocesora {$IFDEF ... $ENDIF) można usunąć, podobnie biblioteki cthre ads i classes. Osoby, które m ają dostęp do Delphi, mogą jednak rozpocząć znacznie prościej: ► utworzyć w Delphi standardowy szablon aplikacji konsolowej, idąc przez ciąg za kładek: Plik | nowy | inny | aplikacja konsolowa
Otrzymany w w yniku plik m ożna nazwać np. szab lo n _ d e lp h i.d p r
program szablon_delphi; {$APPTYPE CONSOLE} uses Syslltils; begin { TODO -oUser -cConsole Main : Insert code here } end.
► otwieramy Lazarusa i wybieramy opcje: Narzędzia | konwertuj projekt Delphi do projektu Lazarusa ...
program Projectl; ($mode objfpc}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Classes { you can add units after this };
► wskazujem y jako plik do konwersji szablon_delphi.dpr, wybieramy sugero wane opcje automatyczne i otrzym ujem y po konwersji plik, który nazwiemy np. szablon_d21.1pr:
program szablon_d21j {$IFDEF FPC}
150
II. EL EM EN TY PR OGR A M OW A N IA O B IEK T O W EG O
{$M0DE Delphi} {$ENDIF}
P-II. Obiektowo w Pascalu
[private] nazwa_llJnazwa_12J...:typ_l; nazwa_21, [public] nazwa_22...:typ_2; ...
{$APPTYPE CONSOLE}
specyfikacje dokładnie takie same jak w definicjach typu rekordowego (record):
uses SysUtils;
wypisuje się tu konstruktor - koniecznie
begin { TODO -oUser -cConsole Main : Insert code here } end.
Podobnie jak poprzednio komendy preprocesora {$IFDEF ... $ENDIF} można usunąć. Te raz najlepiej od razu zapisać nasz program jako Pv3_prog i zacząć pisanie, oczywiście od zdefiniowania typów. W prologu określamy więc, że program jest aplikacją konsolo w ą (dyrektywa ($APPTYPE CONSOLE}, włączamy (uses) niezbędne biblioteki: SysUtils włączana jest automatycznie; ze względu na rozszerzony repertuar potrzebnych funkcji trygonometrycznych musimy włączyć też bibliotekę Math (zob. [5]). Definiujemy stałe globalne (const ...), obowiązujące w całym programie, i nadajemy im wartości; definiu jem y też (type) T_FL jako wybrany typ zmiennopozycyjny double. Listing Pv3-prolog
151
// pola metod: constructor nazwa_con
destruktor - niekoniecznie destructor nazwa_des
oraz pełne nagłówki wszystkich funkcji i procedur opisujących metody postępowa nia z danymi; w pewnych sytuacjach (omówionych dalej) m uszą one być określone jako metody „klasowe” (atrybut class). Zmienne, będące polami danych, są w tych meto dach lokalne - automatycznie dostępne, bez kwalifikowania odwołań. Wszystkie me tody m ogą być też zależne od parametrów formalnych: w nagłówkach trzeba wypisać wówczas pełną ich specyfikację, pola danych oczywiście nie m ogą być parametrami formalnymi: [class] function fun([lista_parametrow_formalnych]):typ_fun; [class] [private] procedure proc[(lista parametrów formalnych)];
program Pv3_prog;
end; // nazwa_klasy
{$APPTYPE CONSOLE}
Wszystkie elementy klasy są w Pascalu domyślnie publiczne (public), ale wpisując klauzulę private na początku definicji klasy, wszystkie elementy uczynimy prywat nymi; możemy też charakter prywatny nadać wybranym elementom danych lub metod.
uses SysUtils,Math; const d2r=pi/180e0; t2r=pi/3200e0; t2d=9e0/160e0; kh2ms=10e0/36e0; type T_FL=double;
Definicja klasy następuje w sekcji definicji typów (po słowie kluczowym type) i ma ogólną strukturę:
W sekcji definicji typów trzeba opisać wszystkie klasy występujące w programie; do piero po tym dokonuje się implementacji metod - podaje się ciała odpowiednich pod programów, również ewentualne przeciążenia operatorów wprowadza się poza definicją klas, w części implementacyjnej. Implementowana metoda musi zostać „zakwalifiko wana”, w postaci np. [class] function nazwa_klasy.fun(...):typ_fun ... // ciało funkcji end; // nazwa_klasy.fun
nazwa_klasy=class // pola danych:
Spotykamy się tu po raz pierwszy na poziomie programowania z konstruktorem: pro cedurą systemową constructor, której wywołanie powoduje utworzenie obiektu (na
152
II. EL EM EN TY PROGR A M OW A N IA O BIEK TO W EG O
ogół w „pamięci program isty”, na stercie (heap)) i nadanie wartości jego polom danych; likwidacja obiektu następuje przez wywołanie destruktora - procedury systemowej destructor. W DP i LP na poziomie aplikacji konsolowych konstruktory nie m ają nazw predefiniowanych (inaczej jest w C++ i F9x). W aplikacjach graficznych w Delphi i Lazarusie otrzym ują one jednak automatycznie nazwy: konstruktor - create, destruktor - destroy.
W sekcji deklaracji zmiennych - po słowie kluczowym var - deklarujemy także „zmien ne klasowe”, czyli obiekty, w standardowej formie: var
P-II. Obiektowo w Pascalu
153
się. Jeśli obiekt tworzony jest jednokrotnie w przebiegu programu, kasowanie go przez wywołanie destruktora (obiekt_ll.destroy) nie jest konieczne, bo porządki z pamię cią zrobi system operacyjny po zakończeniu programu. Jeśli jednak mamy obiekt we wnętrzny w procedurze wywoływanej wielokrotnie, każde aktywowanie tej procedury powoduje kreowanie takiego obiektu; jeśli tworzymy go przez wywołanie konstruktora (tak zawsze będziemy robić), przed wyjściem z procedury destruktor wywoływany jest automatycznie i pamięć zostaje zwolniona. Dlatego w budowanych tu programach nie będziemy w ogóle definiowali ani używali destruktorów. M ożna jednak utworzyć obiekt „ręcznie” przez użycie systemowego operatora new; wówczas należy go także „ręcznie” skasować, używając systemowego operatora dispose; jeśli tego zaniedbamy, może nam na jakim ś etapie obliczeń zabraknąć pamięci —wystąpi tzw. wyciek pamięci.
zmienna_llJzmienna_12,...:typ_l; // "zwykleMzmienne obiekt_lljObiekt_12j...:nazwa_klasy_l; // obiekty - "zmienne klasowe"
W części operacyjnej programu przed pierwszym użyciem obiektu wywołujemy na jego rzecz konstruktor:
obiekt_ll:=nazwa_klasy_l.create( ... )j
P-II.1.1. Działania na wektorach w przestrzeni trójwymiarowej, klasa P_T_V3 W tym punkcie opiszemy wszystkie (prawie) działania na wektorach w przestrzeni 3D, zarówno te, które są m etodami definiowanej tu klasy P_T_V3, jak i przeciążenia operato rów i inne procedury, które operują na wektorach, ale nie są metodami tej klasy. Jako operatory przeciążone (overloaded) zdefiniujemy następujące działania 2-operandowych operatorów arytmetycznych:
obiekt_12:=nazwa_klasy_l.create( ... ); + :dodawanie wektorów, l_v3 + p_v3; 1, p - lewy i prawy operand;
Dopiero teraz obiekty zostały utworzone, a ich polom danych nadane zostały wartości, np. przekazane przez param etry aktualne konstruktora. Tak więc konstruktory musi my w ramach klas definiować (i będziemy je tu nazywać create, choć m ożna też w y brać inne nazwy) i kreować obiekty przez ich wywoływanie. Wywołanie takie może jednak być niejawne, jak np. w konstrukcji:
- :
1. odejmowanie wektorów, l_v3 - p_v3; jeśli oba wektory przesuniemy do wspólnego początku, to jest to wektor od końca p_v3 do końca l_v3 : l_v3 - (l_v3 - p_v3) = p_v3;
2. iloczyn skalarny w przestrzeni 2D (dla składowych E, N)
obiekt_ll:=nazwa_klasy_l.funkcja( ... );
pod warunkiem, że funkcja zwraca wartość typu nazwa_klasy_l, tworzoną w jej treści przez wywołanie konstruktora: nazwa_klasy_l.create( ... ).
UWAGA: w FPC/LP możliwe jest kilkakrotne przeciążenie tego samego operatora, jednak typ operandu lub w yniku musi być za każdym razem inny.
Trochę inaczej jest z destruktorami: w LP i DP mamy predefiniowany destruktor de stroy, którego nie trzeba (a nawet nie należy) w klasie deklarować. Jeśli chcemy, żeby wraz ze skasowaniem obiektu destruktor jeszcze coś wykonał (np. wypisał komunikat, zapisał coś na pliku itp.), należy go zdefiniować, ale dobrze jest nazwać go jakoś inaczej, np. destruct, chyba że standardowej nazwie destroy chcielibyśmy nadać niestandardo we znaczenie - czyli przeciążyć metodę: dotychczas mówiliśmy o przeciążaniu opera torów, dalej w tej książce w kierunku tzw. metod w irtualnych nie chcę jednak posuwać
*:
1. mnożenie wektora przez liczbę: l_v3 * p (skalar), kolejność wektor * skalar jest tu istotna; 2. standardowy iloczyn skalarny {dotproduct) w 3D: l_v3 • p_v3
/ : iloczyn wektorowy {cross product), l_v3 X p_v3 (por. II.3)
154
II. EL EM EN TY PR OGR A M OW A N IA OB IEK T O W EG O
Jeśli w jakim ś wyrażeniu operatory te w ystąpią jako przeciążone, będą m iały po obu stronach spację (to oczywiście nie jest obowiązkowe, moja prywatna konwencja). Do klasy P_T_V3 włączone zostały też funkcje zależne od pojedynczego wektora; zależ ność ta jest widoczna bezpośrednio, ponieważ wektor ten jest parametrem formalnym takiej metody. Funkcji takich nie będziemy wyrażać jako przeciążenie jakiegoś operato ra, chociaż jest to także możliwe. Dodajmy, że operator przypisania: obiekt_a := obiekt_b jest domyślnie przeciążony dla obiektów tej samej klasy. Do tej części programu włączyliśmy ponadto, ze względu na bardziej logiczną strukturyzację kodu, także wszystkie inne funkcje, w których wykorzystywane są wektory i operatory przeciążone (poza funkcją obliczającą wektor przyspieszenia); ich działanie będzie skomentowane bezpośrednio w listingu. Listing P-T-V3-class P_T_V3=class // dane: xe,yn,zu:T_FL; // = v3 // metody: constructor create(XjYjZ:T_FL); class function Pv3_len(V3:P_T_V3):T_FL; // dlugosc v3 class function Pv2_len(var V3:P_T_V3):T_FL; // dlugosc rzutu v3 na płaszczyznę (EN) class function P_v3_ver(var V3:P_T_V3):P_T_V3; // v3 -> wersor v3 class function Pv2_azd(var V3:P_T_V3):T_FL; // azymut [deg] wektora v3 (od N w kierunku E) class procedurę Pv3_aed(var V3:P_T_V3;var A Z ,EL :T_FL); // azymut (AZ) i elewacja (EL) v3 [deg] end; // P_T_V3 class
UWAGA: w tym miejscu, tzn. w sekcji definicji typów (po dyrektywie type), należy zdefiniować także wszystkie inne klasy występujące w programie. Im plementacje metod każdej z klas następują po zakończeniu tych definicji. Tutaj implementacje metod klasy P_T_V3, a także innych procedur, nie będących meto dami tej klasy, ale korzystających z obiektów wektorowych i przeciążonych ope ratorów, zostały dołączone ze względu na m erytoryczną zwartość tekstu. constructor P_T_V3.create(X,Y,Z:T_FL); // konstruktor
P-II. Obiektowo w Pascalu
155
Parametry formalne tego konstruktora (X, Y, Z) służą do nadania wartości polom danych obiektu (xe, yn, zu), parametrami aktualnymi często będą wyrażenia a więc parametry te m uszą być wywoływane przez wartość, nie przez referencję. begin xe:=X; yn:=Y; zu:=Z end;
Implementacja konstruktora create w klasie P_T_V3. Nazwa create to zwy czajowa, ale nieobligatoryjna nazwa konstruktora w Pascalu; będziemy trzymali się tej konwencji, a to, który konstruktor create jest użyty w danym momen cie, kompilator sam rozpozna z kontekstu: na rzecz jakiego obiektu konstruktor jest wywoływany. Inaczej jest w C i F: tam domyślną nazwą konstruktora jest po prostu nazwa klasy. Natomiast jakiegoś własnego destruktora nie będziemy de finiowali; jeśli zajdzie potrzeba programowego skasowania obiektu, użyć można systemowego destruktora destroy. // przeciążenia operatorowi operator + (L_V3:P_T_V3;R_V3:P_T_V3) P_v3_ab:P_T_V3; // suma: L + R begin result:= P_T_V3.create(L_V3.xe+R_V3.xe,L_V3.yn+R_V3.yn,L_V3.zu+R_V3.zu) end;
Przy implementowaniu przeciążenia operatora 2-argumentowego w FPC lewy i prawy operand muszą być niezależnie opisane jako nazwa:typ; wynik działania operatora też trzeba jakoś nazwać (tu: P_v3_ab) i określić jego typ, mimo iż nazwa ta w istocie nigdzie nie musi wystąpić; tu wybrano nazwy analogiczne do nazw w pozostałych wersjach programu. Wynik działania procedury uzyskany jest przez bezpośrednie wykorzystanie konstruktora; wynik ten można zapisać na wybranej nazwie, ale lepiej jest użyć do tego celu zmienną systemową result, która za wsze przechowuje taki wynik (podobnie jest w F9x, w C++ odpowiednikiem jest return). Instrukcja, w której użyty jest konstruktor (obiekt:=klasa.create(...)) sugeruje, że konstruktor jest funkcją; są jednak sytuacje, w których aktywacja konstruktora ma formę instrukcji procedury (obiekt.create(...)). W sensie for malnym konstruktor w P jest więc zarówno funkcją jak i procedurą. operator - (E_V3:P_T_V3;B_V3:P_T_V3) P_v3_b2e:P_T_V3; // różnica: E - B tzn. wektor B -> E begin
156
II. EL EM EN TY PR O GRAM OW ANIA OBIEK T O W EG O
result:= P_T_V3.c reate(E_V3.xe-B_V3.xe,E_V3.yn -B_V3.yn,E_V3.zu-B_V3.zu); end; // UWAGA: drugie przeciążenie operatora operator - (L_V3:P_T_V3;R_V3:P_T_V3) Pv2_dot:T_FL; // iloczyn skalarny dla składowych E, N (2D): L dot R begin result:=L_V3.xe*R_V3.xe + L_V3.yn*R_V3.yn; end;
W FPC ten sam operator można przeciążyć kilkakrotnie (tej możliwości nie ma w C i F): tu operator - (minus) oznacza odejmowanie operandów wektorowych, jeśli wynikiem jest wektor, lub iloczyn skalamy (także operandów wektorowych!) na płaszczyźnie EN, jeśli wynikiem jest skalar. Operator * (mnożenie) działający między wektorami oznacza iloczyn skalamy (wynikiem jest skalar), działający między wektorem i skalarem - wektor, iloczyn wektora przez skalar. operator * (L_V3:P_T_V3;R:T_FL) P_v3_va:P_T_V3; // L(wektor) * R(skalar) begin result:=P_T_V3.create(L_V3.xe*R,L_V3.yn*R,L_V3.zu*R); end; // UWAGA: drugie przeciążenie operatora * operator * (L_V3:P_T_V3;R_V3:P_T_V3) Pv3_dot:T_FL; // iloczyn skalarny L dot R begin result:=L_V3.xe*R_V3.xe + L_V3.yn*R_V3.yn + L_V3.zu*R_V3.zu; end;
operator / (L_V3:P_T_V3;R_V3:P_T_V3) P_v3_axb:P_T_V3; // iloczyn wektorowy: L cross R begin result:=P_T_V3.create(L_V3.yn*R_V3.zu-L_V3.zu*R_V3.yn , L_V3.zu*R_V3.xe-L_V3.xe*R_V3.zu, L_V3.xe*R_V3.yn -L_V3.yn*R_V3.xe); end; // funkcje pojedynczego wektora - metody klasowe:
P-II. Obiektowo w Pascalu
157
Ze względu na sposób, w jaki metody te będą użyte w programie, m uszą one zo stać zdefiniowane jako „metody klasowe” (class f unc/proc ...) [3], z parame trem formalnym należącym do klasy P_T_V3. class function P_T_V3.Pv3_len(V3:P_T_V3):T_FL; // dlugosc v3 begin result:=sqrt(sqr(V3.xe)+sqr(V3.yn)+sqr(V3.zu)); end; // Pv3_len
class function P_T_V3.Pv2_len(var V 3 :P_T_V3):T_FL; // dlugosc rzutu v3 na plaszczyzne (EN) begin result:=sqrt(sqr(V3.xe)+sqr(V3.yn)); end; // Pv2_len class function P_T_V3.P_v3_ver(var V3:P_T_V3):P_T_V3; // v3 -> wersor v3 const eps=le-5; var d :T_FL; p_v3:P_T_V3; begin d :=P_T_V3.Pv3_len(V3); if deeps then d:=eps; with V3 do p_v3:=P_T_V3.create(xe/d,yn/d,zu/d); result:=p_v3 end; // P_v3_ver
class function P_T_V3.Pv2_azd(var V3:P_T_V3):T_FL; // azymut [deg] wektora v3 (od N w kierunku E) const eps=le-8; var p :T_FL; l_x,l_y:boolean; begin with V3 do begin l_x:=abs(xe)0e0 then p:=0e0 else p:=180e0 else begin p:=arctan(xe/yn)/d2r; if xe*yn0e0 then begin s:=sqrt(s); s:=2e0*s/pp; end else s:=-le0; // to w zasadzie nie ma prawa wydarzyć się! result:=s; end; // Pv3_dbe
{$1 P_v3_ztv.pas>
Listing funkcji P_v3_ztv został już przedstawiony wyżej, w pn. II.4.3. (wzory 63-66), i nie będzie tu powtarzany. Dla wskazania, że odpowiedni tekst źródło wy powinien być w tym miejscu włączony, użyta została dyrektywa kompilatora {$1 P_v3_ztv. pas}; tak będzie i w kilku innych miejscach programu. $ I ja k include (włącz) oznacza, że w trakcie kompilacji (lub przed jej rozpoczęciem, jeśli standardem systemu jest preprocesing - tak jest w C) wskazany plik ma być włączony do kompilowanego tekstu; dyrektywy typ include istnieją w różnych formach we wszystkich profesjonalnych kompilatorach. Poniżej, w formie ko mentarza, przypomniany jest tylko nagłówek tej funkcji. //function P_v3_ztv(var RV_V3jRT_V3:P_T_V3;EM:T_FL):P_T_V3; // EM: eps_max [rad] // RV_V3: prędkość rakiety // RT_V3: wektor rakieta -> cel // wartoscia funkcji jest wersor przyspieszenia rakiety
var Sjpbjpejpp:T_FL; // integratory Eulera i Verleta w jeżyku wektorow // i przeciążonych operatorow:
160
II. EL EM EN TY PROGR A M OW A N IA O BIEK TO W EG O
procedurę P_v3_euler (var RN_V3,VN_V3,R0_V3,V0_V3,AC_V3:P_T_V3;H,BETA_EV:T_FL); // RN_V3,VN_V3: nowa pozycja RN i prędkość VN // R0_V3,VO_V3,AC_V3: stara pozycja RO i prędkość V0; przyspieszenie AC begin VN_V3 := VO_V3 + (AC_V3 * H); RN_V3 := (((V0_V3 * BETA_EV) + (VN_V3 * (le0-BETA_EV))) * H) + (RO_V3 + (AC_V3 * (0.5e0*H*H))); end; // P_v3_euler
procedurę P_v3_verlet (var RN_V3,VO_V3,VN_V3,RO_V3,R00_V3,AC_V3:P_T_V3;H,BETA_EV:T_FL); // nowa pozycja RN, stara i nowa prędkość VO, VN // pozycje: stara RO, starsza R00; przyspieszenie AC begin RN_V3 := (RO_V3 * (le0+BETA_EV)) + (ROO_V3 * (-BETA_EV)) + (AC_V3 * (H*H)); V0_V3 := (RN_V3 + (ROO_V3 * (-le0))) * (0.5e0/H); VN_V3 := V0_V3 + (AC_V3 * H); end; // P_v3_verlet
P-II.1.2. Klasa parametrów stałych P
T
CPAR i jej konstruktor
Liczne zmienne, charakteryzujące konkretny w ariant naszych „zabaw”, podzielone zo stały na param etry stale, których wartości określane są przy rozpoczynaniu obliczeń i w zasadzie potem już nie zmieniane (poza kilkom a wyjątkami), i param etry zmienne, których wartości zmieniane są w każdym kroku procesu całkowania równań Newtona i mogą być śledzone przez protokoły (ekranowy i plikowy) tego procesu. Parametry stałe zgromadzone zostały w klasach P/D/C/F_T_CPAR (C - constant). Na zwy zmiennych zostały częściowo przeniesione z programów P/C/Fr_ev_test. Nazwy zmiennych opisujących kąty zakończone są: ...djeśli przechowują wartości w stopniach [deg]; ich odpowiedniki bez ...d (np. azd, az) przechowują wartości w radianach [rad], używane bezpośrednio w wywołaniach funkcji trygonometrycznych. Nazwy zaczyna jące się od t...odnoszą się do punktu docelowego ruchu (target ). Dla konstruktora przyj mujemy standardową nazwę create; jego budowa tym razem nie będzie tak prosta jak w wypadku konstruktora 3-parametrowego dla klasy P_T_V3. Przeciwnie - konstruktor P_T_CPAR.create() jest bardzo rozbudowaną procedurą, w której wartości niektórych parametrów są wczytywane z klawiatury, innych obliczane, występuje też dużo wza
P-II. Obiektowo w Pascalu
161
jem nych powiązań parametrów. Korzysta on z funkcji P_g_f un - obliczającej przyspie szenie ziemskie wg w zoru (56), por. Listing P-g-fun (II.3). Konstruktor P_T_CPAR.create() jest dość obszerny, więc poszczególne jego bloki funkcyjne będą skomentowane bezpośrednio w jego treści. Przypomnijmy jeszcze, że wszystkie elementy klasy w P są domyślnie publiczne (w przeciwieństwie do C, gdzie są domyślnie prywatne). Listing P-T-CPAR-class P_T_CPAR=class // dane - parametry stale: l_par:boolean; // true: spadochron l_rak:boolean; // true: rakieta g:T_FL; // przyspieszenie grawitacyjne [m/sA2] g_v3:P_T_V3; // wektor przyspieszenia grawitacyjnego l_graw:boolean; // true: g jako funkcja fid,fi,sh,fh,dh,hh:T_FL; // szerokosc geograficzna[deg,rad], poziom początkowy i koncowy[m] omega:T_FL; // okres obrotu [h] => prędkość katowa [rad/s] o_v3:P_T_V3; // wektor prędkości kątowej Ziemi/planety l_cor¡boolean; // true: przyspieszenie Coriolisa włączone sm,fm:T_FL; // masa [kg]: startowa, finalna w:T_FL; // prędkość gazów napędowych rakiety [m/s] c:T_FL; // prędkość spalania paliwa rakiety [kg/s] f_rak:T_FL; // = w*c [N]: siła ciągu silnika rakiety cx0,syz:T_FL; // współczynnik oporu kształtu, powierzchnia przekroju ciała [mA2] cxp,spar,tpar:T_FL; // spadochron: współczynnik oporu, powierzchnia, czas otwarcia tc,hpa:T_FL; // temperatura [Cel] i ciśnienie [hPa] na poziomie dolnym rs_v3:P_T_V3; // wektor położenia startowego v0,az,azd,el,eld:T_FL; // prędkość startowa [m/s], jej azymut i elewacja [rad,deg] vs_v3:P_T_V3; // wektor prędkości startowej ts_v3:P_T_V3; // wektor startowego położenia celu td0,ta0,ta0d,tel0,tel0d:T_FL; // [m,rad,deg] odległość, azymut, elewacja celu l_tar¡boolean; // true: cel określony tv0,tav0,tav0d:T_F L; // stała predkosc[m/s] celu; kierunek ruchu celu (poziomy) tv_v3:P_T_V3; // wektor (stałej) prędkości celu l_tv:boolean; // true: cel ruchomy ep_mx,ep_mxd:T_FL; // maksymalny kat sterowania [rad,deg]
162
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
tster:T_FL; // czas wlaczenia sterowania rakiety; 9.8065): '); readln(g)
164
II. EL EM EN TY PR OGR A M OW A N IA O B IEKTOW EGO
end; if g0:[m];=0.0;=360e0 then tav0d:=tav0d-360e0; tav0:=tav0d*d2r; // [deg] => [rad] I_tv:=tv0>0e0; // cel: pozioma prędkość tv0, kierunek tav0d tv_v3 := P_T_V3.create(tv0*sin(tav0),tv0*cos(tav0),0e0); writeln('składowe ENU prędkości celu[m/s]: ',tv_v3.xe:10:3, tv_v3.yn:10:3,tv_v3.z u :10:3); end end; // start: prędkość., azymut, elewacja: repeat writelnCstart: predkosc(>0:[m/s];=0; 1 .44 ): ');
P-II. Obiektowo w Pascalu
167
read(syz,cx0,cxp); write('spadochron: srednica[m], czas otwarcia[s]: '); read(spar,tpar); // czas otwarcia spadochronu until (syz>0e0) and (cx0>0e0) and (cxp>=0e0) and (spar>0e0); if cxp 500 m powyżej startu else hh:=5000e0; // poziom górny 5 km (arbitralnie) repeat write('kaliber[mm](>0) lub przekrój[mA2]( ',
arctan(g/sqrt(p*p+g*g))/d2r:6 :1); end until p>g; if l_tar then begin repeat writeln( czas wlaczenia sterowania[s] (0): ') ; read(tster,ep_mxd); if (tster>0e0) and (ep_mxd>10e0) then begin writeln( kat > 10 moze zdestabilizować lot: potwierdź lub zmniejsz '); readln(epjnxd); end; until (tster0e0); ep_mx:=ep_mxd*d2r; // [deg] => [rad] l_ster:=false; // przy starcie sterowanie wyłączone end end; // warunki meteo na starcie (dla spadochronu przy ładowaniu): writeln('warunki meteo na wysokosci[m]: ’,dh:8:l); write('temperatura[Cels](0 => 20 C) i cisnienie[hPa](0 => 1000): readln(tCjhpa); if tc=0e0 then tc:=20e0; tc:=tc+273.15e0; // temperatura [K] if hpa=0, 0-bez zapisu): '); readln(nrec); // zapis co nrec krokow until nrec>=0; if nrec>0 then begin if l_tv then // cel ruchomy if l_ev then AssignFile(nfl,'Pv3etv.dat') else AssignFile(nflj'Pv3vtv.dat') else if l_tar then // cel nieruchomy if l_ev then AssignFile(nflj'Pv3et.dat') else AssignFile(nflj'Pv3vt.dat') else // cel nieokreślony if l_ev then AssignFile(nfl,'Pv3e.dat') else AssignFile(nfl,'Pv3v.dat'); rewrite(nfl) // otwarcie pliku do zapisu end; end; // P_T_CPAR.create
P-1L1.3. Klasa parametrów z m ie n n y c h
P_T_VPAR
Parametry zmienne zgromadzone zostały w klasach P/D/C/F_T_VPAR (V - variable). Na zwy zmiennych zostały częściowo przeniesione z programów P/C/Fr_ev_test. Nazw y zaczynające się od t... dotyczą punktu docelowego ruchu {target). Obiekty wektorowe o nazwach zaczynających się od z... są wersorami (zob. 11.4.3). Konstruktorem klasy jest tu P_T_VPAR.create(var CP:P_T_CPAR); tym razem nie jest on bezparametrowy, za leży od param etru formalnego typu P_T_CPAR. Jest to konieczne, ponieważ wartości p o czątkowe parametrów zmiennych m usząbyć zainicjowane przez wartości odpowiednich parametrów stałych. Listing P-T-VPAR-class P_T_VPAR=class // dane - parametry zmienne: am:T_FL; // am := sm - c*t: aktualna masa l_pop:boolean; // true: spadochron otwarty l_bal:boolean; // true: paliwo rakiety wypalonej lot balistyczny hc:T_FL; // [m] aktualna wysokosc tk:T_FL; // [K] aktualna temperatura VjVm:T_FL; // aktualna prędkość [m/Sj Mach (Ma)]
P-II. Obiektowo w Pascalu
171
zv_v3:P_T_V3; // aktualny wersor prędkości td:T_FL; // aktualna odległość do celu td_v3:P_T_V3; // aktualny wektor rakieta -> cel tsv_v3:P_T_V3; // aktualny wektor start -> cel cxjbjbef:T_FL; // aktualne parametry oporu sp,tp:T_FL; // aktualne parametry spadochronu gef:T_FL; // aktualne przyspieszenie grawitacyjne // metody: constructor create(var CP:P_T_CPAR); // konstruktor end; // P_T_VPAR class
Podobnie jak dla klas P_T_V3j P_T_CPAR poniższa implementacja konstruktora P_T_VPAR.create() musi nastąpić po zakończeniu sekcji definicji typów i po zaimplementowaniu metod klasy P_T_V3: procedur działania na wektorach i defi nicji operatorów przeciążonych. constructor P_T_VPAR.create(var CP:P_T_CPAR); // konstruktor begin zv_v3 := P_T_V3.create(0.0j0.0j0.0); tsv_v3 := P_T_V3.create(0.0j0.0j0.0); td_v3 := P_T_V3.create(0.0j0.0j0.0); with CP do begin if l_tar then begin tsv_v3 := ts_v3; td:=td0; td_v3 := ts_v3; end; l_pop:=false; // start: spadochron zamknięty l_bal:=false; // start: silnik rakiety włączony sp:=syz; tp:=0e0; am:=sm; gef:=g; end end; // P_T_VPAR.create
P-II.1.4. Budowa wektora przyspieszenia Kluczowe znaczenie dla realizacji naszych obliczeń ma wyznaczanie, w każdej badanej chwili czasu T, wektora przyspieszenia, który zostanie przekazany jako parametr ak tualny w miejsce param etru formalnego AC_V3 do procedur Eulera P/D/C/F_v3_euler
172
II. E L EM EN TY P R OGR A M OW A N IA OB IEK T O W EG O
lub Verleta P/D/C/F_v3_verlet. W implementacjach tych metod w poprzednich wersjach programów (P/C/F_e_step, P/C/F_v_step) przyspieszenia obliczane były wewnątrz tych procedur, a czas (konieczny do obliczania zmienności masy (24)) był elementem struk tur przekazujących param etry danego przypadku. Budowany tu wektor jest wartością funkcji P/D/C/F_v3_acc, w której realizowane są obliczenia wg wzorów (25, 29, 47, 50, 51, 55,57,62, 65,66, 68) - sporo tego, ale jak na tak ą liczbę zaprogramowanych formuł, funkcja jest raczej zwarta, właśnie dzięki intensywnemu wykorzystywaniu wektorów i przeciążonych operatorów. Dla zbudowania wektora przyspieszenia dla danej chwili czasu T musimy znać, poza pa rametrami stałymi CP i zmiennymi VP, aktualne (czyli „stare”) wektory: położenia RO, prędkości V0, a jeśli cel został określony, jest ruchomy i użyty zostanie algorytm napro wadzania 11.4.3 - także położenie celu TP. Niektóre param etry stałe w obiekcie CP m ogą ulec zmianie - np. z powodu wypalenia paliwa rakiety lub otwarcia spadochronu; para metry zmienne VP zm ieniają się z czasem z założenia. Dlatego oba te param etry obiek towe wywoływane są przez referencję (var). Listing P-v3-acc
Najpóźniej w tym miejscu musimy włączyć do programu wszystkie funkcje ska larne; ich listingi zostały już przedstawione w punktach IL1.1-II.4.4 i nie będą tu powtarzane. ($1 P_cx_fun.pas} {$1 P_ph_fun.pas} {$1 P_th_fun.pas}
P-II. Obiektowo w Pascalu
173
begin a_v3 := P_T_V3.create(0.0,0.0,0.0); za_v3 := P_T_V3.create(0.0,0.0,0.0);
Lokalne obiekty a_v3, za_v3 zostały tu wykreowane; będą one kreowane przy każdym wywołaniu tej funkcji, tzn. w każdym kroku całkowania równań Newto na. W LP są one automatycznie kasowane przy wyjściu z funkcji, dlatego nie ma potrzeby jawnego wywoływania systemowego destruktora na końcu. with CP,VP do begin hc:=R0_V3.zu; // [m] aktualna wysokosc a_v3 := g_v3; if l_graw then gef:=P_g_fun(fi,hc) // gef jako funkcja fi, hc else gef:=g; // stale g a_v3.zu:=-gef; // aU v :=P_T_V3.Pv3_len(VO_V3); // aktualna prędkość if (v cel [m] const d_gas=0.003468654e0;//[kg*K/3] D = M(asa molowa)/R(stala gazowa) var a_v3,za_v3:P_T_V3;
if l_par then // dla skoku/zrzutu spadochronowego: begin if (T>=tpar) and (not l_pop) then begin l_pop:=true; tp:=tpar+2e0; writelnCOTWIERANIE SPADOCHRONU, wysokosc[m]: ’,hc:8:l) end; if (not l_pop) and (hc [Pa]=[N/m] b :=0.5e0*bef*sp*cx*sqr(v)/am; // całkowity opor a_v3 := a_v3 - (zv_v3 * b); // deceleracja; operatory przeciążone if l_cor then a_v3 := a_v3 + ((o_v3 / V0_V3) * (-2e0)); // efekt przyspieszenia Coriolisa; operatory przeciążone end; // with ... result:=a_v3; end; // P_v3_acc
P-II. Obiektowo w Pascalu
175
P-II.1.5. Program zarządzający Listing Pv3-driver // Pv3_driver var lin,k,j:integer; t,tpt,v_w,a_w,a_wd,tdold,v_en,r_en,p,q:T_FL; w_v3,tso_v3,vef_v3:P_T_V3; l_f1:boolean=true; z:char; ro_v3,rn_v3,vo_v3,vn_v3,roo_v3,ac_v3,pa_v3:P_T_V3; // wektory: położenia (r), prędkości (v), przyspieszenia (a) pc:P_T_CPAR; pv:P_T_VPAR; begin // wszystkie zadeklarowane wektory - obiekty klasy P_T_V3 // zostają zainicjowane przez wywołanie konstruktora: ro_v3 := P_T_V3.create(0.0,0.0,0.0); rn_v3 := P_T_V3.create(0.0,0.0,0.0); vo_v3 := P_T_V3.create(0.0,0.0,0.0); vn_v3 := P_T_V3.create(0.0,0.0,0.0); roo_v3 := P_T_V3.create(0.0,0.0,0.0); ac_v3 := P_T_V3.create(0.0,0.0,0.0); pa_v3 := P_T_V3.create(0.0,0.0,0.0); w_v3 := P_T_V3.create(0.0,0.0,0.0); tso_v3 := P_T_V3.create(0.0,0.0,0.0); vef_v3 := P_T_V3.create(0.0,0.0,0.0); pc:=P_T_CPAR.create(); // parametry stale zainicjowane pv:=P_T_VPAR.create(pc); // parametry zmienne zainicjowane with pc,pv do
Dzięki tej pascalowej sztuczce - instrukcji grupującej - nie musimy używać od wołań kwalifikowanych, oszczędzamy bardzo dużo pisania... Przypominam, że operatory przeciążone otoczone są spacjami. begin tdold:=le6; // tzn. bardzo daleko ... t:=0e0; // inicjalizacja czasu ro_v3 := rs_v3; vo_v3 := vs_v3; // startowe położenie i prędkość if not l_ev then // przed metoda Verleta: jeden krok Eulera
176
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
begin t:=t+h;
177
Jeśli występuje wiatr, „nowa” pozycja punktu rn_v3 jest odpowiednio przesuwa na, i tak zmodyfikowana wchodzi do następnego kroku całkowania; z tego powodu integrator Verleta (zależny od 2 kolejnych położeń) nie może być tu stosowany, od powiednie zabezpieczenie wprowadzone jest w konstruktorze P_T_CPAR. create. W programie nie włączono wpływu wiatru na ewentualny ruch celu; możliwość uwzględniania wiatru przy strzelaniu do ruchomego celu nie jest jednak w progra mie zablokowana.
ac_v3 := P_v3_acc(t,pc,pv,ro_v3,vo_v3,tsv_v3); P_v3_euler(rn_v3Jvn_v3,ro_v3Jvo_v3,ac_v3jhjle0); roo_v3 := ro_v3; // roo_v3 := r(t-h) ro_v3 := rn_v3; // ro_v3 := r(t) vo_v3 := vn_v3 // vo_v3 := v(t+h) end; k:=0; lin:=l; if not l_par then tp:=0e0; z:=‘t'; if l_tar then // nagłówki writeln( t--| E | |-dist|az/t |') else writeln( • t--| E | cx--l-dec/G');
P-II. Obiektowo w Pascalu
n
|
u|--vE--|--vN--|--vU--|--dpE— |--dpN--
N
|
U ---- |— vE — |— vN— |— vU— |-- v —
Prędkości vo_v3, vn_v3 są prędkościami względem ośrodka, czyli stawiającego opór powietrza, i te prędkości wchodzą do procesu całkowania. Dla nas jednak bar dziej interesująca jest prędkość względem ziemi - prędkość „efektywna” vef_v3 - suma wektorowa vn_v3 i prędkości wiatru w_v3, i ona właśnie jest pokazywana na ekranie i na plikach; jednak prędkość w skali Macha (vM) obliczana jest na pod stawie vo/vn_v3, ponieważ charakteryzuje ona opór względem powietrza. vef_v3 := vn_v3 + w_v3; // efektywny wektor prędkości end else vef_v3 := vn_v3;
// główna pętla symulacji: while z='t' do begin t:=t+h; // krok czasowy k:=k+l; // numer kroku if l_tar then // jeśli cel określony: begin tso_v3 :=tsv_v3; //stara pozycja celu tsv_v3 := tsv_v3 + (tv_v3 * h)j // nowa pozycja celu td_v3 := tsv_v3 - ro_v3; // aktualny wektor punkt -> cel ac_v3 := P_v3_acc(tJpc,pvJro_v3Jvo_v3,td_v3); end // aktualne przyspieszenie else ac_v3 := P_v3_acc(t,pcJpv,ro_v3,vo_v3Jro_v3); if l_ev then // krok całkowania
p_v3_euler(rn_v3ivn_v3Jro_v3Jvo_v3,ac_v3JhJbeta_ev) else
P_v3_verlet(rn_v3,vo_v3,vn_v3jro_v3,roo_v3,ac_v3,h,beta_ev); if l_wind then // jeśli wieje wiatr: begin p:=rn_v3.zu; // aktualna wysokosc
v_w:=P_wa_fun(pJdhJhhJv_wlJv_wh)j // aktualna prędkość wiatru a_wd:=P_wa_fun(p,dh,hh,a_wld,a_whd); //aktualny kierunek wiatru a_w:=a_wd*d2r;
00
w_v3 := P_T_V3.create(v_w*sin(a_w)jV_w*cos(a_w)j e ); rn_v3 := rn_v3 + (w_v3 * h); // pozycja przesunięta przez wiatr
v:=P_T_V3.Pv3_len(vef_v3); // aktualna prędkość if l_tar then // jeśli cel określony: begin td_v3 := tsv_v3 - rn_v3; // wektor punkt -> cel t d :=P_T_V3.Pv3_len(td_v3); // odległość punkt -> cel tp:=P_T_V3.Pv2_azd(tsv_v3); // kierunek start -> cel tpt:=tp/t2d; q:=td; writeln(t:7:2,rn_v3.x e :8 :0,rn_v3.y n :8 :0,rn_v3.zu :8 :0, vef_v3.x e :7:0,vef_v3.y n :7:0,vef_v3.zu :7:0, td_v3.x e :8:1,td_v3.y n :8:1,round(td):6,round(tpt):5); if tdxtdold then begin write('odległość do celu ROŚNIE!'); lin:=lin+l end; tdold:=td end else // cel nieokreślony: writeln(t:7:2,rn_v3.x e :8:0,rn_v3.y n :8:0,rn_v3.z u :8:0, vef_v3.x e :7:0,vef_v3.y n :7:0,vef_v3.zu :7:0, v:7:0jvm:7:3,cx:7:3jb/gef:6:2); // zapis na plik co nrec krokow: if (nrec>0) and (((k mod nrec)=0) or (k=l)) then begin
178
II. EL EM EN TY PR OGR A M OW A N IA OBIEK T O W EG O
r_en:=P_T_V3.Pv2_len(rn_v3); // odległość pozioma v_en:=P_T_V3.Pv2_len(vef_v3); // prędkość pozioma if v_en0e0 then begin s:=sqrt(s); s:=2e0*s/pp; end else s:=-le0; // to w zasadzie nie ma prawa wydarzyć się! Dv3_dbe:=s; end; // Dv3_dbe // integratory Eulera i Verleta w języku wektorow // i procedur symulujących operatory wektorowe procedurę D_v3_euler (var RN_V3,VN_V3,R0_V3,V0_V3,AC_V3:D_T_V3;H,BETA_EV:T_FL);
188
II. EL EM EN TY PROGR A M OW A N IA O BIEK TO W EG O
// RN_V3,VN_V3: nowa pozycja RN i prędkość VN // R0_V3,V0_V3,AC_V3: stara pozycja RO i prędkość V0; przyspieszenie AC begin VN_V3:=D_v3_ab(V0_V3,D_v3_va(AC_V3,H)); RN_V3:=D_v3_ab(D_v3_va((D_v3_ab(D_v3_va(V0_V3,BETA_EV), D_v3_va(VN_V3,(le0-BETA_EV)))),H), D_v3_ab(R0_V3,D_v3_va(AC_V3, (0.5e0*H*H)))); end; // D_v3_euler D_v3_ab symuluje operator +, D_v3_va - operator * procedurę D_v3_verlet (var RN_V3,V0_V3,VN_V3,R0_V3,R00_V3,AC_V3:D_T_V3;HjBETA_EV:T_F L); // nowa pozycja RN, stara i nowa prędkość V0, VN // pozycje: stara RO, starsza R00; przyspieszenie AC begin RN_V3:=D_v3_ab(D_v3_ab(D_v3_va (R0_V3, (le0+BETA_EV)), D_v3_va(ROO_V3,(-BETA_EV))),D_v3_va(AC_V3,(H*H))); V0_V3:=D_v3_va(D_v3_ab(RN_V3,D_v3_va(R00_V3,(-le0))),(0.5e0/H)); VN_V3:=D_v3_ab(VO_V3,D_v3_va(AC_V3,H)); end; // D_v3_verlet
P-II.2.3. Parametry stałe: typ obiektowy d _t _c p a r i jego konstruktor Wprowadzenie do tego punktu jest identyczne z wprowadzeniem do punktu P-II.1.2, więc nie będzie tu powtarzane; jedynie nazwy P_T_... należy zastąpić przez D_T_... Konstruktor D_T_CPAR() jest dość obszerny, więc poszczególne jego bloki funkcyjne będą skomentowane bezpośrednio w jego treści. Przypomnijmy jeszcze, że wszystkie elementy typu obiektowego są w P domyślnie publiczne. Listing D-T-CPAR-object
opis identyczny z zawartym w Listingu P-T-CPAR-class; listy danych agreguje m y wg typów. l_par,l_rak,l_graw,l_cor,l_tar,l_tv,l_ster,l_wind,l_ev:boolean; gjfidjfijShjfhjdhjhh,omega, sm,fm,w,c,f_rak,cx0,syz,cxp,spar, tpar,tc,hpa,v0,az,azd,el,eld, td0,ta0,ta0d,tel0,tel0d,tv0,tav0, tav0d, ep_mx,ep_mxd,tster, v_wl,v_wh,a_wl,a_wld,a_wh,a_whd, beta_ev,h :T_FL; g_v3,o_v3,rs_v3,t s_v3,vs_v3,tv_v3, wl_v3,wh_v3:D_T_V3
Dla oszczędności miejsca nie powtarzamy tu bardzo obszernego komentarza, opisującego znaczenie każdego z elementów pół danych z osobna, bo byłby to
;
nfl:TextFile; // zmienna plikowa - "plik logiczny", zredagowany nrec:integer;
II metody: constructor create(); end; // D_T_CPAR object {$1 P ę fun.pasł
Ze względu na hierarchiczną budowę programu w Pascalu każdy podprogram musi być zdefiniowany (tzn. wprowadzony do kompilowanego tekstu) nie później niż przed pierwszym użyciem (może oczywiście zostać zdefiniowany wcześniej). Funkcje o nazwach P ..., które wystąpią w tym programie w wielu miejscach, nie korzystają z wektorów i przeciążonych operatorów - to dokładnie te same funk cje, które wystąpiły w programie Pv3_prog, więc nie będą tu powtarzane, a jedy nie dyrektywy włączające {$1 ...} wskażą miejsca, gdzie powinny one wystąpić w kodzie źródłowym. W szczególności konstruktor D_T_CPAR.create wykorzy stuje funkcję P e fun. dlatego odpowiednia dyrektywa włączająca {$1...} musi wystąpić najpóźniej w tym miejscu. Podobnie
jak dla typu D_T_V3 poniższa implementacja konstruktora D_T_CPAR.create() musi nastąpić po zakończeniu sekcji definicji typów i po zaimplementowaniu procedur działania na wektorach - metod typu obiektowego D_T_V3.
D_T_CPAR=object // dane - parametry stale:
189
P-II.2. DP: to samo trochę inaczej, program Dv3_prog.dpr
constructor D_T_CPAR.create; var p,q:T_FL; k:integer; z:char;
// konstruktor
190
II. E L EM EN TY PR OGR A M OW A N IA OBIEK T O W EG O
begin rs_v3.create(0.0,0.0,0.0); ts_v3.create(0.0,0.0,0.0); vs_v3.create(0.0,0.0,0.0);
Wektory rs_v3, ts_v3, vs_v3 m uszą zostać najpierw utworzone przez wywo łanie na ich rzecz konstruktora. Pozostałe pola wektorowe w obiekcie D_T_CPAR (g_v3, o_v3, wl_v3, wh_v3) nie m uszą być tu konstruowane - odpowiednie konstruktory zostaną wywołane później. Zauważmy, że aktywacja konstruktora ma tu postać instrukcji procedury. writeln('W CO CHCESZ SIE ZABAWIĆ?'); write('rakieta(r), spadochron(p), rzut(inaczej): '); readln(z); l_par:=z='p'; l_rak:=z='r'; if not l_par then begin write ('szerokosc geo[deg](>90: g stale);wysokosc n.p.m. startu i celu[m]: '); readln(fid,sh,fh) end else repeat write( 'szerokosc geo[deg](>90:g stale);wysokosc n.p.m. skoku,ładowania[m]: '); readln(fid,fh,sh) until fh>sh; l_graw:=(fid=-90.0); if l_graw then begin fi:=fid*d2r; if l_par then begin p:=fh; q:=sh; end else begin p:=sh; q:=fh; end; g:=P_g_fun(fi,p); writeln('g: start, cel: ',g:9:5,P_g_fun(fi,q):9:5); end else
P-II.2. DP: t o
s a m o t r o c h ę i n a c z e j , p r o g r a m D v 3 _ p r o g .d p r
191
begin write('g [m/sA2] (0 => 9.8065): '); readln(g) end; if g0:[m];=0.0;360e0) then ta0d:=ta0d-360e0; ta0:=ta0d*d2r; // [deg] => [rad] ts_v3.x e :=rs_v3.xe+td0*sin(ta0); ts_v3.yn:=rs_v3.yn+td0*cos(ta0); // współrzędne EN celu writeln ('współrzędne ENU celu[m]: ',ts_v3.xe:10:1,ts_v3.yn:10:1,ts_v3.zu:10:1); end; if z='w’ then // startowa pozycja celu jako współrzędne EN begin repeat write('współrzędne (E,N) celu wzgledem startu[m]: '); read(ts_v3.xe,ts_v3.yn); ts_v3.z u :=fh; td0:=ts_v3.Dv2_len(); // pozioma odległość start -> cel until td0>0e0; ta0d:=ts_v3.Dv2_azd(); // azymut start -> cel ta0:=ta0d*d2r; // [deg] => [rad] writeln('cel: odległość[km],azymut: ',td0/1000e0:10:2,ta0d:10:2); ts_v3:=D_v3_ab(ts_v3,rs_v3); // absolutne współrzędne celu end; tel0d:=0e0; l_tv:=false; if l_tar then // elewacja celu begin if (abs(fh-sh)>300e0) then // jeśli różnica poziomow startu i celu > 300 m begin ts_v3.zu:=fh-sh; // temp: różnica wysokości ts_v3.Dv3_aed(p,tel0d); tel0:=tel0d*d2r; ts_v3.zu:=fh; writeln('elewacja celu: ',tel0d:10:2); end; write ('prędkość celu(>0:[m/s];=0;0:[m/s];=0;1.44): '); read(syz,cx0,cxp); write('spadochron: srednica[m], czas otwarcia[s]: ‘); read(spar,tpar); // czas otwarcia spadochronu until (syz>0e0) and (cx0>0e0) and (cxp>=0e0) and (spar>0e0); if cxp 500 m nad poziomem startu else hh:=5000e0; // poziom górny 5 km (przyjęty dowolnie) repeat write('kaliber[mm](>0) lub przekrój[mA2]( ", arctan(g/sqrt(p*p+g*g))/d2r:6:1); end until p>g; if l_tar then begin repeat writeln('czas wlaczenia sterowania[s] (0e0) and (ep_mxd>10e0) then begin writeln('kat > 10 moze zdestabilizować lot: potwierdź lub zmniejsz '); readln(ep_mxd); end; until (tster0e0); ep_mx:=ep_mxd*d2r; // [deg] => [rad] l_ster:=false; // przy starcie sterowanie wyłączone end end; // warunki meteo na poziomie startu (dla spadochronu - ładowania): writeln('warunki meteo na wysokosci[m]: ',dh:8:l); write('temperatura[Cels](0:=>20 C) i cisnieniefhPa](0:=>1000): '); readln(tc,hpa); if tc=0e0 then tc:=20e0; tc:=tc+273.15e0; // temperatura [K] if hpa0e0; wl_v3.create(0e0,0e0,0e0); wh_v3.create(0e0,0e0,0e0); if l_wind then begin randomize; // zainicjowanie generatora liczb losowych if v_wl [rad] wl_v3.create(v_wl*sin(a_wl),v_wl*cos(a_wl),0e0); wh_v3.create(v_wh*sin(a_wh),v_wh*cos(a_wh),0e0); end; // integrator: repeat write('integrator, param, beta: Euler(l..0),Verlet(-l..=0, 0-bez zapisu): readln(nrec); // zapis co nrec krokow until nrec>=0; if nrec>0 then begin if l_tv then // cel ruchomy if l_ev then AssignFile(nfl,'Dv3etv.dat') else AssignFile(nfl,'Dv3vtv.dat') else if l_tar then // cel nieruchomy if l_ev then AssignFile(nfl,'Dv3et.dat') else AssignFile(nfl,'Dv3vt.dat') else // cel nieokreślony if l_ev then AssignFile(nfl,'Dv3e.dat') else AssignFile(nfl,'Dv3v.dat'); rewrite(nfl) end; end; // D_T_CPAR.create
');
P-II.2. DP: to samo trochę inaczej, program Dv3_prog.dpr
197
P-II.2.4. Parametry zmienne: typ obiektowy d _t_v p a r i jego konstruktor Wprowadzenie do tego punktu jest identyczne z wprowadzeniem do punktu P-II.1.3, więc nie będzie tu powtarzane; jedynie nazwy P_T_... należy zastąpić przez D_T_... Listing D-T-VPAR-object D_T_VPAR=object // dane - parametry zmienne:
Dla oszczędności miejsca nie powtarzamy tu komentarza, opisującego znacze nie każdego z elementów pól danych z osobna, bo byłby to opis identyczny z za wartym w Listingu P-T-VPAR-class; listy pól danych agregujemy wg typów. am,hc,tk,v,vm,td,cx,b,bef,sp,tp,gef:T_FL; l_pop,l_bal,¡boolean; zv_v3,td_v3,t sv_v3:D_T_V3; // metody: constructor create(var CP:D_T_CPAR); // konstructor end; // D_T_VPAR object
Podobnie jak dla typów obiektowych D_T_V3, D_T_C PAR poniższa implementacj a konstruktora D_T_VPAR.create() musi nastąpić po zakończeniu sekcji definicji typów i po zaimplementowaniu metod typu D_T_V3. constructor D_T_VPAR.create(var CP:D_T_CPAR); // konstruktor begin zv_v3.create(0.0,0.0,0.0); tsv_v3.create(0.0,0.0,0.0); td_v3.create(0.0,0.0,0.0); with CP do begin if l_tar then begin tsv_v3:=ts_v3; td:=td0; td_v3:=ts_v3; end; l_pop:=false; // start: spadochron zamknięty l_bal:=false; // start: silnik rakiety włączony sp:=syz; tp:=0e0; am:=sm; gef:=g end end; // D_T_VPAR.create
198
II. EL EM EN TY PR O GRAM OW ANIA O BIEK TO W EG O
P-II.2. DP:
t o s a m o t r o c h ę i n a c z e j , p r o g r a m D v 3 _ p r o g .d p r
199
P-II.2.5. Budowa wektora przyspieszenia Wprowadzenie do tego punktu jest identyczne z wprowadzeniem do punktu P-II.1.4, więc nie będzie tu powtarzane; jedynie nazw y P_T_... należy zastąpić przez D_T_... Listing D-v3-acc
Najpóźniej w tym miejscu musimy włączyć wszystkie potrzebne funkcje skalar ne; są one identyczne z wykorzystywanymi w programie Pv3_prog i zachowują używane tam nazwy. {$1 P_cx_fun.pas} {$1 P_ph_fun.pas} {$1 P_th_fun.pas} {$1 P_log_fun.pas} {$1 P_wa_fun.pas) function D_v3_acc(T:T_FL;var CP:D_T_CPAR;var VP:D_T_VPAR; var R0_V3,V0_V3,TP_V3:D_T_V3):D_T_V3; // T - czas // CP,VP - parametry stale i zmienne // R0_V3 - wektor położenia [m] // V0_V3 - wektor prędkości [m/s] // TP_V3 - wektor rakieta -> cel [m] const d_gas=0.003468654e0; //[kg*K/3] D = M(asa molowa)/R(stala gazowa) var a_v3,za_v3:D_T_V3; begin a_v3.create(0.0,0.0,0.0); za_v3.create(0.0,0.0,0.0); with CP,VP do begin hc:=R0_V3.zu; // [m] aktualna wysoksc a_v3:=g_v3; if l_graw then gef:=P_g_fun(fi,hc) // gef jako funkcja fi, hc else gef:=g; // stale g a_v3.zu:=-gef; // aU v:=VO_V3.Dv3_len(); // aktualna prędkość if v=tpar) and (not l_pop) then begin l_pop:=true; tp:=tpar+2e0; writelnCOTWIERANIE SPADOCHRONU, wysokosc[m]: ’,hc:8:l) end; if (not l_pop) and (hc0e0) and (T>=tster) then begin za_v3:=D_v3_ztv(VO_V3,TP_V3,ep_mx); // gdy sterowanie jest włączone, wersor rakiety może byc zmodyfikowany if not l_ster then begin writelnCSTEROWANIE WŁĄCZONE1); l_ster:=true end
200
II. EL EM EN TY PR OGR A M OW A N IA OBIEK T O W EG O
end; a_v3:=D_v3_ab(a_v3,D_v3_va(za_v3,(f_rak/am))) end else begin am:=fm; writelnCKONIEC PRACY SILNIKA'); l_rak:=false; l_bal:=true end end; b:=P_ph_fun(hc,dh/1000e0,hpa); // ciśnienie [hPa] bef:=d_gas*b*100e0/tk; // gestosc powietrza, [hPa] => [Pa]=[N/m] b:=0.5e0*bef*sp*cx*sqr(v)/am; // całkowity opor a_v3:=D_v3_a b (a_v3,D_v3_va(zv_v3, (- b ))); if l_cor then a_v3:=D_v3_ab(a_v3,D_v3_va(D_v3_axb(o_v3,VO_V3),(-2e0))); // wkład od przyspieszenia Coriolisa, D_v3_axb to iloczyn wektorowy end; // with D_v3_acc:=a_v3; end; // D_v3_acc
P-II.2.6. Program zarządzający Listing Dv3-driver // Dv3_driver
lin,k,j:integer; t,tpt,v_w,a_w,a_wd,tdold,v_en,r_en,p,q:T_FL; w_v3,tso_v3,vef_v3:D_T_V3; ł_f1:boolean=true; z:char; ro_v3,rn_v3,vo_v3,vn_v3, roo_v3,a c_v3,pa_v3:D_T_V3; // wektory: położenia (r), prędkości (v), przyspieszenia (a) pc:D_T_CPAR; pv:D_T_VPAR; begin ro_v3.create(0.0,0.0,0.0); rn_v3.create(0.0,0.0,0.0); vo_v3.create(0.0,0.0,0.0);
P-II.2. DP: to samo trochę inaczej, program Dv3_prog.dpr
201
vn_v3.create(0.0,0.0,0.0); roo_v3.create(0.0,0.0,0.0); ac_v3.create(0.0,0.0,0.0); pa_v3.create(0.0,0.0,0.0); // wektory wykreowane pc.create(); // parametry stale zainicjowane pv.create(pc); // parametry zmienne zainicjowane with pc,pv do
Użycie instrukcji grupującej oszczędza bardzo dużo pisania... begin tdold:=le6; // tzn. bardzo daleko ... t:=0e0; // inicjalizacja czasu ro_v3:=rs_v3; vo_v3:=vs_v3; // startowa pozycja i prędkość if not l_ev then // przed startem metody Verleta: jeden krok Eulera begin t:=t+h; a c_v3:=D_v3_ac c(t,pc,pv,ro_v3,vo_v3,t sv_v3); D_v3_euler(rn_v3,vn_v3,ro_v3,vo_v3,ac_v3,h,le0); roo_v3:=ro_v3; // roo_v3 = r(t-h) ro_v3:=rn_v3; // ro_v3 = r(t) vo_v3:=vn_v3 // vo_v3 = v(t+h) end; k:=0; lin:=l; if not l_par then tp:=0e0; z:='t‘; if l_tar then // nagłówki writeln( ■ 1--| E— |— N ---|— U — |--vE--| --vN-- |--VU-- |--dpE - - |--dpN-|-dist|az/t|') else writeln( 1— 1--| — E— |— N — |— U — |--vE--|--vN--|--vll--| — v— |--vM— [— cx--|-dec/G'); // glowna pętla symulacji while z='t' do begin t:=t+h; // krok czasowy k:=k+l; // numer kroku if l_tar then // jeśli cel jest określony: begin tso_v3:=tsv_v3; // stara pozycja celu
202
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
tsv_v3:=D_v3_ab(tsv_v3,D_v3_va(tv_v3,h)); // nowa pozycja celu td_v3:=D_v3_b2e(tsv_v3,ro_v3); // wektor punkt -> cel ac_v3:=D_v3_acc(t,pc,pv,ro_v3,vo_v3,td_v3); end else ac_v3:=D_v3_acc(t,pc,pv,ro_v3,vo_v3,ro_v3); if l_ev then // krok całkowania D_v3_euler(rn_v3,vn_v3, ro_v3,vo_v3,ac_v3,h,beta_ev) else D_v3_verlet(rn_v3,vo_v3,vn_v3,ro_v3,roo_v3,ac_v3,h,beta_ev); if l_wind then // efekty wiatru begin p:=rn_v3.zu; // aktualna wysokosc v_w:=P_wa_fun(p,dh,hh,v_wl,v_wh); // aktualna prędkość wiatru a_wd:=P_wa_fun(p,dh,hh,a_wld,a_whd); //aktualny kierunek wiatru a_w:=a_wd*d2r; w_v3.create(v_w*sin(a_w),v_w*cos(a_w),0e0); rn_v3:=D_v3_ab(rn_v3,D_v3_va(w_v3,h)); // położenie przesunięte
Jeśli występuje wiatr, „nowa” pozycja punktu rn_v3 jest odpowiednio przesuwa na, i tak zmodyfikowana wchodzi do następnego kroku całkowania; z tego powodu integrator Verleta (zależny od 2 kolejnych położeń) nie może być tu stosowany, od powiednie zabezpieczenie wprowadzone jest w konstruktorze D_T_CPAR. create. W programie nie włączono wpływu wiatru na ewentualny ruch celu; możliwość uwzględniania wiatru przy strzelaniu do ruchomego celu nie jest jednak w progra mie zablokowana. Prędkości vo_v3, vn_v3 są prędkościami względem ośrodka, czyli stawiające go opór powietrza, i te prędkości wchodzą do procesu całkowania. Dla nas jed nak bardziej interesująca jest prędkość względem ziemi - prędkość „efektywna” vef_v3 - suma wektorowa vn_v3 i prędkości wiatru w_v3, i ona właśnie jest poka zywana na ekranie i na plikach; jednak prędkość w skali Macha (vM) obliczana jest na podstawie vo/vn_v3, ponieważ charakteryzuje ona opór względem powietrza. vef_v3:=D_v3_ab(vn_v3,w_v3) end else vef_v3:=vn_v3; v :=vef_v3.Dv3_len(); // aktualna prędkość if l_tar then // na ekran: cel określony begin td_v3:=D_v3_b2e(tsv_v3,rn_v3); // wektor punkt -> cel td:=td_v3.Dv3_len(); // odległość punkt - cel tp:=tsv_v3.Dv2_azd(); // kierunek start - cel tpt:=tp/t2d;
P - I I .2 . D P : t o s a m o t r o c h ę i n a c z e j , p r o g r a m D v 3 _ p r o g .d p r
203
q:=td; writeln(t:7:2,rn_v3.x e :8:0,rn_v3.y n :8:0,rn_v3.z u :8:0, vef_v3.x e :7:0,vef_v3.y n :7:0,vef_v3.zu :7:0, td_v3.xe:8:l,td_v3.yn:8:1,round(td):6,round(tpt) :5); if td>tdold then begin writeln('odległość do celu ROŚNIE!'); lin:=lin+l end; tdold:=td end else // cel nieokreślony: writeln(t:7:2,rn_v3.x e :8:0,rn_v3.y n :8:0,rn_v3.zu :8:0, vef_v3.x e :7:0,vef_v3.y n :7:0,vef_v3.zu :7:0, v:7:0,vm:7:3,cx:7:3,b/gef:6:2); // zapis na plik: ' if (nrec>0) and (((k mod nrec)=0) or (k=l)) then // co nrec krokow: begin r_en:=rn_v3.Dv2_len(); // odległość pozioma v_en:=vef_v3.Dv2_len(); // prędkość pozima if v_en versor v3 {T_FL d,eps=le-5; d=Cv3_len(); if (d friend C_T_V3 operator * (C_T_V3 L_V3, T_FL R) //L(vector) * R(scalar)
{ return C_T_V3(L_V3.xe * R,L_V3.y n * R,L_V3.z u *R);
} friend T_FL operator || (C_T_V3 L_V3jC_T_V3 R_V3)
T_FL Cv2_azd() // v3 vector azimuth ([deg], from N towards E) { T_FL p,eps=le-8; bool l_x,l_y; l_x=fabs(xe)0e0) p=0e0; else p=180e0;
} else { p=atan(xe/yn)/d2r; if (xe*yn0e0) EL=90e0; else EL=-90e0; } // Cv3_aed // following components of C_T_V3 class depend on more than one vector:
Funkcja C_v3_ztv realizuje algorytm naprowadzania, opisany w II.4.3 (wzory 63-66, por. Listing P-v3-ztv): wektor RV opisuje prędkość punktu, RT jest wek torem od punktu do celu; EM określa maksymalny dopuszczalny kąt odchylenia emax. Funkcja wyznacza wersor kierunku naprowadzania punktu (rakiety) na cel w przestrzeni trójwymiarowej (3D).
T_FL pJq Ju,cem,par=le-5; // vector parallelity criterion cem=cos(EM); zv_v3=RV_V3.C_v3_ver(); // velocity versor zvr_v3=RT_V3.C_v3_ver(); u=zv_v3 || zvr_v3; // dot_product(zv_v3,zvr_v3) if (u B vector length, etc ... pe=(P_V3-E_V3).Cv3_len(); pp=(B_V3-E_V3).Cv3_len();
Metoda Cv3_len() jest tu aktywowana na rzecz obiektów chwilowych - wyni ków działania przeciążonego operatora s=0.5e0*(pb+pe+pp); s=s*(s-pb)*(s-pe)*(s-pp); // Heron formula if (s>0e0)
C-II. Obiektowo w C++: program Cv3_prog.cc
215
void c_v3_verlet(C_T_V3 RN_V3,C_T_V3 VO_V3,C_T_V3 VN_V3, C_T_V3 RO_V3,C_T_V3 R00_V3,C_T_V3 AC_V3,T_FL H,T_FL BETA_EV) // new position RN, old & new velocity VO, VN // positions: old RO, older ROO; acceleration AC // overloaded operators used
{ RN_V3=(RO_V3 * (le0+BETA_EV)) + (R00_V3 * (-BETA_EV)) + (AC_V3 * (H*H)); VO_V3=(RN_V3 + (R00_V3 * (-le0))) * (0.5e0/H); VN_V3=V0_V3 + (AC_V3 * H); } // C_v3_verlet }; // C_T_V3 class
{ s=sqrt(s); s=2e0*s/pp;
} else s=-le0; // this should never happen ... return s; } // Cv3_dbe
Wektorowa implementacja algorytmu Eulera (8, 10): z wektorów „starego” poło żenia RO i prędkości V0, oraz wektora przyspieszenia AC, dla wartości kroku cał kowania H i parametru „mieszania” BETA_EV - generowane są: „nowe” położenie RN i prędkość VN. void C_v3_euler(C_T_V3 RN_V3,C_T_V3 VN_V3, C_T_V3 R0_V3,C_T_V3 V0_V3,C_T_V3 AC_V3,T_FL H,T_FL BETA_EV) // RN_V3(3),VN_V3(3): new position RN & velocity VN // ROJV3(3),VO_V3(3),AC_V3(3): // old position RO & velocity VO; acceleration AC // overloaded operators used
{ VN_V3=VO_V3 + (AC_V3 * H); RN_V3=(((V0_V3 * BETA_EV) + (VN_V3 * (le0-BETA_EV))) * H) + (R0_V3 + (AC_V3 * (0.5e0*H*H))); } // C_v3_euler
Wektorowa implementacja algorytmu Verleta (14,15,5): z wektorów „starego” położenia RO i o krok „starszego” ROO, oraz wektora przyspieszenia AC, dla war tości kroku całkowania H i parametru „mieszania” BETA_EV - generowane są: „nowe” położenie RN i prędkości: „stara” V0 i „nowa” VN.
C-II.2. Klasa parametrów stałych C_T__CPAR i jej konstruktor Liczne zmienne, charakteryzujące konkretny wariant naszych „zabaw”, podzielone zostały na param etry stałe, których wartości określane są przy inicjalizacji obliczeń i w zasadzie potem już nie zmieniane (poza kilkoma w yjątkami) i parametry zmienne, których wartości zmieniane są w każdym kroku procesu całkowania równań Newtona i m ogą być śledzone przez protokoły (ekranowy i plikowy) tego procesu. Parametry stałe zgromadzone zostały w klasie C_T_CPAR (C - constant). Nazwy zmien nych zostały częściowo przeniesione z programu Cr_ev_test. Nazwy zmiennych opi sujących kąty zakończone są: ...d jeśli przechowują wartości w stopniach [deg]; ich od powiedniki bez ...d (np. azd, az) przechowują wartości w radianach [rad], używane bezpośrednio w wywołaniach funkcji trygonometrycznych. Nazwy zaczynające się od t... odnoszą się do punktu docelowego ruchu (target). Konstruktor domyślny oczywi ście ma nazwę C_T_CPAR(); tym razem jednak jego budowa nie będzie tak prosta jak w wypadku konstruktora 3-parametrowego dla klasy C_T_V3. Przeciwnie - konstruktor C_T_CPAR() jest bardzo rozbudowaną procedurą w której wartości niektórych parame trów są wczytywane z klawiatury, innych obliczane - występuje dużo wzajemnych po wiązań parametrów; mimo to - jest to pełnoprawny konstruktor. Korzysta on z funkcji C_g_fun - obliczającej przyspieszenie ziemskie wg wzoru (56), por. Listing P_g_fun, II.3; oraz z bezparametrowej funkcji C_f rand (), stanowiącej bardzo uproszczony genera tor liczb pseudolosowych z przedziału otwartego . Nie ma żadnych powodów merytorycznych, żeby funkcje te, o raczej ogólnym charakterze i wykorzystywane także poza klasą były elementami klasy C_T_CPAR. Jednak ich użycie w konstruktorze musi być zadeklarowane dyrektywą extern, informującą kompilator, że implementacja tych funk cji nastąpi poza klasą a odpowiedni kod zostanie włączony na etapie konsolidacji.
216
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
Konstruktor C_T_CPAR() jest dość obszerny, więc poszczególne jego bloki funkcyjne będą skomentowane bezpośrednio w jego treści. Listing Cv3-T-CPAR-class class C_T_CPAR {public: // data: bool l_par,l_rak,l_graw,l_cor,l_tar,l_tv,l_ster,l_wind,l_ev; T_FL g,fid,fi,sh,fh,dh,hh, omega, sm,fm,w,c,f_rak,cx0,syz,exp, spar, tpar,tc,hpa,v0,az,azd,el,eld,td0,ta0,ta0d,tel0,tel0d,tv0, tav0,tav0d,tster,v_wl,v_wh,a_wl,a_wld,a_wh,a_whd,beta_ev, h,ep_mx,ep_mxd; C_T_V3 g_v3,o_v3,ts_v3,rs_v3,tv_v3,vs_v3,wl_v3,wh_v3; int nrec; FILE *nfl;
C-II. Obiektowo w C++: program Cv3_prog.cc
// // // // // // // // // // // // // // // // //
217
td0,ta0,ta0d,tel0,tel0d:T_FL; [m,rad,deg] target: distance, azimuth, elevation l_tar:boolj true: target defined tv0,tav0,tav0d:T_FL; target: speed [m/s], horizontal velocity azimuth tv_v3:C_T_V3; target velocity vector l_tv:bool; true: movable target ep_mx,ep_mxd:T_FL; maximum steering angle [rad,deg] tster:T_FL; time [s] for steering starting; rotation speed [rad/s] o_v3:C_T_V3; omega vector l_cor:boolj true: Coriolis effects included sm,fm:T_FL; mass [kg]: starting, final w:T_FL; rocket gas speed [m/s] c:T_FL; fuel burning speed [kg/s] f_rak:T_FL; = w*c [N]: rocket engine force cx0,syz:T_FL; drag coefficient & surface [mA2] exp,spar,tpar:T_FL; parachute parameters & opening time tc,hpa:T_FLjtemperature [Cel], atmospheric pressure [hPa],lower level rs_v3:C_T_V3; starting position vector v0,az,azd,el,eld:T_FL; at start: velocity [m/s], azimuth & elevation [rad,deg] vs_v3:C_T_V3; starting velocity vector ts_v3:C_T_V3; starting target position
C_T_CPAR() // constructor {T_FL p,q; int k; char z; extern T_FL C_g_fun(T_FL ,T_FL),C_frand(); c o u t « " W CO CHCESZ SIE ZABAWIC?"«endl; coutz; l_par=z=='p‘; l_rak=z=='r'; if (!l_par) {cout« "szerokosc geo[deg](>90:g stale);wysokosc n.p.m. startu i celu[m]:"; cin>>fid>>sh>>fh;
} else
{ do
{ cout>fid>>fh>>sh; } while (fh>rs_v3.xe>>rs_v3.yn; if (l_par)
{ fi=fid*d2r; if (l_par)
{ p=fh; q=shj
}
{
else
l_tar=false; l_tv=false;
{
>
p=sh; q=fh;
else
} g=C g fun(fi.p); printf("g: start, cel: %9.5f %9.5f \n",g,C_g_fun(fi,q));
} else
{ c o u t « " g [m/sA2] (0 => 9.8065):
cin>>g;
{ rs_v3.zu=shj ts_v3.zu=fh; // vertical (U) target coordinate tel0=0e0; tel0d=0e0; // initial target elevation tster=-le0; // at start: steering off coutz; l_tar=(z=='o') ||(z=='w‘);
}; if (g>omega; l_cor=l_graw&&(omega>0e0); // Coriolis effects included if (l_cor) // constant omega within the whole EN range
{ // // //
omega=2*pi/(omega*3600e0); o_v3.xe=0e0j o_v3.yn=omega*cos(fi); o_v3.zu=omega*sin(fi);
// // // //
planet's rotation speed [rad/s] omega E omega N omega U
Tu wyjątkowo, w formie komentarza, pokazane jest „ręczne” budowanie wektora prędkości kątowej obrotu Ziemi/planety o_v3 wg (58); zauważmy też, że wektor ten będzie stały w całym zakresie m chu - jest to sensowne przybliżenie dla zasię gów (na Ziemi) rzędu 200 km. o_v3=C_T_V3(0e0,omega*cos(fi),omega*sin(fi));
}; do
{
219
}; if (z==‘w' ) / / t arget position by EN coordinates
{ do
{ coutts_v3.xe>>ts_v3.yn; ts_v3.zu=fh; td0=ts_v3.Cv2_len(); // horizontal distance start -> target } while (td0 target ta0=ta0d*d2r; // [deg] => [rad]
220
II. EL EM EN TY PR O GRAM OW ANIA O BIEK TO W EG O
printf( "cel: odległość[km],azymut: %10.2f%10.2f\n",td0/1000e0,ta0d); ts_v3=ts_v3 + rs_v3; // absolute target coordinates
>; tel0d=0e0; l_tv=falsej if (l_tar) // target elevation
{ if (fabs(fh-sh)>300e0)//if start & final level difference>300 m
{
C-II. Obiektowo w C++: program Cv3_prog.cc
221
P=(v0*v0)*sin(2e0*el)/g/1000e0; // range without drag [km] if ((!l_rak)&&(!l_par))
{ cout«"bez oporu: zasięg, wysokosc[km], kierunek (E,N) celu: " « endl; printf("%15.5f%15.5f%15.5f%15.5f\n",p,v0*sin(el)*sin(el)/g/2e0, p*sin(az),p*cos(az)); if (p>200e0) cout1.44): cin>>syz>>cx0>>cxp; cout0:[m/s]j=0;>azd>>eld; } while ((eld>90e0)I I(eld [rad] vs_v3.zu=v0*sin(el); vs_v3.xe=v0*cos(el); // velocity: vertical U & projection on EN vs_v3.yn=vs_v3.xe*cos(az); vs_v3.xe=vs_v3.xe*sin( az); // velocity: EN components
} else // not parachute
{ dh=sh; // starting level if ((l_tar)&&(fh>sh+500e0)) hh=fh; // if final level > 500 m over starting level else hh=5000e0; // upper level 5 km (arbitrary) do
{ cout>syz>>cx0; } while ((syz==0e0)||(cx00e0)
{ syz=syz/1000e0; // [mm] => [m] syz=pi*syz*syz/4e0; // for axial bodies
222
II. EL EM EN TY PR O GRAM OW ANIA OB IEK T O W EG O
C-II. Obiektowo w C++: program Cv3_prog.cc
223
} else syz=-syz; // drag: b=0.5*ro*syz*cx*vA2
}; // rocket: if (l_rak)
{ do
{ do
{ cout«"rakieta z silnikiem liniowym"f_rak>>c; } while ((f_rak0e0)&&(ep_mxd>10e0))
// meteo conditions at start (for parachute jump at landing): printf("warunki meteo na wysokosci[m]: %8.1f\n",dh); cout>tc>>hpa; if (tc==0e0) tc=20e0; tc=tc+273.15e0; // temperature [K] if (hpa>v_wlj l_wind=v_wl!=0e0; wl_v3=C_T_V3(0e0,0e0,0e0); wh_v3=C_T_V3(0e0,0e0,0e0); if (l_wind)
{ srand(time(NULL)); // init of random number generator if (v_wla_whd; if (a_whd0.5e0) a_whd=a_wld+a_whd*C_frand()j//upper wind direction randomly .. else a_whd=a_wld-a_whd*C_frand(); // +-a_whd relative to lower wind
{ cout«"kat > 10 może zdestabilizować lot: potwierdź lub zmniejsz cin>>ep_mxd;
}; } while ((tster>0e0)&&(ep_mxd [rad] l_ster=false; // at start steering off
}J if (a_wld>360e0) a_wld=a_wld-360e0; if (a_whd360e0) a_whd=a_whd-360e0; printf("wiatr: dolny %8.2f m/s, kierunek %8.2f\n",v_wl,a_wld); printf("wiatr: górny %8.2f m/s, kierunek %8.2f\n",v_wh,a_whd)j
224
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
a_wl=a_wld*d2r; a_wh=a_whd*d2r; // [deg] => [rad] wl_v3=C_T_V3(v_wl* s in(a_wl),v_wl*cos(a_wl),0e0 ); wh_v3=C_T_V3(v_wh*sin(a_wh),v_wh*cos(a_wh),0e0);
C-II. Obiektowo w C++: program Cv3_prog.cc
225
}; }; // C_T_CPAR() }; // C_T_CPAR class
}; // integrator: do
{ cout=0, 0-bez zapisu): "j cin>>nrec; // each nrec line recorded to nfl } while (nrec0)
{ if (l_tv) // movable target
{ if (l_ev) nfl=fopen("Cv3etv.dat","w"); else nfl=fopen("Cv3vtv.dat","w");
// // // // // //
II
}
// // //
else if (l_tar) // non-movable target
II
{
// //
if (l_ev) nfl=fopen("Cv3et.dat","w"); else nfl=fopen("Cv3vt.dat","w");
} else if (l_ev) // target undefined nfl=fopen("Cv3e.dat","w")j else nfl=fopen("Cv3v.dat","w");
am: = sm - c*t: actual mass l_pop: true: parachute opened l_bal: true: rocket fuel burned out, engine off he: [m] actual altitude tk: [K] actual temperature v,vm: actual velocity [m/s, Mach (Ma)] zv_v3: actual velocity versor td: actual distance to target td_v3: actual vector point -> target tsv_v3: actual vector start -> target cx,b,bef: actual drag parameters sp,tp: actual parachute parameters gef: actual gravitation
// methods: C_T_VPAR(C_T_CPAR &CP) // constructor
{ if (CP.l_tar)
{ tsv_v3=CP.ts_v3; td=CP.td0; td_v3=CP.ts_v3;
226
II. EL EM EN TY PR OGR A M OW A N IA O B IEK T O W EG O
l_pop=false; // at start: parachute closed l_bal=false; / / a t start: rocket engine on sp=CP.syz; tp=0e0; am=CP.sm; gef=CP.g; } // C_T_VPAR
C-II. Obiektowo w C++:
p r o g r a m C v 3 _ p r o g .c c
227
pow(p, r ) jest standardową funkcją C++, zwracającąpr else g=asup+(gmax-asup)*exp(log((gsup-asup)/(gmax-asup)) *pow((U-umax)/(usup-umax),2)); return CX0*g; } // C_cx_fun
}; // C_T_VPAR class
C-1L4. Funkcje niekorzystająee z wektorów Szereg funkcji, realizujących wykorzystywane tu modele fizyczne II.l - II.2 i funkcji pomocniczych, m a charakter całkowicie skalarny; m uszą być one zdefiniowane zanim przystąpimy do obliczania wektora przyspieszenia. Listing Cv3-fun
Przyspieszenie ziemskie jako funkcja szerokości geograficznej FI i wysokości HM, wg wzoru (56). T_FL C g fun(T FL FI,T_FL HM) // gravitation as function of latitude FI and altitude HM (T_FL sf,s2f; sf=sin(FI); s2f=sin(2e0*FI); return 9.780318e0*(le0+0.0053024e0*sf*sf-0.0000058e0*s2f*s2f)-3.086e-6*HMj }; // C_g_fun
Współczynnik oporu kształtu cx jako funkcja prędkości Uw skali Macha (50-52). T_FL C_CX_fun(T_FL U,T_FL CX0) // drag coefficient (cx) as function of velocity U in Mach's [Ma] scale (T_FL g; T_FL umax=0.98e0, gmax=2.5e0,alow=le0,ulow=le0/3e0,glow=alow+0.01e0; T_FL asup=alow+(gmax-alow)/2e0,usup=3e0,gsup=asup+(gmax-asup)/2e0; if (Ikumax) g=alow+(gmax-alow)*exp(log((glow-alow)/(gmax-alow)) *pow((U-umax)/(ulow-umax),2));
Ciśnienie [hPa] jako funkcja wysoksci H [m] przy założeniu, że na wysokości H0 [m] ciśnienie wynosi P0 [mbar=hPa] dla modelowej atmosfery standardowej; dane dla ciśnienia (ptab [mbar]) w funkcji wysokości (htab [km]) dla atmosfery standardowej wg Encyklopedii Fizyki I, str. 263 (por. Tabela 1). Dla poszczegól nych przedziałów wysokości stosowane są odpowiednie „wzory barometryczne” (45): wartości a dla poszczególnych przedziałów obliczane są wg (48) i groma dzone w tablicy eps. Elementy [0] wszystkich tablic nie są wykorzystywane. Mamy tu po raz pierwszy konstrukcję inicjującą wartości w tablicy: ma ona ogólnąpostać: typ tablica[zakres_indeksu]={wartość,wartość,wartość, ...}; T_FL C_ph_fun(T_FL H,T_FL H0,T_FL P0) // pressure [hPa] as a function of altitude H[m], with assumption // that at the altitude H0[m] pressure is given as P0[mbar=hPa], // for the model standard atmosphere; // the data for pressure (ptab [mbar]) as function of altitude // (htab [km]) for the standard atmosphere according to // Encyklopedia Fizyki I p. 263 {T_FL htab[16]={0e0,0e0,5e0,10e0,15e0,20e0,25e0,30e0,40e0,50e0,60e0, 70e0,80e0,90e0,100e0,200e0}; T_FL ptab[16]={0e0,1013.25e0,540.48e0,265e0,121.12e0,55.29e0,25.27e0, 11.85e0,2.99e0,0.87e0,0.25e0,0.06e0,0.01e0,1.35e-3, 2.13e-4,1.62e-6}; T_FL hi,eps[16]; // element [0] not used! if (P0==0e0) P0=ptab[l]j eps[l]=log(ptab[2]/P0)/(htab[2]-H0); // assumed point (H0,P0) for (int i=l;i [km] if (hi return P0*exp(eps[l]*(hi-H0)); // up to 5 km else // barometric formula for a subrange
228
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
for (int i=2;i=htab[i])&&(hi } // C_ph_fun
Temperatura fK] jako funkcja wysokości H [m] przy założeniu, że na wysoko ści H0 [m] temperatura wynosi T0 [K]; dane liczbowe wg Encyklopedii Fizyki I, str. 139. Dla poszczególnych przedziałów wysokości obliczane są współczynniki interpolacji liniowych (49). T_FL C_th_fun(T_FL H,T_FL H0,T_FL T0) // absolute temperature [K] as a function of altitude H[m]; // data according to: Encyklopedia Fizyki I, p. 139 {T_FL a01=-0.0063e0, // [K/m] - average troposphere temperature gradient hl=15000e0, // [m]; tl = 190-210 [K] - tropopause h2=50000e0, t2=300e0, // stratopause h3=85000e0, t3=150e0, // mezopause h4=500000e0, t4=1500e0; // termopause T_FL tl,b01,al2,bl2,a23,b23,a34,b34; a23=(t2-t3)/(h2-h3); b23=(h2*t3-h3*t2)/(h2-h3); b34=(h3*t4-h4*t3)/(h3-h4); a34=(t3-t4)/(h3-h4)j b01=T0-a01*H0; // a01, b01 - troposphere parameters tl=a01*hl+b01; // variable temperaturę for tropopause al2=(tl-t2)/(hl-h2); bl2=(hl*t2-h2*tl)/(hl-h2); if (H=CP.tster))
{ za_v3=za_v3.C_v3_ztv(VO_V3,TP_V3,CP.ep_mx); // when steernig on, the rocket versor can be modified; eqs (6366) if ( !CP.l_ster)
{ cout ',EM/d2r endif end function F_v3_ztv
real*8 function Fv3_dbe(B,E,P,B_V3,E_V3,P_V3) ! distance P to line B-E real*8,intent(in)::B,E,P real*8,intent(in),optional::B_V3(3),E_V3(3),P_V3(3) real*8 s,pb,pe,pp
Funkcja Fv3_dbe buduje w przestrzeni trójwymiarowej trójkąt o bokach B, E, P, lub o wierzchołkach wskazywanych przez wektory B_V3, E_V3, P_V3. Jej wartością jest odległość punktu P od linii łączącej punkty B i E (czyli wysokość w trójkącie o podstawie BE); wykorzystany jest wzór Herona na pole trójkąta. Pokazana jest tu kolejna możliwość F9x: opcjonalne parametry formalne - ta kie, które m ogą wystąpić lub nie na liście parametrów aktualnych danego wy wołania. W nagłówku segmentu m uszą one wystąpić po parametrach „nie
F-II. Obiektowo w F9x: program Fv3_prog.f90
247
opcjonalnych”, a w specyfikacji mieć atrybut optional; w treści segmentu można badać obecność ich na liście parametrów aktualnych funkcjąlogicznąpresent (...). if (present(B_V3)) then pb=Fv3_len(P_V3.T O .B_V3) else pb=B endif if (present(E_V3)) then pe=Fv3_len(P_V3.T O .E_V3) else pe=E endif if (present(P_V3)) then pp=Fv3_len(B_V3.T O .E_V3) else pp=P endif s=0.5d0*(pb+pe+pp); s=s*(s-pb)*(s-pe)*(s-pp) ! Heron formula if (s>0d0) then s=sqrt(s); Fv3_dbe=2d0*s/pp else pause ' ERR in Fv3_dbe' ! in priciple not possible endif end function Fv3_dbe
Integratory Eulera i Verleta wykorzystujące wektory i ich algebrę dostępną sys temowo w F9x. Wektory przenoszące wyniki działania procedury muszą mieć atrybut intent(out)- odpowiada to wywołaniu przez nazwę/referencje w innych językach. subroutine F_v3_euler(RN_V3,VN_V3,RO_V3,VO_V3,AC_V3,H,BETA_EV) real*8,intent(out)::RN_V3(3),VN_V3(3) ! new position RN & velocity VN real*8,intent(in)::R0_V3(3),V0_V3(3),AC_V3(3) ! old position RO & velocity VO; acceleration AC real*8,intent(in)::H,BETA_EV VN_V3=VO_V3 + (H * AC_V3) RN_V3=H * ((BETA_EV * VO_V3) + ((ld0 - BETA_EV) * VN_V3)) & + (R0_V3 + (0.5d0*H*H) * AC_V3) end subroutine F_v3_euler
subroutine F_v3_verlet(RN_V3,V0_V3,VN_V3,R0_V3,R00_V3,AC_V3,H,BETA_EV)
248
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
F-II. Obiektowo w F9x: program Fv3_prog.f90
249
Listing Fv3-fun-lib real*8,intent(out)::RN_V3(3),VO_V3(3),VN_V3(3) ! new position RN, old & new velocity VO, VN real*8,intent(in)::R0_V3(3),f!00_V3(3),AC_V3(3) ! position: old RO, older R00; acceleration AC real*8,intent(in)::H,BETA_EV RN_V3=(((ld0+BETA_EV) * R0_V3) + ((-BETA_EV) * R00_V3)) + ((H*H) * AC_V3) VO_V3=(0.5/h) * (RN_V3 - R00_V3) VN_V3=VO_V3 + (H * AC_V3) end subroutine F_v3_verlet
&
end module Fv3_v_class
Jeśli chcemy - tak jak poprzednio - moduł ten skompilować, napiszemy: g95 Fv3_v_class.f90 -c -o Fv3_v_class.o95
W naszym katalogu roboczym zobaczymy teraz dwa kolejne pliki: Fv3_v_class.o95 - kod pośredni Fv3_v_class.mod - mapa m odułu (plik znakowy, możemy go sobie obejrzeć)
Kod pośredni możemy oczywiście dopisać do naszego archiwum Fv3_o95.1ib: ar -rv Fv3_o95.1ib Fv3_v_class.o95
F-II.4. Moduł funkcji pomocniczych, Fv3_fun_lib Moduł Fv3_fun_lib zawiera wszystkie skalarne funkcje pomocnicze wprowadzo ne w II.1-II.4, wykorzystywane przez inne moduły programu i przez program zarzą dzający. Nie korzysta ze stałych zdefiniowanych w Fv3_def ani z danych, operatorów i procedur wektorowych - nie korzysta więc z m odułu Fv3_v_class. Dlatego z tych modułów nie „dziedziczy” przez dyrektywę use. Nie zawiera żadnych danych, tyl ko procedury składowe wymienione po contains. Jest to w istocie pewna biblioteka ( _ lib = library) niezależnych podprogramów (dlatego w każdym z nich występuje dy rektywa implicit none), połączonych we wspólny moduł tylko dla wygody; tak rozu miany moduł (występujący w takim znaczeniu także w innych językach) merytorycznie nie m a nic wspólnego z klasą.
module Fv3_fun_lib ! no data, only ... contains ! methods: function F g fun(FI.HM) implicit none ! gravitation as function of latitude FI and altitude HM real*8 F g fun.FI.HM F_g_fun=9 .780318d0*(ld0+0.0053024d0*(sin(FI))**2 & -0.0000058d0*(sin(2*FI))**2) - 3.086d-6*HM end function F_g_fun real*8 function F_cx_fun(U,CX0) implicit none ! drag coefficient (cx) as a function of Mach's velocity U [Ma] real*8 U,CX0,g,alh,ulh real*8,parameter::umax=0.98d0,gmax=2.5d0,alow=ld0,ulow=ld0/3d0, glow=alow+0.01d0 real*8,parameter::asup=alow+(gmax-alow)/2d0,usup=3d0, & gsup=asup+(gmax-asup)/2d0
&
if (Ikumax) then g=gauss_fun(U,alow,ulow,glow) else g=gauss_fun(U,asup,usup,gsup) endif F_cx_fun=CX0*g contains ! internal components:
Mamy tu przykład segmentu, który zawiera w sobie (contains) funkcję lokalną gauss_fun, por. (51, 52), Listing P-cx-fun; nazwy: gmax, umax są w niej nielo kalne. real*8 function gauss_fun(U,ALH,ULH,GLH) ! local function real*8 U,ALH,ULH,GLH gauss_fun=ALH+(gmax-ALH)*exp(log((GLH-ALH)/(gmax-ALH))*((U-umax)/ &
250
II. EL EM EN TY PR OGR A M OW A N IA O B IEKTOW EGO
(ULH-umax))**2) end function gauss_fun end function F_cx_fun real*8 function F_ph_fun(HjH0,P0) implicit none ! pressure [hPa] as a function of altitude H[m]j for the model ! standard atmosphere; at H0[m] pressure has the value P0[mbar=hPa] ! the standard data for pressure (ptab [mbar]) & altitude (htab [km]) ! according to Encyklopedia Fizyki I p. 263 real*8,parameter::htab(15)=(/0d0,5d0,10d0, 15d0j20d0j25d0.,30d0, & 40d0j50d0j60d0j 70d0j80d0,90d0,100d0,200d0/)
Te dwie dyrektywy definiują tablice stałych, inicjowane listami wartości postaci (/wartość_l ,wartość_2,.../); są to te same wartości co w Tabeli 1 (II.l). Możliwe jest w tym wypadku użycie typowej fortranowskiej konstrukcji: krotność * wartość.
real*8,parameter::ptab(15)=(/1013.25d0j540.48d0j265d0j & 121.12d0j55.29d0,25.27d0,11.85d0,2.99d0, & 0.87d0j0.25d0j0.06d0,0.01d0jl.35d-3,2.13d-4jl.62d-6/) integer i real*8
hi,eps(15)
if (P0==0d0) P0=ptab(l) eps(l)=log(ptab(2)/P0)/(htab(2)-H0) ! point (H0,P0) is defined do i=l,14 eps(i)=log(ptab(i+l)/ptab(i))/(htab(i+1)-htab(i)) enddo hi=H/1000d0; ! [m] => [km] if (hi=htab(i)).and.(hi90:g stale)jwysokosc n.p.m. skoku i ladowania[m]: read(*,*,err=602) cp%fid,cp%fh,cp%sh if (cp%fh 9.8065): read(*,*,err=603) cp%g endif if (cp%g300d0) then ! when target more than 300 m over start cp%ts_v3(3)=cp%fh-cp%sh; call Fv3_aed(cp%ts_v3, p,cp%tel0d); tel0=tel0d*d2r; cp%ts_v3(3)=cp%fh print '(a,fl0 .2 )','elewacja celu: ',cp%tel 0 d endif 610 print *, & 'prędkość celu(>0 :[m/s];< 0 :[km/h]); kierunek ruchu(>=0 ;=360d0) cp%tav0d=cp%tav0d-360d0 cp%tav0 =cp%tav 0 d*d 2 r; ! [deg] => [rad] cp%l_tv=cp%tv0 >0 d 0 [ target: horizontal velocity tv 0 , direction tav 0 d cp%tv_v3(l)=cp%tv0*sin(cp%tav0); cp%tv_v3(2)=cp%tv0*cos(cp%tav0) cp%tv_v3(3)=0d0 print '(a,3fl0 .3)','składowe ENU prędkości celu[m/s]: ',cp%tv_v3 endif endif 611 print *, & ! at start: velocity, azimuth, elevation 'start: predkosc(>0 :[m/s];= 0 ;1.44): ' read(*,*,err=612) cp%syz,cp%cx 0 ,cp%cxp 613 print 500,'spadochron: srednica[m], czas otwarciafs]: ' read(*,*,err=613) cp%spar,cp%tpar ! parachute opening time if ((cp%syz 10 d 0 )) then print 500,'kat > 10 może zdestabilizować lot: potwierdź lub zmniejsz read(*,*,err=616) cp%ep_mxd endif if ((cp%tster>0 d 0 ).and.(cp%ep_mxd [rad] endif endif ! meteo conditions at start (for parachute jump at Ending): fii7 nrint '(a,f 8 .1 ) ','warunki meteo na wysokosci[m]: ,cpAan print 5 0 0 , temperatura[Cels](0=>20 C) i cisnienie[hPa](0=>1000): read(*,*,err=617) cp%tc,cp%hpa if (cp%tc==0d0) cp%tc=20d0; cp%tc=cp%tc+273.15d0 ! temperature [K] if (cp%hpa [rad] cp%wl_v 3 (l)=cp%v_wl*sin(cp%a_wl); cp%wl_v3(2)=cp%v_wl*cos(cp%a_wl) cp%wh_v3(l)=cp%v_wh*sin(cp%a_wh ) ; cp%wh_v3(2)=cp%v_wh*cos(cp%a_wh) endif ! integrator:
260
II. ELEM EN TY P R OGR A M OW A N IA O BIEK TO W EG O
F-II. Obiektowo w F9x: program Fv3_prog.f90
261
W naszym katalogu roboczym zobaczymy teraz dwa kolejne pliki: 622
print 500, & 'integrator, param, beta: Euler(l..0),Verlet(-l..=0d0; ! true - Euler; false - Verlet if (cp%l_wind.and..not.cp%l_ev) then print *, & 'gdy wieje wiatr, tylko integrator Eulera jest dostępny ...' cp%beta_ev=abs(c p%b et a_ev)j cp%l_ev=.true. endif if ( .not.cp%l_ev) cp%beta_ev=-cp%beta_ev if (cp%beta_ev>ld0) cp%beta_ev=ld0 if (cp%h=CP%tster)) then za_v3=F_v3_ztv(V0_V3,TP_V3,CP%ep_mx) ! when steernig on, the rocket versor can be modified if (,not.CP%l_ster) then print *, 'STEROWANIE WŁĄCZONE' CP%l_ster=.true. endif endif a_v3=a_v3+((CP%f_ra k/VP%am)* za_v3)
program Fv3_prog use Fv3_vpar_class
Pamiętajmy o hierarchii „dziedziczenia” w F9x: use Fv3_vpar_class ozna cza automatycznie włączenie Fv3_cpar_class, co automatycznie włącza Fv3_v_class, co włącza Fv3_def.
use Fv3_fun_lib integer lin,k,j real*8 t,tpt,v_w,a_w,a_wd,tdold,p,q,v_en,r_en
266
II. EL EM EN TY PR OGR A M OW A N IA OB IEK T O W EG O
real*8,dimension(3)::w_v3,tso_v3,vef_v3 logical::l_fl=.true. character z type(F_T_CPAR)::pc=F_T_CPAR( & [constant parameters, default structural constructor .false., .false., .false., .false., .false., .false., .false., .false., .false., ! 9 logicals initialized &
! 44 real*8 initialized 8 real*8 vectors initialized ! 2 integers initialized
Te dwie dyrektywy, powyżej i poniżej, deklarują struktury: pc (typu F_T_CPAR) i pv (typu F_T_VPAR), i nadają wartości początkowe wszystkim ich polom, wyko rzystując domyślne konstruktory strukturalne (default structural constructor) o nazwach identycznych z nazwami odpowiednich typów (nie modułów!) - tak jak w wersji C. Dzięki temu w pseudo-konstruktorach zdefiniowanych w m o dułach Fv3_cpar_class, Fv3_vpar_class możemy zmieniać tylko niektóre z wartości pól danych, mając pewność, że wszystkie pozostałe pola m ają nadane wartości, nie przechowują jakichś wartości przypadkowych; w wersjach P/D/C musieliśmy o to dodatkowo zadbać. Zauważmy, że każdy z wektorów inicjowany jest jedną wartością 0d0, nadawaną od razu wszystkim elementom. type(F_T_VPAR): :pv= F_T_VPAR( & ¡variable parameters, default strutural constructor 0d0,0d0,0d0,0d0,0d0,0d0,0d0,0d0,0d0,0d0,0d0,0d0, & ! 12 real*8 initialized .false.,.false., & ! 2 logicals initialized 0d0,0d0,0d0) ! 3 real*8 vectors initialized real*8,dimension(3): :ro_v3,rn_v3,vo_v3,vn_v3,roo_v3,ac_v3,pa_v3 ! vectors for: positions (r), velocities (v), accelaration (a) [real*8 & ! F_wa_fun,F_log_fun,F_cx_fun,F_ph_fun,F_th_fun [implicit interfaces
W Fortranie każda nazwa przenosząca wartość musi mieć zadeklarowany typ, w szczególności zadeklarować trzeba użycie wszystkich występujących funkcji; w F9x nazywa się to niejawnym (im p lic it) interface funkcji. Tu dyrektywa ta nie jest konieczna Gest zakomentowana), ponieważ wymienione w niej funkcje są
F-II. Obiektowo w F9x: program Fv3_prog.f90
267
elementami modułu włączonego przez use ..., którego plik *.mod (generowany przy kompilacji) już te niejawne interfejsy zawiera. Jeśli funkcja przenosi wartość typu pochodnego, lub operuje na wskaźnikach (pointer - tu to nie wystąpi), konieczny jest jawny (explicit) interface, który jest dokładnym powtórzeniem nagłówka takiej funkcji. interface ! explicit interface for F_v3_acc function F_v3_acc(T,CP,VP,R0_V3,VO_V3,TP_V3) result(ac_v3) use Fv3_vpar_class implicit none type(F_T_CPAR) CP; type(F_T_VPAR) VP real*8,dimension(3),intent(in)::RO_V3,VO_V3,TP_V3 real*8 ac_v3(3) real*8 T end function F_v3_acc end interface pc=Ft_cpar_() ! manual pseudo-constructor for constant parameters pv=Ft_vpar_(pc) ! manual pseudo-constructor for variable parameters tdold=le6; ! ie very far ... t=0d0 ! time init ro_v3=pc%rs_v3; vo_v3=pc%vs_v3; ! starting position & velocity if (.not.pc%l_ev) then ! before Verlet: single Euler step t=t+pc%h; ac_v3=F_v3_acc(t,pc,pv,ro_v3,vo_v3,pv%tsv_v3) call F_v3_euler(rn_v3,vn_v3,ro_v3,vo_v3,ac_v3,pc%h,ld0) roo_v3=ro_v3; ! roo_v3 = r(t-h) ro_v3=rn_v3; ! ro_v3 = r(t) vo_v3=vn_v3; ! vo_v3 = v(t+h) endif k=0; lin=l; if (.not.pc%l_par) pv%tp=0d0; z='t'; 500 format(a$) if (pc%l_tar) then ! headers print 500,1— 1--| — E— |— N — |— U — |— vE — |--vN-~ |--vU-- |— dpE |' print *,'-dpN--|-dist|az/t|' else print 500, t--|---E— I— U--I--VE— I-VN--I--VU— |— v — |’ print *,' -vM--j — cx— |-dec/G' endif ! main integration loop do while (z=='t 1) t=t+pc%h; ! time step
II. ELEM EN TY PROGR A M OW A N IA O BIEK TO W EG O
268
k=k+l ! step number if (pc%l_tar) then ! when target defined tso_v3=pv%tsv_v3 ! old target position pv%tsv_v3=pv%tsv_v3+(pc%h*pc%tv_v3) ! new target position pv%td_v3=ro_v3.T O .pv%tsv_v3 ! actual rocket -> target ac v3=F_v3_acc(t,pc,pv,ro_v3jVo_v3,pv%td_v3) ! actual acceleration else ac v3 =F _v 3_ ac c( t, pc ,p v, ro _v 3, vo _v3 ,r o_ v3 )
! ac tu al a c ce le ra ti on
endif if (pc%l_ev) then call F_v3_euler(rn_v3,vn_v3,ro_v3,vo_v3,ac_v3,pc%h,pc%beta_ev) else call F_v3_verlet(rn_v3,vo_v3,vn_v3,ro_v3,roo_v3,ac_v3,pc%h,pc%beta_ev) endif ! integration step done if (pc%l_wind) then ! wind effects p=rn_v3(3) ! actual altitude v_ w = F_ w a _ f U n ( p Jp c % d h ip c % h h , p c % v _ w l Jpc%v_wh) ! actual wind speed a _ w d = F _ w a _ fu n( p, pc %d h, pc %h hJp c % a _ w l d Jpc%a_whd) ! actual wind azimuth a_w=a_wd*d2r;
w_v3(l)=v_w*sin(a_w); w_v3(2)=v_w*cos(a_w ) ; w_v3(3)=0d0 rn_v3=rn_v3+(pc%h*w_v3) ! position shifted due to wind
Jeśli występuje wiatr, „nowa” pozycja punktu rn_v3 jest odpowiednio przesuwa na, i tak zmodyfikowana wchodzi do następnego kroku całkowania; z tego powo du integrator Verleta (zależny od 2 kolejnych położeń) nie może być tu w prosty sposób zastosowany, odpowiednie zabezpieczenie wprowadzone jest w pseudo-konstruktorze Ft_cpar_(). W programie nie włączono wpływu wiatru na ewen tualny ruch celu; możliwość uwzględniania wiatru przy strzelaniu do ruchomego celu nie jest jednak w programie zablokowana. Prędkości vo_v3, vn_v3 są prędkościami względem ośrodka, czyli stawiające go opór powietrza, i te prędkości wchodzą do procesu całkowania. Dla nas jed nak bardziej interesująca jest prędkość względem ziemi - prędkość „efektywna” vef_v3 - suma wektorowa vn_v3 i prędkości wiatru w_v3, i ona właśnie jest poka zywana na ekranie i na plikach; jednak prędkość w skali Macha (vM) obliczana jest na podstawie vo/vn_v3, ponieważ charakteryzuje ona opór względem powietrza. vef_v3=vn_v3+w_v3 else vef_v3=vn_v3 endif pv%v=Fv3_len(vef_v3) ! actual velocity
F-II. Obiektowo w F9x: program Fv3_prog.f90
269
if (pc%l_tar) then ! when target defined pv%td_v3=rn_v3.TO.pv%tsv_v3 ! vector projectile -> target pv%td=Fv3_len(pv%td_v3) ! distance projectile - target pv%tp=Fv2_azd(pv%tsv_v3) ! azimuth start -> target tpt=pv%tp/t2d print 501,t,rn_v3,vef_v3, (pv%td_v3(j),j=l,2),int(pv%td),int(tpt) 501 format(f7.2,3f8.0,3f7.0,2f8.1,i6,i5) if (pv%td>tdold) then print *,'odleglosc do celu ROSNIE!' lin=lin+l endif tdold=pv%td else ! target not defined print 502,t,rn_v3,vef_v3,pv%v,pv%vm, pv%cx,pv%b/pv%gef
Tablica na liście we/wy oznacza: czytaj/pisz całą tablicę, w zlinearyzowanej (antyleksykalnie!) kolejności elementów; tu mamy wektory, więc po prostu elementy 1 do 3 , jeśli byłaby tablica 2 -indeksowa, to transmitowana będzie kolumnami. 502 fo rmat(f7.2,3f8.0,4f7.0,2f7. 3, f 6.2 ) endif ! file recording if ((pc%nrec>0).and.((mod(k,pc%nrec)==0).or.(k==l))) then ! file recording for each nrec step: r_en=Fv2_len(rn_v3); ! horizontal distance v_en=Fv2_len(vef_v3); ! horizontal velocity if (v_en90: g stale); wysokosc n.p
skoku i ładowania[m]: 40
275
Z-II. Skoki, rzuty i rakiety, czyli dalsze zabawy z komputerem
6.85 6.90 6.95 7.00 7.05 7.10
-1336. -1335. -1335. -1334. -1334. -1333.
2566. 2565. 2564. 2562. 2561. 2560.
1359. 1358. 1357. 1357. 1356. 1356.
13. 12. 11. 11. 10. 10.
-20. -21. -21. -22. -22. -23.
-17. -15. -14. -13. -12. -11.
29. 28. 28. 28. 27. 27.
0.062 0.056 0.051 0.046 0.042 0.038
1.156 1.177 1.198 1.220 1.242 1.263
4.51 4.31 4.06 3.77 3.48 3.18
opór łagodnie maleje 7.15 7.20
-1333. -1332.
2559. 2558.
1355. 1355.
10. 9.
-23. -24.
-10. -9.
27. 27.
0.035 0.032
1.284 1.304
2.90 2.64
8.60 8.65 8.70
-1321. -1321. -1321.
2523. 2522. 2520.
1346. 1346. 1346.
7. 7. 7.
-26. -26. -26.
-5. -5. -5.
27. 27. 27.
0.016 0.016 0.016
1.439 1.439 1.440
1.01 1.01 1.01
-6. -6. -6. -6. -6. -6.
-5. -5. -5. -5. -5. -5.
9. 9. 9. 9. 9. 9.
0.015 0.015 0.015 0.015 0.015 0.015
1.440 1.440 1.440 1.440 1.440 1.440
1.00 1.00 1.00 1.00 1.00 1.00
koniec fazy otwierania spadochronu
g: start, cel: 9.79706 9.80 0 1 5 okres rotacji[godz.] (0-bez e f e k t ó w C o riolisa; 24 - Ziemia): 0 masa początkową, k o ń c o w a [k g ] ( 0 : [m/s]; = 0 ; < 0 : [ t y s ] ) ; e l e w a c ] a ( - 9 0 . .90). -360 45 0 powierzchnia c i a l a [ m A 2], cx0, c x p ( 0 = > 1 . 4 4 ) : 4 1 0 spadochron: srednica[m], c zas otwa r c i a [ s ] : 12 5 powierzchnia s p a d o chronu [mA 2]: 226 . 1 9 warunki meteo na w y s o k o s c i [ m ] : 500.0 te m p e r a t ura[C e l s ] ( 0 = >2 0 C) i c i s n i e n i e [ h P a ] ( 0 = > 1 0 0 0 ) : 5 9 80 wiat r na 500. m: prędk o ś ć w l [ m / s ] ( 0 : b r a k ; < 0 : l o s o w a m e 0..-wl): 8 kierunek al[deg] (< 0 : losow a n i e 0..-al): 135 wiatr na 1500. m: p r ę d k o ś ć w h [ m / s ] ( 0 . .-5: lo s o w a n i e mnożnika),
175.25 175.30 1 75.35 175.40 1 75.45 1 75.50 P OZ I O M CELU
6. 2. 501. -2. 6. 1. 501. -2. 1. 6. 501. -1. 6. 500. -1. 1. 0. 500. 6. -1. 500. 6. -0. 0. OSIĄGNIĘTY, nacisnij ENTER
kierunek ah[deg] (=0, 0 -bez^ z a p i s u ) : 10 ^ ^ |--cx— |-dec/G
zapis na plik co n rec k r o k o w -t— |— 0.05 0.10 0.15
E- .| -1583. -1580. -1576.
n—
|—
2550. 2552. 2554.
U
|--vE-- |--vN-- |--vU-- |—
1500. 1500. 1500.
71.
5.00 -1376. 2587. 1414. 26. OTWIERANIE SPADOCHRONU, w y s okosc[m]: -1342. -1341. -1340. -1339. -1339. -1338.
2573. 2572. 2571. 2570. 2569. 2568.
1368. 1367. 1365. 1364. 1363. 1362.
19. 18. 17. 16. 16. 15.
... maksymalne przeciążenie 6.75 6.80
-1337. -1336.
2568. 2567.
1361. 1360.
14. 13.
0 .303 0.2 9 7 0. 2 9 0
1.006 4 .53 1 . 006 4 . 3 3 1 . 005 4. 1 4
41.
0. 1 2 6
1 .000
0 -80
37. 36. 35. 34. 33. 32.
0. 1 0 2 0 .098 0. 0 9 4 0.089 0.084 0 .078
1. 0 4 4 1.052 1.062 1 .074 1.087 1.102
3.56 3.87 4.15 4. 3 8 4.57 4.67
1 414.0
opór gwałtownie narasta 6.45 6.50 6.55 6.60 6.65 6.70
84. 82. 80.
-15. -15. -16. -17. -17.
31. 30.
0 .073 0 .067
1.118 1.136
4. 7 0 4 .65
współrzędna E [m]
Rys. 12. Rzut toru na płaszczyznę (E,N): początek w punkcie (-1587, +2548), koniec w punkcie lądowania (0,0); skale obu osi są oczywiście różne; punkty wykresu co 2 s. Dobrze widoczny jest punkt otwierania spadochronu. Generowanie wykresu: plot 'plik' u 3:4 title 'N(E)' w lp lt -1 lw 1 pt 12 ps 1
276
II. EL EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
I I
i >
Rys. 13. Pierwsze 15 s zrzutu, punkty co 0.5 s: prędkość efektywna (czyli prędkość względem ziemi) v lt), i prędkość pozioma vEJt), jako funkcje czasu; różnica tych dwóch krzywych jest mia rą prędkości opadania v„. Ładunek opuszcza samolot z prędkością 100 m/s, znacznie przekraczającąprędkość granicznąw tych warunkach, dlatego w pierwszych sekundach następuje gwałtow ne hamowanie, do ok. 40 m/s (deceleracja 4.5 G). Otwieranie spadochronu rozpoczyna się w 5. s, prędkość spada do ok. 26 m/s (deceleracja 4.7 G), a potem powoli zmniejsza się liniowo. Pamię tajmy, że prędkość efektywna to suma wektorowa prędkości opadania (5 m/s) i prędkości wiatru, którą na poziomie samolotu przyjęliśmy jako 30 m/s i która maleje liniowo z wysokością; końco wa efektywna prędkość lądowania wyniesie 9 m/s. Generowanie wykresu: plot 'plik' u 2:12 title 'Vef(t)' w lp lt -1 lw 1 pt 12 ps 1, 'plik' u 2:10 title 'Vhor(t)' w lp lt -1 lw 1 pt 6 ps 1
Z -II.1 .2 . F e lix B a u m g a r tn e r sk a cz e ze s tr a to s f e ry 14 października 2012 roku o godzinie 2 0 . czasu polskiego austriacki skoczek spado chronowy Felix Baumgartner opuścił kapsułę balonu stratosferycznego na wysokości 39 045 m [40]. Wiele milionów ludzi na całym świecie (ja także) obserwowało ten eks tremalny skok na żywo dzięki bezpośredniej transmisji telewizyjnej. Wpisując w prze glądarce internetowej hasło „Felix Baum gartner”, otrzymamy mnóstwo informacji, wiele pięknych zdjęć i różne filmiki. Dane z tych źródeł możemy podsumować nastę pująco: po 259 s swobodnego spadania skoczek przebył 36 529 m i osiągnął maksymaln ą prędkość 1342 km /h (373 m/s); otworzył spadochron na wysokości 2516 m n.p.m.; po dalszych 283 s wylądował w okolicy Roswell w Nowym M eksyku (332 m n.p.m., szero kość geograficzna ok. 33.38°N).
Z - I I . S k o k i , r z u t y i r a k ie t y , c z y l i d a l s z e z a b a w y z k o m p u t e r e m
277
Do modelowania tego skoku naszym programem potrzeba nam jeszcze paru danych nieco „osobistych” skoczka. Zacznijmy od wskaźnika BMI: cóż to takiego i po co to nam? BMI, czyli Body-to-Mass Index, to masa ciała [kg] dzielona przez kwadrat wzro stu [m], wielkość dobrze znana osobom dbającym o prawidłową sylwetkę: dla doro słego człowieka BMI powinien być w przedziale od 18.5 do 25 (powyżej zaczyna się nadwaga, a dalej - otyłość; ale np. skoczkowie narciarscy starają się trzymać poniżej dolnej granicy). Przyjmijmy więc dla dorosłego, wysportowanego mężczyzny wartość pośrednią 22. Jeśli przyjmiemy wzrost skoczka hs = 1.8 m, otrzymamy masę 71 kg; do tego należy jednak dodać m asę skafandra (typu kosmicznego) i wszelkiego dodatkowe go wyposażenia, więc rozsądne będzie przyjęcie pełnej masy skoczka jako 120 kg. Po wierzchnię odniesienia S m ożna oszacować jako od 14 * h 2 dla pozycji skoczka z roz łożonymi szeroko rękami i nogami (jak robi się przy akrobacjach spadochroniarskich), do 1/8 * h f przy spadaniu pionowo, głową w kierunku ruchu: dla wzrostu 1.8 m daje to wartości [m2] od 1.6 do 0.4. Pozycja skoczka w trakcie długiego spadania ulegała zmia nie (widać to na relacji filmowej, w pewnej fazie lotu skoczek wpadł w niebezpieczne rotacje, z których udało mu się wyjść, manewrując ciałem): pozostaje więc przyjąć zno w u wartość pośrednią, np. 1 m 2. W spółczynnik oporu kształtu cx0 trzeba dobrać, eks perymentując; na pewno powinien być on większy od typowych dla pocisków warto ści z przedziału 0.1-0.15. Przy wartości cx0= 0.65 (Listing Z-II-12a) po 259 s spadania, wysokość to 2505 m, prędkość 62 m/s. Pełne otwarcie spadochronu następuje na wys. 2366 m (przeciążenie 6.7 G); uzyskana prędkość maksymalna na wysokości ok 30.5 km: 310 m/s = 0.99 M a jest jednak za mała. Prędkość m aksym alną ok. 373 m/s (1.216 Ma, wysokość ok. 28 km) możemy odtworzyć, wybierając cx0 = 0.28 (Listing Z-II-12b); przy tej wartości na poziomie 2516 m jesteśmy jednak ju ż po 191 s spadania (prędkość 97 m/s); po 259 s spadania bylibyśmy tak nisko, że konieczne byłoby awaryjne otwarcie spadochronu ju ż po ok. 211 s, na wysokości 631 m (przeciążenie 10.7 G, koniec otwiera nia na 447 m). To wskazuje, że w naszym prostym modelu trzeba by wprowadzić jesz cze jed n ą zależność, S od wysokości, co nie jest trudne, zostawiam to dociekliwym; stosowany w aerodynamice lotniczej param etr D (drag) jest iloczynem S * cx, i to on jako całość często jest dobierany. Ale przyczyny trudności w dokładnym wymodelowa niu tego skoku m ogą być głębsze: nasz „uniwersalny” model atmosfery jest zapewne zbyt prymitywny. Termodynamika atmosfery zależy istotnie np. od zawartości w niej pary wodnej, czego w naszym modelu nie ma; w NASA stosuje się kilka różnych mode li atmosfery, zależnie od warunków klimatycznych. Spadochron o średnicy 4.2 m nieźle modeluje ostatnią fazę skoku i lądowanie z prędkością ok. 7 m/s w 536 s; przesunięcie Coriolisa w czasie spadania swobodnego to 73 m E (Rys. 14).
II. E L EM EN TY PR OGR A M OW A N IA O BIEK TO W EG O
278 L isting Z -II-12a
W CO CHCESZ SIE Z A BAWIĆ? rakieta(r), s p a dochron(p), r z u t (i n a c z e j ): p szerokosc geo[deg](>90: g stale); w y s o k o s c n.p.
262.75 262.80 2 62.85 skoku i ł a d o w a n i a [ m ] :
g: start, cel: 9.67548 9.79494 okres rota c j i [ g o d z . ] (0-bez e f e k t ó w Coriol i s a ; 24 - Ziemia): 24 masa początkową, k o n c o w a [ k g ] ( < = 0 je ś l i nie rakieta): 120 0 start: wspolrz e d n e [ m ] E, N: 0 0 start: predko s c ( > 0 : [ m / s ] ; < 0 : [ k m / h ] ) ;
I
N
I
U
|-
0
t — |— 0.00 0 .00
0.05 0 .10 0.15
0.
-1.
0.001
30594. 30579. 30563. 30548. 30532.
1. 1. 1. 1. 1.
-0 -0 -0 -0 -0
-310. -310. -310. -310. -310.
310. 310. 310. 310. 310.
0. 9 9 0 0. 9 9 0 0.990 0. 9 9 0 0. 9 9 0
1.625 1.625 1.625 1.625 1.625
1.03 1.04 1 .04 1.04 1.04
50.10 50.15 50.20 50.25 50.30
258.95
2505.
0.
-62.
62.
0 . 187
0. 6 5 0
1.03
2502. 2498.
0. 0.
-0 2504. -0 -0
-62. -62.
62. 62.
0.187 0 .187
0.650 0. 6 5 0
1.03 1.03
191.00 191.05 191.10
-52. -50. -48. -46. -43. -41. -38. -35. -33. -30. -27. -25. -23. -21. -19. -18.
52. 50. 48. 46. 43. 41. 38. 35. 33. 30. 27. 25. 23. 21. 19. 18.,
0 .160 0. 7 6 2 4. 2 7 0.783 4 . 7 3 0 .156 5.20 0. 1 5 0 0 .806 5.65 0.1 4 4 0.833 6.05 0. 862 0.137 0. 1 3 0 0 .895 6.37 0 .930 6. 5 9 0 .122 6.68 0. 1 1 4 0.967 6.64 1.006 0.105 6 .46 1.045 0.097 1. 0 8 4 6. 1 7 0 .089 5.80 1.123 0.082 5.38 1. 160 0.075 1.195 4. 9 3 0. 068 1.228 4. 4 9 0 .062 1.257 4. 0 7 0 .057
-0.
1.439 1.439 1.440
1.06 1.05 1.04
73. -0. 73. -0. -0. 73. OSIĄGNIĘTY, 73. -0.
333. 332. 332.
0. 0. 0. ENTER 0.
-0. -0. -0.
-7. -7. -7.
7. 7. 7.
0.021 0.021 0.021
1.440 1.440 1.440
1.00 1.00 1.00
-0.
-7.
7.
0.021
1.440
1.00
nacisn ij 332.
E-—
|— 0. 0. 0.
N-- i 0. -0. -0.
-U— 1 - ■vE— |— vN-- I - - V U - | - - - V - I - V M - I 0. 0. -0. 0. 0.000 39045. 0. -1. 1. 0.001 39045. -0. 0. -0. -1. 1 . 0.003 39045.
- C X - I - dec/G 0.280 0.00 0.280 0.00 0.280 0.00
22. 22. 22. 22. 22.
-0. -0. -0. -0. -0.
-0. -0. -0. -0. -0.
27912. 27893. 27875. 27856. 27837.
-374. -374. -374. -374. -374.
374. 374. 374. 374. 374.
1.214 1.214 1.214 1.214 1.214
0.698 0.698 0.698 0.698 0.698
0.99 1.00 1.00 1.00 1.00
0.289 0.289 0.288
0.281 0.281 0.281
1.05 1.05 1.05
na tej wysokości wi prawdziwym skoku otwierany jest spadochron
72. 72.
-0. -0.
szybkie narastanie przeciążenia 260.55 260.60 260.65 260.70 260.75 260.80 260.85 260.90 260.95 261.00 261.05 261.10 261.15 261.20 261.25 261.30
0.024 0.024 0.024
rejon maksymalnej prędkości
rejon maksymalnej prędkości:
259.00 259.05
8. 8. 8.
wszystkie dane jak: w Z-II-12a, oprócz poniższych:
44.20 44.25 4 4.30 4 4.35 44.40
39045. 39045.
-8. -8. -8.
p o wierzchnia ciała[m''2], c x 0 , cxp(0=>1.44) : 1 .28 0 spadochron: srednica[m], czas otwarcia[s]: 4.2 259
0. 6 5 0 0. 6 5 0
0. -0.
-0. -0. -0.
W CO CHCESZ SIE ZABAWIĆ?
0. 1.
0. 0.
0. 0. 0.
Listing Z-II-12b
0 -0
0.05 0 .10
2367. 2367. 2366.
azy m u t ( > = 0 ; < 0 : [ t y s ] ) ; e l e w a c j a (-90..90):
0 0 -90 powierzchnia c i a l a [ m A 2], cx0, c x p ( 0 = > 1 . 4 4 ) : 1 .65 0 spadochron: srednica[m], czas o t w a r c i a [ s ] : 4 . 2 259 powierzchnia spad o c h r o n u [mA 2]: 2 7.71 w arunki meteo na w y s o k o s c i [ m ] : 332.0 t e m p e r a t u r a [C e l s ] ( 0 = > 2 0 C) i c 3:brak; < 0 : losow a n i e 0..-■wl): 0 w i atr na 332. m: pr ę d k o ś ć wl ), V e rlet(-l..90: g stale); wysokosc n.p.tn. skoku i l a d o w a m a [ m ] . 56 5000
446.
1451.
446.
1450.
446. 446. 446. 446. 446. 446.
1449. 1447. 1446. 1445. 1444. 1444.
0. 0. 0.
446. 446. 446.
1431. 1430. 1430.
271.70 271.75 271.80
271.85
g- start, cel: 9.80048 9.81508 okres rotacji [ g o d z . ] (0-bez e f e k t ó w C o riolisa; 24 - Ziemia): 0 masa początkową, k o n c o w a [ k g ]( 0 : [m/s];=0; 1 . 4 4 ) : 1 .7 0 spadochron: srednica[m], c zas o t warcia[s]: 5 60 powierzchnia spad o c h r o n u [mA 2]: 39.27 warunki m e teo na w y s o k o s c i [ m ] : 270.0 te m p e r a t u r a[C e l s ] ( 0 = > 2 0 C) i c i s n i e n ie [ h P a ] ( 0 = > 1 0 0 0 ) : 0 0 wiatr na 270. m: pręd k o ś ć w l [ m / s ] ( 0 :brak; < 0 : l o s o w a m e 0 ..-wl): 0 integrator, param, beta: E u l e r ( l . . 0 ) , V e r l e t ( - l . . < 0 ) ; k r o k h: 1 .05 zapis na plik co nrec kr o k o w ( n rec>= 0 , 0 -bez zapisu): 20 t I— N — I— U — |— v E — |— v N — — v U — |— v — | - v M — |— c x — |-dec/G 0.05
0 10
0. 0.
5.
10 .
4995. 4990.
0. 0.
96. 95.
'duże przeciążenie priy opuszczaniu samolotu^ ^ 2'30
0.
1 8 l f mkST
162.
4817.
OSP: « d° M i .
1900
0 -.
430.
59.95
0.
446.
0.
e.
0 '. 60.15
...
0.
“ r 446! 446.
T
^
6155 £ « Sil" S'70
0 0
0' 0. 0.
446. 446 446 Ta I. 446.
^
> ^
^
5.09 5.03
^
-69.
8 6 . 0. 2 6 6
0.702
1.98
3812.
0.
3. 3.
-59. -59.
59. . . 1 7 » 59. 0. 1 7 9
0.790 0.700
1.« 1 .02
1539.
0.
0-
-52. -52.
52. 52.
0.155 0. 1 5 5
0.700 0.700
1.02 1.03
-52. -52. -52.
52. 52. 52.
0.155 0.155 0.155
0 . 7 0 0 1 .03 0 . 7 0 0 1 .03 0 . 7 0 0 l.
= 0 ; < 0 : [ t y s ] ) ; e l e w a c j a ( - 9 0 . .90):
-vM--||- - V E ~ | - - V N - - |--vU--|-— V — | ■ | ~ -M — 1 " -U— 0 34. 44. 0.130 0. 1. 81. 28. 0 34. 44. 0.129 0. 2. 82. 28. 0. 0.
57. 57.
4 .72 0. 100. 4.74 0. 101. P OZIOM CELU OSIĄGNIĘTY,
108. 108.
0 0
0 80. 80. 0 nacisnij ENTER
-cx--|-dec/G 0.150 0.150
1.59 1.56
21. 21.
0. -0.
21. 21.
0.061 0.061
0.150 0 . 150
0.35 0.34
14. 14.
-20. -20.
24. 24.
0.071 0.071
0.150 0.150
0.47 0.47
Z - II .2 .2 . S trz e la m y n a T o r u ń s k im P o lig o n ie A r ty le r y js k im ( n a js ta rs z y m n a św iecie)
wiatr na 80. m: p r ędkość w l [ m / s ] ( 0 : b r a k ; < 0 : l o s o w a n i e 0.. -wl): 0 integrator, p a ram . beta: E u l e r ( l . . 0 ) , V e r l e t ( - l . . < 0 ) ; k r o k h : 1 .02 zapis na plik co nrec k r o k o w (nrec>=0, 0- b e z zapisu): 5 0.02 0.04
0. 0.
1. 2.
80. 81.
0. 0.
49. 49.
18. 17.
52. 52.
0.155 0.153
0. 1 5 0 0 .150
2.25 2 .20
1.42 1.44
0. 0.
56. 56.
92. 92.
0. 0.
32. 32.
0. -0.
32. 32.
0.093 0.093
0. 1 5 0 0. 1 5 0
0 .81 0.80
22. 22.
-14. -14.
26. 26.
0.076 0. 0 7 6
0. 1 5 0 0. 1 5 0
0. 5 4 0.54
0. 80. 99. 0. 0. 80. 100. 0. POZIOM CELU OSIĄGN I Ę T Y , n a cisnij E NTER
E—
Tutaj także prędkość została zmniejszona z 47 m/s do 45, z takich samych jak wyżej po wodów.
53 0 20 bez oporu: zasięg, w y s o k o sc[km], k i e r u n e k (E,N) celu: 0.18 4 0 1 0.31592 0.00000 0.18 4 0 1 k aliber[mm](>0) lub p r z e k r ó j [ m A 2](90: g stale); w y s o k o s c n.p.m.
s tartu i celu[m]. 53 50 50
g: start, cel: 9.81 3 1 9 9.8 1 3 1 9 okres rotacj i[godz.] (0-bez e f e k t ó w Coriol i s a ; 24 - Ziemia): 24 masa początkową, k o n c o w a [ k g ] ( < = 0 j eśli n ie rakieta): 6 .2 0 start: wspolr z e d n e [ m ] E, N: 0 0 cel: odleglos c + a z y m u t ( o ) ; w s p ó ł r z ę d n e EN(w); n i e o k r e s l o n y ( m a c z e 3 ): n start: predk o s c ( > 0 : [ m / s ] ; < 0 : [ k m / h ] ) ; a z y m u t (>=0;0) lub p r z e k r ó j [ m A2]( 2 0 C) i c i s n i e n i e [ h P a ] ( 0 = > 1 0 0 0 ) : 0 0
291
Z-II.2. Rzuty
w i a t r na 50. m: p r ędkość wl[m/s](0:brak; < 0 :losowanie 0..-wl): 0 integrator, param, beta: Euler(l..0),Verlet(-l..=0, 0-bez zapisu): 10 - U — ]. V E - I - v N — |— -vU— |— — v — - V M - I - - C X - I - dec/G ---1 — |--- E- — 541. 407. 677. 1.986 0.286 5.86 70. 0. 0.05 0. 27. 674. 1.977 0.286 5.80 0. 539. 405. 0. 54. 91. 0.10 2 3.50 2 3.55 23.60 23.65 52.70 52.75 52.80 52.85 P OZIOM CELU
5. 5. 5. 5.
|
-u— |"
1-
7624. 7636. 7648. 7659.
27. 13223. 27. 13230. 13237. 27. 13244. 27. OSIĄGNIĘTY,
3673. 3673. 3673. 3673.
0. 0. 0. 0.
78. 1. 67. 1. 56. 1. 1. 45. nacisnij ENTER
234. 234. 233. 233.
1. 0. -0. -1.
234. 234. 233. 233.
0.711 0.711 0.710 0.710
0.196 0.195 0.195 0.195
0.33 0.33 0.32 0.32
145. 145. 144. 144.
-220. -220. -220. -221.
263. 263. 263. 264.
0.769 0.769 0.769 0.770
0.226 0.226 0.226 0.226
0.69 0.69 0.69 0.69
Z -iI.2.2.1. Strzela się na ogół do jakiegoś konkretnego celu... Skoro wiemy już, jak wygląda numeryczna charakterystyka naszego działa, możemy przystąpić do realizacji konkretnych „zadań bojowych”. A więc otrzymujemy np. roz kaz „zniszczyć gniazdo enpla znajdujące się w odległości 5 km, azymut 29-87”: enpel to fonetyczna wersja używanego w wojsku powszechnie akronimu NPL, czyli „nieprzyja ciel”; a cóż to za liczba „29-87” (wymawia się to szybko: dwadzieścia-dziewięć osiemdziesiąt-siedem)? Otóż w wojsku tradycyjnie kąty m ierzy się w tysięcznych (formalnie, 1 tysięczna [tys] to kąt, pod jakim odcinek 1 m widzimy z odległości 1 km; to naprawdę ułatwia życie i ocenę odległości, podziałka w tysięcznych jest też w każdej profesjonal nej lornetce): kąt pełny, 2n [rad] = 360° [deg] = 60-00 [tys] (tradycyjny system niemiec ki, przyjęty też w Układzie Warszawskim) lub 64-00 (aktualny system NATO); 1 [tys] s 0.06/0.05625° jest techniczną granicą dokładności nastawiania kąta w artylerii. A więc np. azymut NE to 07-50/08-00, E to 15-00/16-00, NW to 52-50/56-00 itd. W naszym programie azymuty (tylko!) możemy podawać także w [tys] wg aktualnego standardu NATO (jako liczby ujemne, bo przyjęliśmy konwencję, w której azymut jest nieujemny; elewacje m ogą być dodatnie lub ujemne, dlatego podajemy je zawsze w stopniach); przykładowy azymut 29-87 podamy jako -2987 lub jako 168°. Wracając do otrzym ane go zadania: trzeba tylko ustalić kąt elewacji, dla którego zasięg poziomy wyniesie 5 km. Odpalamy komputer, z naszym lub podobnym programem, i wykonujemy w nim kilka „strzałów symulowanych”, żeby znaleźć właściwą wartość; kiedyś służyły do takich ob liczeń odpowiednie tablice czy kalkulatory artyleryjskie. W naszym programie położe nie celu możemy określić na dwa sposoby: albo podając odległość i azymut (co jest pro ste i intuicyjne), albo podając współrzędne (E, N) względem naszego stanowiska, skąd
II. EL EM EN TY P R OGR A M OW A N IA OBIEK T O W EG O
292
odległość i azymut program sobie obliczy; poziom celu ju ż podaliśmy wcześniej, więc jeśli trzeba, to i elewację celu (to nie to samo, co potrzebna nam elewacja lufy!) też moż na łatwo obliczyć. Po kilku próbach znajdujemy, że przy elewacji 4.88° nasze działo ma zasięg poziomy ok. 5 km, a tor pocisku ma maksimum na poziomie 180 m. Uwaga: jeśli cel został określony, nasz protokół ekranowy zmienia nieco postać: po współrzędnych E, N, U, i składowych prędkości vE, vN, vU, podawane są: składowe dpE, dpN odległości pocisk => cel, całkowita odległość (dist, w trzech wymiarach), i azymut (az/t) start => cel (gdy cel jest nieruchomy) lub pocisk => cel (gdy cel porusza się) - w tysięcznych. Listing Z-II-221a W C O C HCESZ SIE Z A BAWIĆ? rakieta(r), s p a dochron(p), rzut( i n a c z e j ) : i szerokosc g e o [ d e g ] (>90: g stale); w y s o k o s c n.p.m.
s t artu i celu[m]: 53 50 50
g: start, cel: 9.81 3 1 9 9.8 1 3 1 9 okres r o t a c j i [ g o d z . ] (0-bez e f e k t ó w C o r i o l i s a ; 24 - Ziemia): masa początkową, k o ń c o w a [k g ] (0: [m/s]; = 0 ; < 0 : [tys]); elewac;ja(-90. .90):
680 -2987 4 .8 8 bez oporu: zasięg, w y s o k o sc[km], k i er u n e k (E,N) celu: 8.00411 0.25176 1.66159 - 7 .82974 kaliber[mm](>0) lub p r z e k r ó j [ m A 2]( 2 0 C) i c i s n i e n i e f h P a ] ( 0 = > 1 0 0 0 ) : 0 0 w i atr na 50. m: pręd k o ś ć w l [ m / s ] ( 0: b r a k ; < 0 : l o s o w a n i e 0..-wl): 0 integrator, param, beta: E u l e r ( l . . 0 ) , V e r l e t ( - l . . < 0 ) ; kr o k h: 1 .02 __ -li b Lrrtrt-LrAiH (nnorS-Cł ft-hP7 7ani ! 50 |- - V E - I - -vN--|- -vU--|- - d p E - | -dpN--|-dist|az/t| . - t - l - - E — |-- - N---1— ■U — 58. 1 0 35.1 -4877.8 4 9 8 6 2987 -662. 140. 51. -13. 3. 0.02 1032.3 -4864.6 4 9 7 2 2987 57. -661. 140. 52. -26. 6. 0.04 46 5 . 0 - 2187.6 46 3 . 1 -2178.3
2 240 2 987 2 230 2987
4 .6
-8.8
10 2987
3 .2
- 1 .8
3 2987
1.7
5 .2
5 2987
573. 575.
-2703. -2713.
182. 182.
99. 99.
-467. -467.
0. -0 .
10.28
1033.
-4882.
52.
74.
-350.
-46.
1 0 .3 0
10 3 5 .
-4889.
5 1.
74.
-3 5 0 .
-4 6.
50.
74.
-350.
-47.
49.
74.
-3 4 9 .
-4 7.
0 .2
-4896. 1036. 10.32 o dległość do c elu ROŚNIE!
1 0 .3 4
1038.
-4 9 0 3 .
odległość do c elu ROŚNIE! P OZIOM CELU O S I ĄGNIĘTY, n a cisnij E NTER
No, ale może zdarzyć się, że jesteśm y nie na nizinnym poligonie toruńskim czy (słyn nym) drawskim, ale w otoczeniu wzgórz ponad 180-metrowych, a enpel rezyduje gdzieś za tym i wzgórzami. Musimy więc zastosować strzelanie stromo-torowe: pamiętamy, że bez oporu powietrza zasięg (20) dla elewacji rj i 90-7/ jest identyczny; przy oporze powie trza jest tak tylko w przybliżeniu. Skoro nie mamy właściwej do takiego zadania haubicy (czyli działa do strzelania stromo-torowego), musimy dyszel oporowy naszej „połówki” nieco wkopać w ziemię (robi się takie rzeczy w warunkach bojowych). Przy sumarycz nym kącie elewacji 80° otrzymujemy ten sam zasięg ok. 5 km i wysokość toru 9 km; czas lotu pocisku wzrasta jednak ponad ośmiokrotnie. Program analizuje, czy zbliżamy się do celu: jeśli tak nie jest - sygnalizuje to, ale zostawia nam decyzję, czy chcemy kon tynuować symulację. Przy strzelaniu stromo-torowym początkowo od celu oczywiście oddalamy się, bo rośnie wysokość; dopiero gdy miniemy maksimum i pocisk zaczyna spadać - do celu zbliżamy się; efektywna celność jest jednak znacznie niższa. Przesu nięcie Coriolisa na takiej drodze jest też inne, nawet zmienia znak z wysokością (bo za leży od kąta, jaki tworzy tor z osią obrotu Ziemi), dlatego wybraliśmy nieco inny azy mut dla tego strzału (-2980); zob. Rys. 17.
24
start: wspolr z e d n e [ m ] E, N: 0 0 cel: o d l e g l o s c + a z y m u t (o ) ; w s p ó ł r z ę d n e EN(w); n i e o k r e ś l o n y ( i n a c z e j ) : o odleglosc(> 0: [ m ] ; < 0 : [ k m ] ) ; a z y m u t (>=0.0;< 0 : [ t y s ] ) : -5 -2987 współrz ęd ne ENU celu[m]: 103 8 . 0 - 4891.1 50.0 prędkość celu ( > 0 : [ m / s ] ; < 0 : [ k m / h ] ) ; k i e r u n e k ruchu( > = 0 ; < 0 : [ t y s ] ) : 0 0
4.88 4.90
293
Z-II.2. Rzuty
W CO CHCESZ SIE ZABAWIĆ? rakieta(r), spadochron(p), r z u t ( i n a c z e j ) : i szerokosc g e o [ d e g ] (>90: g stale); wysokosc n.p.m. startu i celu[m]: 53 50 50 g: start, cel: 9.81319 9.81319 okres r o t a c j i f g o d z . ] (0-bez e f e któw Coriolisa; 24 - Ziemia): 24 masa początkową, k o ń c o w a [k g ] (0:[m];=0.0;0:[m/s];=0;0:[m/s];=0;0) lub pr z e k r o j [ m A 2]( 2 0 C) i c i s n i e n i e [ h P a ] ( 0 = > 1 0 0 0 ) : 0 0 wi atr na 50. m: prędkość w l [ m / s ] (0:brak; 90: g stale); wysokosc n.p.m. startu i celu[m]: 53 50 50 g: start, cel: 9.81319 9.81319 okres rotacji[godz.] (0-bez e f e któw Coriolisa; 24 - Ziemia): 24 masa początkową, k o n c o w a [ k g ] ( 0 : [ m ] ; < 0 : [ k m ] ); a z y m u t ( > = 0 . 0 ; < 0 : [ t y s ] ) : -5 -2987 w s p ó ł rzędne ENU celu[m]: 1038.0 -4891.1 50.0 prędkość c e l u(>0:[m/s];=0;0:[m/s];=0;0) lub p r z e k r o j [ m A 2]( 2 0 C) i c i s n i e n i e [ h P a ] ( 0 = > 1 0 0 0 ) : 0 0 w iatr na 50. m: prędkość wl[m/s](0:brak; cel odchyla się od toru już o prawie 90°, gdy kąt ten zostanie przekroczony, pojawia się komunikat MINĄŁEŚ CEL! Biorąc pod uwagę promień rażenia głowicy bojowej (244 m), jest to zbliżenie wystar czające do poważnego uszkodzenia celu (co istotnie nastąpiło: samolot U2 nie został bezpośrednio trafiony); zob. Rys. 26.
310
II. E L EM EN TY PROGR A M OW A N IA OB IEK T O W EG O
311
Z-II.3. Rakiety
Listing Z-II-33c 34.30 W CO C HC E S Z SIE ZABAWIĆ?
dotąd dane identyczne jak dla Z-II-33a, Z-II-33b cel: odleglos c + a z y m u t ( o ) ; w s p ó ł r z ę d n e EN(w); n i e o k r e s l o n y ( i n a c z e j ) : w współrzędne (E,N) celu w z g l e d e m startu[m]: 1 5 0 0 0 -6650 cel: odległoś ć [ k m ] , a z y m u t : 16 . 4 1 11 3 . 9 1 elewacja celu: 50.25 prędkość celu ( > 0 : [ m / s ] ; < 0 : [ k m / h ] ) ; k i e r u n e k r u c h u ( > = 0 ; < 0 : [ t y s ] ): -690 0 składowe ENU pr ę d k o ś c i celu[m/s]: 0.000 191.667 0 .000 start: predko s c ( > 0 : [ m / s ] ; < 0 : [ k m / h ] ) ; a z y m u t ( > = 0 ; < 0 : [ t y s ] ) ; e l e w a c j a ( - 9 0 . .90): 0 90 86.4 k aliber[mm](>0) lub p r z e k r ó j [ m A 2]( 1 0 .0 0 maksymalne zbliżenie 3 4 .3 1 15101. -1 5 3 . 1 9900. 1 126. -4 7 . 1331. - 1 0 0 .6 r e d u k c ja k a ta 8 6 .8 2 -> 1 0 .0 0 -4 7 . 1331. -1 1 1 .9 3 4 .3 2 1 5112. -1 5 3 . 1 9913. 1126. o d le g ło ś ć do c e lu ROŚNIE! 1 0 .0 0 MINALES CEL! re d u k c ja k a ta 9 2 .9 5 -> 3 4.33
15123.
-154.
19927.
1127.
-47.
1332.
-123.2
76.4
163 1605
7 8 .7
162 1605
8 1 .1
163 1604
83.5
165 1604
41.667
4 .245 rakieta może start o w a ć p od k a t e m > 1 2.9 czas wlaczeni a sterowani a [ s ] ( < 0 : bez s t erowania); m a k s y m a l n y kat s t e r o w a n i a [ d e g ] ( > 0 ) : 30 10 warunki m eteo na w y s o k o s c i [ m ] : 270.0 t e m p e r a t ura[C e l s ] ( 0 = > 2 0 C) i c i s n i e n i e [ h P a ] ( 0 = > 1 0 0 0 ) : 0 0 wiatr na 270. m: prędk o ś ć w l [ m / s ] ( 0 : b r a k ; < 0 : l o s o w a n i e 0..-wl): 0 integrator, param, beta: Eu l e r ( l . . 0 ) , V e r l e t ( - l . . < 0 ) ; k rok h: 1 .01 zapis na plik co nrec kro k o w (nrec>=0, 0- b e z zapisu): 10
t - - | — E— | — N— | — U—
0.01 0.02
|--vE — |--vN--|--vU-- | — dpE— | - d p N - - | - d i s t | a z / t |
0. 0.
-0. -0.
270. 270.
0. 0.
-0. -0.
p o z i o m 10 k m 24.81 6776. 24.82 6783.
-14. -14.
9993. 10001.
653. 653.
-2. -2.
814. 814.
29.99
-27.
14799.
891.
-3.
1052.
4252.4
-874.6
6 7 7 4 1 661
-27. -27.
14809. 14820.
892. 892.
-3. -3.
1053. 1053.
4243.5 4234.5
-872.7 -870.7
6 7 6 0 1661 6 747 1 660
10748.
0. 150 0 0 . 0 -6648.1 25 6 6 0 20 2 4 1. 150 0 0 . 0 -6646.2 2 5660 20 2 4 8 2 2 3 . 8 -1880.6 1 3 088 1727 8 2 17.3 - 1 878.7 13 0 7 7 1 727
współrzędna E [m]
STEROWANIE WŁĄCZONE 30.00 30.01
10757. 10765.
kierunek rakieta -> cel zaczyna odchylać się (w 3 wymiarach!) od trajektorii o więcej niż 10° 33.85 redukcja 33.86 redukcja 33.87 redukcja
14588. kata 14599. kata 14610. kata
19297. 1102. -131. 1 0.00 10.09 -> 19310. 1103. -131. 1 0.00 1 0.30 -> -132. 19323. 1103. 1 0.00 10.51 ->
-48.
1293.
4 11.7
■31.3
-48.
1294.
4 00.7
-28.9
-48.
1295.
389.7
-26.5
odległość 15 km (E) została przekroczona - rakieta musi ostro zawracać! 15011. kata 15022. kata
19794. 1122. -149. 1 0.00 4 6 . 3 8 -> 19807. 1122. -149. 1 0.00 50 .07 ->
-47.
1324.
-10.7
59.6
-47.
1325.
-21.9
62.0
34.29 15078. redukcja kata
19874. 1125. -152. 10.00 7 4.72 -->
-47.
1329.
-78.1
74.0
34.23 redukcja 34.24 redukcja
815 1611
Rys. 26. Ostatnia sekunda lotu rakiety S-75 (w kierunku E) i fragment toru celu (w kierunku N): rzut obu torów na płaszczyznę (E,N), punkty co 0.1 s. Dodatkowo pokazano odległość rakieta-cel w funkcji współrzędnej E. Najmniejsza odległość to 162 m, głównie w pionie. Rakieta jest od 30 s lotu (współrzędne: E = 10757 m; U = 14809 m, tzn. ok. 5.2 km poniżej celu) naprowadza na na cel, jej tor nie jest więc równoległy do osi E, jednak kąt odchylania siły ciągu od osi rakie ty jest konstrukcyjnie ograniczany do 10°. Całkowite odchylenie toru rakiety od kierunku E jest wynikiem zarówno naprowadzania, jak i działania przyspieszenia Coriolisa. G e n e r o w a n i e w y k r e s u : plot 'plik' u 3:4 title 'rakieta' w lp lt -1 lw 1 pt 12 ps 1, 'plik' u 23:24 title 'cel' w lp lt -1 lw 1 pt 10 ps 1,'plik' u 3:21 title 'odległość r-c' w lp lt -1 lw 1 pt 6 ps 1
K O M PEN D IU M KONSTRUKCJI JĘZYK OW Y CH
Hierarchia typów danych/struktur:
Kompendium konstrukcji językowych
Kompendium zawiera tabelaryczne zestawienie podstawowych informacji o wszystkich konstrukcjach Pascala, C++ i Fortranu użytych w tej książce, z ich klasyfikacją, uzu pełnieniami i niektórymi rozszerzeniami dostępnymi w ram ach standardów tych języ ków. Pominięto: (obszerne) listy funkcji standardowych poszczególnych języków; ope racje na strukturach dynamicznych (listy, drzewa); bardziej zaawansowane koncepcje, takie jak typy wariantowe w Pascalu, metody w irualne, interfejsy, szablony itp.; dla pli ków uwzględniono tylko operacje konsolowe i podstawowe informacje dotyczące zapi su zredagowanego. Oznaczenia: DP - Delphi Pascal 7; LP - FPC/Lazarus; P - Pascal (DP lub LP); TP - Turbo Pascal C z c io n k a p o g r u b i o n a C o n s o l a s :
s ło w a
kluczowe i symbole języków, np.
b e g in + { ..
Fortran: c z c io n k a p o g r u b io n a C o n s o la s :
kursywa pogrubiona ConsoLas :
konstrukcje dostępne ty l k o w F9x konstrukcje F9x, dostępne także w kompilatorach GNU F77
kursywa Consolas: konstrukcje standardu A N SIF77, w ystępujące tak że w F9x i w GNU F77 Czcionka pogrubiona Arial Narrow: typy, sekwencje i inne konstrukcje językowe. T_fix, T_float, S J r u e ...
C z c io n k a C o u r ie r : Czcionka Times: typ
nazw a:
nazw y
własne zmiennych itp.
objaśnienia, komentarze itp. deklaracja zmiennej n a z w a typu typ
typy proste {simple
types)
| |
skalarne (scalar) | porządkowe (T_ord: ordinal) | | standardowe (standard) I I | | arytmetyczne stałopozycyjne (T_fiX: fixed point) | I I I calkowitoliczbowe (integer) | | | | | kardynalne (cardinal) | | | monetarne (currency) j | logiczne/boolowskie (TJog: logical/boolean) | | | | znakowe (character) wyliczeniowe/symboliczne (T_enum: enumarate/symbolic) j j j okrojone (T_Sllb: subrange) rzeczywiste - arytm. zmiennopozycyjne (T_float: floating-point) zgodne ze standardem IEEE 754; inne | wskaźnikowe (T_ptr: pointer) typy strukturalne {structural types) łańcuchy {string) zespolone (complex) homogeniczne: tablice (array) zbiory/mnogości (T_set: set) heterogeniczne: rekordy/struktury/typy pochodne (record/structure/derived type) struktury dynamiczne (listy, drzewa) pliki (file) podprogamy, typy proceduralne | funkcje (function) procedury (procedure) operatory: przeciążone (overloaded); definiowane/własne obiekty/klasy/moduły (object/class/module) Klasyfikacja instrukcji: proste
| | |
(simple statements)
przypisania (assignment statement) procedury (procedure statement) skoku (goto statement) strukturalne (structural statements) sekwencje (composed statement) selekcje (selections) | warunkowe (conditional/ifstatement) | przełączniki (case/switch statement) iteracje (loops) o nieznanej liczbie powtórzeń sterowane z góry (pre-controlled loop) sterowane z dołu (post-controlled loop) o znanej liczbie powtórzeń (for/do statement)
313
£ £ u II c c •H
te
X v1/1 1/1l »H
1
5
c 6 0
TJ ;v 01 c O
ro c X3 co in ro CS MD
O O III ■O 2 £
I
X 3
°
£ £
w 1 3
2 ^ £_ ro x u 3
8
X
O a E O U II
3 3u
1
2 '
'
CS
> u c
OJ
Ł. u
îfî
o O 2i £ rH
c roi § 0J X ai 'c ; >
b iii t
Xl X 3
£ ? III o 5
£ 0 o
Xi OJ9
c
U3
- s y m b o lic z n e
£ Q O O
wyliczeniowe
OJO
znakow e
O rH
O £
(U ■O ■H Ł- c:
logiczne (TJog)
Lgned short a w c h a rjt Lgned int a unsigned long
in t long
s■H
U U
iar a AnsiChar (1B) LdeChar 12B: UNICODI
+j i •H +j £0
m oo
OD1 c
(2B)0...65535
2
Ol
c 0 •H
jrdbool
CS
1
a long
MD CS
C3 O
1
m X OO
m onetarne
o P i Î5 g ■D o =£ C "5 « o U)
2 2
1
1 "co o CD ro c ">î X o N ■o O c Q OüO ■¡/! l/ł l/ł c 1 ° (/) 3 3 =
: long
i ro X O XI X ■W O u U) •H
O c Ł. Ł. OJ OJ
|
kardynalne -
w£ < «= I ■F W) 1 a> 3 c a (0 '> c o g N >> N N Û) O 1 X CL 0) O
la llin t iteger
k o m p e n d iu m :
k o m p e n d iu m :
Tabela 2/1
Tabela 2/2
314
315
T
i w I
m
1 £ 1 i ! E 13 3 ■o S
1 & II cl ; ■o +j
nazwa
definiowanie: type T_sub_nazwa = T_ord_p deklarowanie: var z okr :T sub nazwa
deklarowanie: var z symb :T enum
.. T_ord_k
< 0
B il Ci | oj
.2 §
§ § O ’T3
•H
1
I=1 s i
Ml
o
» ■o « ^ 1 > ° -a e oj a $ ’ o. > N g N » .'9 2 ^ -H
A *8
■F ’■§ fU Jg o 2 rH O
a e c c
8 S£ o v CQ ^
+a 3c £0 s? s 2 PQ H m
f/> v
■
cJ c —
ic¡)
l
j >> 3 a 3
i
5
Si
! S -a •rt N3 °C S L) r,
Hi) 3 ii -I :>i i i
I ?. u
double = real (6.5B m + 1.5B c) ( ~ ± 10'324...± 10+3°8) dokładność 15-16poz.
deklarowanie: T enum nazwa z_sym b Ja 0 u 5 3 CU a- s 7 iso IIIo 3 »A C v0 100 o H “-C/3 ■ * O ■ o 3 H^ fO 44 (U o III o £_ 'o C 41 II S A ŚP £
\
(d n
ru >
S C co N *g, N 2 2 i
1«, t-h id C3 1 1 s N in i fi € CL i - £r 3 ----
fi
5
i
1 1
1t l C1S1 L 111?
"cl ^
a
1 'd'
Tabela 2/4
05 a a
k o m p e n d iu m :
N 1 O o CU44 »n cl>
u a ■c i
S S
3
S * (0 I a6 N
li ■8^ I o
S CN
ia a
| ^
■§ 5 g C' u óS S 3 2 i -a N fi fl
¡5
S S M S § «
“ii ii f i 1 1 f s
aa
•3 € I
1 o
i
S n
a s a
I
S i € (13 $
!i
. . :§ t:
S* >1
i 1
V lii]
317
316
K O M PEN D IU M : T ab e la
K O M PE N D IU M : T a b e la
2 /5
2 /6
G\ CO
a * I .i
g 05 a
1 &
g
S * fi § '§
S 5
1 1#
ci i
•-H a, ,I & c3 & B « M *> Ł * ^
6 ■£
1
8B
I g
£ y - g |f .§ c c S I 8 §
!
i S S p |° f T3|
■g ,—, +J III O
cd *H cd
? ^|
*4! i
11,1frc!
1 §
cca ^C cd q «
'
« e S 2 "aj i i » I # T3 I— S
5 -I '3 jl 'S ¥
..
S
* S?
N Hi ¥
I -JP J ft n I
fi N 3 "Ł 5 5
li fi
s -g
i
|
-o i 9 . . Si rt) »3 Cd r§ g S -a S 'V
M
8 s «= a t
•§
3 J3 & C' a ■**! S £
« cd a
£ -8
> . >v ni m Ł. Ł.
£- L.
■o 4j m et
* ij 2 a i ■s m ra cd
1
320
Tabela 2/8
* •* ^
"8 £
K O M PE N D IU M :
k o m p e n d iu m :
Tabela 2/7
es ’3 d p~* § ! S •• O. -a
I l t i - 11
O O)
J i® g a ’S .tf *• ^ | 5 .2 5 %
y& H "O, ' "N "°|
£ '!
l | |
11
r- 5 fi
1 £• £ M
l&l l i
£>.5 S ,0| |
1 ”,
iti
o ' 1 - Ti v
*K £
4-1 £
0). -i?
'cT S (U ^
Ij gI
E S
® ,.ą
O
w :
— Q) d o c £
|1
>m o
¿ ,2 c* £
C Tl
Ti. .1
l i ¥ ?
3 &
l!; i R
li
O
.» s* ! I l^ ii
I» a T , J"3 J V>> “fti -’“I a g i
ti §
£ £o *o £
sil Sil
1 il
2 tn o. g ,2, ° a» -6
2 * ! •-
Iii
H
* £ ", •- ’i
■^5
.a CJ
lf
CM ••
l i i .J i « L (U, J Kr pl < 1 *i 3O i— ' 'Ii i i s- L. (D T3 ”o l, Cu d)i H i i- -2* a I
O -H A
§ S 1 . s -a t-'^g -Q
‘ S 1 35 °l
sh
i diii £ 3
._