2.2. Ett enkelt Fortran-program Vi kommer nu att gå in på grunderna till programmering, och väljer Fortran som programmeringsspråk, dels för att det är det mest använda för numeriska beräkningar i fysik och teknik, och dels för att standarden nyligen moderniserats. Vi kommer här att följa den nya standarden, Fortran 90 (Fortran 95 skiljer sig endast obetydligt från Fortran 90), och endast tillfälligt beröra den äldre standarden, FORTRAN 77, som fortfarande användes. I föregående avsnitt har vi redan i princip beskrivit hur man går till väga för att skriva ett program. Det är viktigt att specificera problemet så väl, att det blir lätt att koda. En enkel programkod är också lättare att felsöka. Sedan man bortskaffat alla syntaktiska fel efter kompileringen, skall programmet länkas och testas. Fel som upptäcks vid testningen, rättas genom att källkoden ändras. Detta är oftast den mest krävande delen av programmeringen. Vi skall börja med att studera ett enkelt exempel på ett Fortran-program, som har till uppgift att beräkna medelpunkten och radien för en cirkel, som går genom tre givna punkter. Programmet är skrivet med en sträng Fortran 90 syntax, vilket innebär, att det kan kompileras med en Fortran-kompilator (t.ex. ELF90 från Lahey och F från Fortran Company (g95)), som inte kan tolka äldre syntax. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 1
En fri kompilator för Fortran 95/2003 är GNU Fortran, som även kan installeras i Windows (se http://gcc.gnu.org/wiki/gfortran). Den översätter också FORTRAN 77-kod. Vi skall först se på huvudprogrammet, som läser in punkter, anropar en subrutin, som beräknar medelpunkten och radien, och slutligen skriver ut resultatet. PROGRAM cirkel IMPLICIT NONE! Detta program beräknar medelpunkten och radien! för en cirkel genom tre givna punkter! Deklarationer REAL :: x1,y1,x2,y2,x3,y3,x0,y0,r INTEGER :: ier! Explicit interface (elf90) interface subroutine calcrk (a1, b1, a2, b2, a3, b3, x, y, r, i) implicit none real, intent(in) :: a1, b1, a2, b2, a3, b3 real, intent(out) :: x, y, r integer, intent(out) :: i Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 2
end subroutine calcrk end interface! Läs in punkterna PRINT *, "Skriv koordinaterna för tre punkter" PRINT *, "i ordningen x1,y1,x2,y2,x3,y3" READ *, x1,y1,x2,y2,x3,y3! Beräkna medelpunkten och radien CALL calcrk(x1,y1,x2,y2,x3,y3,x0,y0,r,ier)! Skriv ut medelpunkten och radien, om allt gått väl: IF (ier == 0) THEN PRINT *, "Cirkelns medelpunkt är (",& & x0,",",y0,")" PRINT *, "och dess radie = ",r ELSE PRINT *, "Punkterna är på rät linje, "& &, "eller några av dem sammanfaller!" END IF STOP END PROGRAM CIRKEL Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 3
Programmet har skrivits i princip i fritt format, vilket innebär, att man inte behöver fästa större avseende vid vilka kolumner som innehåller programinstruktioner, till åtskillnad från det fasta formatet som gäller i FORTRAN 77. Kommentarer anges med utropstecken (!), och de får också stå efter en instruktion på samma rad. För att fortsätta en instruktion på nästa rad används tecknet & som sista tecken på raden, och det bör oftast upprepas på följande rad. I Fortran 90 kan man använda följande tecken (undantag görs för kommentarer, och text som skall skrivas ut): ABCDEFHIJKLMONPQRSTUVWXYZ0123456789 =+-*/(),.$ :!"%& ; <>? där använts för att beteckna ett mellanrum. Tecknet får dessutom användas i namn. Om vi studerar programmet litet närmare, kan vi konstatera att det börjar med instruktionen PROGRAM cirkel, och avslutas med END PROGRAM CIRKEL. Instruktionen PROGRAM anger programmets namn. Detta namn följer reglerna för symbolnamn i Fortran: Det måste börja med en bokstav, och får endast innehålla bokstäver och siffror. Medan FORTRAN 77 endast tillät sex tecken i symbolnamn, tillåter Fortran 90 numera 31 tecken. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 4
Fortran tolkar normalt alla variabler, vilkas namn börjar med någon av bokstäverna A-H och O-Z som decimaltal (reella variabler), och alla övriga variabler som heltal, om man inte deklarerar dem på ett annat sätt. Detta kallas implicit deklaration. För att undvika detta, borde man använda IMPLICIT NONE, vilket leder till, att alla variabler som används, måste deklareras (F tillåter inte IMPLICIT deklarationen). Fördelen med detta är, att odeklarerade variabler genast observeras av kompilatorn, vilket underlättar felsökningen. Exempel på variabeldeklarationer syns på de följande raderna: REAL :: x1,y1,x2,y2,x3,y3,x0,y0,r INTEGER :: ier där först koordinaterna för de tre punkterna, och sedan koordinaterna för medelpunkten, samt radien deklareras reella. Variabeln ier anger en felkod, som levereras av rutinen som beräknar cirkelns medelpunkt och radie. Därefter följer ett s.k. interface-block, som används för att beskriva subrutinens variabler. Det är inte obligatoriskt, men rekommenderas i Fortran 90. Med intent kan man ange om variablerna användes för inmatning eller utmatning. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 5
De följande instruktionerna, som är de första som verkligen utförs, styr inmatningen. Instruktionerna PRINT *, "Skriv koordinaterna för tre punkter" PRINT *, "i ordningen x1,y1,x2,y2,x3,y3" ger en uppmaning på skärmen till användaren att skriva ut koordinaterna för de tre punkterna. Observera, att texten, som skall skrivas ut, begränsas av citationstecken " eller apostrofer i Fortran 90. Den följande instruktionen READ *, x1,y1,x2,y2,x3,y3 läser in talen, som matas in, varpå subrutinen, som gör den egentliga uträkningen anropas: CALL calcrk(x1,y1,x2,y2,x3,y3,x0,y0,r,ier). Då instruktionen CALL utförs, kommer utförandet av programmet tillfälligt att avbrytas, och kontrollen överflyttas till subrutinen calcrk. Symbolnamnen, som befinner sig inom parentes efter subrutinens namn, kallas argument, och används för att överföra information till subrutinen (i detta fall de tre punkternas koordinater), samt för att ta emot den information, som ges av rutinen efter avslutad räkning (i detta fall medelpunktens koordinater och radien). Variabeln ier är = 0, om subrutinen lyckats räkna ut cirkelns medelpunkt och radie. Om detta misslyckas (t.ex. om de givna punkterna ligger på en rät linje), får ier värdet 1. Värdet av ier kan man testa med villkorssatsen IF: Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 6
IF (ier == 0) THEN... ELSE... END IF Om ier = 0, så skrivs resultatet av beräkningen ut med instruktionerna PRINT *, "Cirkelns medelpunkt är (",& & x0,",",y0,")" PRINT *, "och dess radie = ",r Observera textens indragning, vilket underlättar läsningen! Här visas också, hur man kan ange att instruktionen fortsätter på nästa rad. Om ier 0, skrivs ett felmeddelande ut: och programmet avslutas. PRINT *, "Punkterna är på rät linje, "& &, "eller några av dem sammanfaller!" Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 7
För att kunna använda detta program, måste vi också skriva subrutinen, som beräknar cirkelns medelpunkt och radie. Detta sker genom att substituera de tre givna punkternas koordinater i cirkelns ekvation, och lösa ekvationssystemet (x 1 x 0 ) 2 + (y 1 y 0 ) 2 = r 2 (x 2 x 0 ) 2 + (y 2 y 0 ) 2 = r 2 (x 3 x 0 ) 2 + (y 3 y 0 ) 2 = r 2 En rutin som löser detta ekvationssystem visas nedan utan närmare kommentarer: SUBROUTINE calcrk(x1,y1,x2,y2,x3,y3,x0,y0,r,ier)! Routine that computes the center and radius for a circle! passing through three given points! TS 1995-02-28 IMPLICIT NONE REAL, intent(in) :: x1,y1,x2,y2,x3,y3 REAL, intent(out) :: x0,y0,r REAL :: x12,y12,x13,y13 REAL :: r12,r13,a,b,d INTEGER, intent(out) :: ier ier = 0 Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 8
x12 = x1-x2 y12 = y1-y2 x13 = x1-x3 y13 = y1-y3 d = x12*y13 - x13*y12! Test for collinear points: IF (abs(d) > 0.) THEN r12 = x12*(x1+x2) + y12*(y1+y2) r13 = x13*(x1+x3) + y13*(y1+y3) a = 0.5*r12/d b = 0.5*r13/d! Compute center and radius: x0 = a*y13 - b*y12 y0 = -a*x13 + b*x12 r = sqrt((x1-x0)**2 + (y1-y0)**2) ELSE ier = 1 END IF RETURN END subroutine calcrk (om man använder F, måste subrutinen inbäddas i en modul, något vi senare skall studera). Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 9
När man har skrivit subrutinen, kan man kompilera huvudprogrammet cirkel, och subrutinen calcrk. Kompilering av Fortran-program går i princip till på ett liknande sätt på alla datorer. Om vi som exempel väljer det program, som vi nyss studerat, så kan vi kompilera det med en f90-kompilator (jfr man f90) under HP Unix på följande sätt: soul/home/pcu/fyl/sundius/test/f90> f90 -o cirkel cirkel.f90 calcrk.f90 soul/home/pcu/fyl/sundius/test/f90> dir c* -rw------- 1 sundius fyl 850 Mar 3 1997 calcrk.f90 -rwx------ 1 sundius fyl 34656 Jan 31 15:51 cirkel* -rw------- 1 sundius fyl 815 Mar 3 1997 cirkel.f90 -rw------- 1 sundius fyl 342 Dec 9 2004 complex.f90 -rw------- 1 sundius fyl 513 Dec 16 1999 cosfit.f90 I detta fall kommer kommandot f90 både att kompilera och länka filen, och optionen -o anger namnet på den exekverbara filen. Programmet körs genom att man skriver filens namn (om inte katalogen ingår i vägen, måste man antingen först skriva setenv PATH "$PATH":., eller skriva./cirkel, som alltid fungerar) Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 10
soul/home/pcu/fyl/sundius/test/f90> cirkel Skriv koordinaterna för tre punkter i ordningen x1,y1,x2,y2,x3,y3 1 3 5 3 3-3 Cirkelns medelpunkt är ( 3.000000, 0.3333333 ) och dess radie = 3.333333 Man kan också kompilera filerna, och därpå skilt linka de alstrade objektfilerna, som har extensionen.o (kompilerat med gfortran på punk): punk.it.helsinki.fi/home/pcu/fyl/sundius/test/f90> gfortran -c cirkel.f90 calcrk.f90 punk.it.helsinki.fi/home/pcu/fyl/sundius/test/f90> gfortran -o cirkel cirkel.o calcrk.o Namnet på den fil, som innehåller källkoden, har normalt extensionen.f90, men kan också vara.f95,.f03 eller t.o.m..f, beroende på vad kompilatorn tillåter. Om man inte ger något namn åt den exekverbara filen, kallas den av kompilatorn a.out, och körs med kommandot./a.out. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 11
2.3. Konstanter och variabler i Fortran Efter den korta introduktionen skall vi mera i detalj gå in på Fortran-språkets struktur. Vi skall börja med att repetera framställningen av tal. Vi har redan i olika sammanhang nämnt att datorerna räknar med binära tal, så att de egentligen bara känner till heltal. Ett binärt tal framställs som en följd av ettor och nollor, som t.ex. 101101. Siffrorna brukar kallas bitar. Varje bit svarar mot en binär siffra, så att biten längst till höger (den minst signifikanta) anger antalet multipler av 2 0, den följande biten antalet multipler av 2 1, osv. Istället för binär representation, brukar man ofta använda en hexadecimal framställning, som inte kräver så många siffror. Datorns minnesstorlek anges oftast i en annan enhet, nämligen byte, som är åtta bitar. Med en byte kan man inte framställa tal större än 255, och därför är den s.k. ordlängden vanligen minst 4 bytes (32 bitar). Den mest signifikanta biten (dvs den som befinner sig längst till vänster) anger förtecknet. Decimaltal (dvs reella tal) måste också framställas i samma form. Detta är inte så konstigt, eftersom varje decimaltal kan uttryckas som ett heltal gånger en tiopotens eller exponent (vi kan också tolka det som en logaritmisk framställning, med mantissa och karaktäristika). Om ordlängden är 32 bitar, så reserveras vanligen 8 bitar för exponenten, och 24 bitar för mantissan (kallas också för en flyttalsrepresentation). Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 12
I verkligheten uppfattas mantissan som ett decimaltal (normaliserat till 1), och talet kan då framställas i formen a = r 2 m. Detta kallas också för IEEE-standarden. Exponenten uttrycks vanligen som ett heltal mellan 0 och 255, och 127 måste subtraheras för att man också skall kunna framställa negativa exponenter. Det största positiva tal som kan representeras med denna metod är således ca 2 127 10 38. På grund av att mantissan innehåller 24 bitar får man ca 7 decimalers noggrannhet i räkningarna. Detta sätt att framställa tal med ett begränsat antal siffror, leder till att man inte kan uttrycka vilka tal som helst. För en dator med 32-bitars ordlängd begränsas heltal till intervallet [ 2 10 9, 2 10 9 ]. Reella tal är till sitt absoluta värde begränsade av intervallet [10 38, 10 38 ], så att verkligt stora eller små tal inte kan representeras korrekt av datorn. Tal som underskrider den nedre gränsen åstadkommer underflow, medan tal som överskrider den övre gränsen ger upphov till overflow. I det senare fallet avbryts programmets utförande automatiskt, medan underflow leder till att resultatet av räkneoperationen blir noll, vilket inte alltid observeras. Fortran känner till både numeriska och icke-numeriska konstanter (eller literala konstanter). De numeriska konstanterna kan vara heltaliga, reella eller komplexa. En heltalig konstant är ett tal, som saknar decimalpunkt, medan en reell konstant har decimalpunkt, och kan efterföljas av tecknet E och en heltalig exponent. 4., -1.5, 2.E-5, -4.E8 är exempel på reella konstanter. En komplex konstant består av två reella tal inom parentes, åtskilda av ett komma : (4.51, -3.72). Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 13
De icke-numeriska konstanterna är logiska konstanter och teckenkonstanter. De logiska konstanterna är två:.true. och.false. (observera punkten före och efter!). En teckenkonstant anges i Fortran som en teckensträng omgiven av citationstecken (eller apostrofer): "Detta är en teckenkonstant". Om teckensträngen innehåller ett citationstecken, upprepas det: "Descartes"" filosofi" (detta problem undviks i Fortran 90, där man också kan använda apostrofer: "Descartes filosofi"). En variabel är en storhet som kan definieras och omdefinieras under programexekveringen. I Fortran måste den alltid ha ett namn. En variabel har alltid en datatyp, som i Fortran 90 deklareras på följande sätt : TYP :: namn (det dubbla kolonet saknas i FORTRAN 77), där TYP anger datatypens namn, och namn är variabelns namn. Tre reella variabler a,b,c anges med instruktionen REAL :: a,b,c, och två heltal i,j med instruktionen INTEGER :: i,j. Om typen för en variabel inte är angiven, antog Fortran-kompilatorn förr automatiskt att en variabel, vars namn börjar med I-N, är av heltalstyp, medan alla övriga är reella variabler. Detta undvikes i Fortran 90 genom att använda deklarationen IMPLICIT NONE. Man måste då deklarera alla variabler i programmet, men samtidigt underlättar man upptäckten av typografiska fel vid kompileringen. En variabel kan definieras antingen genom att den tilldelas ett värde, eller genom en READ-instruktion. Tilldelningen sker genom en instruktion av formen namn = uttryck, där namn anger variabelnamnet, och uttryck är ett uttryck, som efter uträkning får ett bestämt värde. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 14
Uttrycket får innehålla operander, operatorer och parenteser. Ett exempel på en dylik tilldelningssats är a = b + c, där variabeln a tilldelas värdet av summan b + c. I detta uttryck är variablerna b och c operander, medan + är en operator. Om vi antar att alla variabler a, b och c är reella, så får variabeln a ett värde, som är mycket nära den exakta summan av b och c (observera, att datorn alltid räknar med ändlig precision). Men om t.ex. variabeln a är heltalig, så måste resultatet av räkneoperationen först avkortas till ett heltal, innan variabeln a tilldelas ett värde. Detta kallas för stympning av resultatet, och kan också inträffa vid heltalsdivision, som vi strax skall se. Fortran har fem aritmetiska operatorer: + (addition), - (subtraktion), * (multiplikation), / (division) och ** (potensering). Genom kombination av dessa operatorer med godtyckliga operander kan man i princip åstadkomma hur invecklade uttryck som helst. Men det är inte alltid alldeles klart, hur ett sådant uttryck räknas ut. Låt oss t.ex. betrakta a = b*c+d/e-f**g+h. För att göra det lättare att tolka ett sådant uttryck, har man infört vissa prioritetsregler, som överensstämmer med dem som används i matematiken. Högsta prioritet ges åt potensering (och funktionsberäkningar, om sådana förekommer i uttrycket), lägre prioritet åt multiplikation och division, och den lägsta prioriteten ges åt addition och subtraktion. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 15
Uttrycket ovan kan alltså tolkas på följande sätt: Först utförs operationen f**g, och resultatet sparas i en tillfällig variabel, som vi kan kalla tmp1. Sedan beräknas b*c, som sparas i tmp2, samt d/e, som sparas i tmp3. Sedan adderas tmp2 och tmp3, och resultatet sparas i tmp4. Därpå subtraheras tmp1 från tmp4, och resultatet sparas i tmp5. Slutligen adderas tmp5 och h, och resultatet tilldelas variabeln a. Prioritetsordningen kan förändras, liksom i vanlig matematik, genom att använda parenteser. Sålunda kan vi få ett helt annat resultat genom att skriva ovanstående uttryck t.ex. i formen a = (b*c+d)/(e-f**g)+h. Vad händer om alla operander inte är av samma typ? Vi kan t.ex betrakta uttrycket a = b*c/d, där t.ex. b är reell och har värdet 5.0, medan c och d är heltaliga och har värdena 4 och 8. Emedan multiplikation och division har samma prioritet, kommer beräkningen att utföras från vänster till höger, dvs vi beräknar först b*c efter att ha konverterat c till ett decimaltal (4.0), och dividerar sedan resultatet med d, som först konverterats till decimaltal (8.0). Variabeln a får alltså värdet 2.5. Om vi skriver föregående uttryck i formen a = c/d*b, som är matematiskt ekvivalent med den ursprungliga formen, kommer vi först att beräkna uttrycket c/d, som nu beräknas som en heltalsdivision 4/8, avkortat till 0! Därpå multipliceras resultatet (efter konvertering till ett decimaltal) med b, men eftersom resultatet av heltalsdivisionen var noll, kommer också variabeln a att tilldelas värdet 0. Detta visar, att man bör undvika att blanda typer, om man inte säkert vet vad som händer. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 16
Vi skall studera ett annat exempel på tillordningar av variabler, nämligen beräkning av sin, cos och tan för en vinkel i en rätvinklig triangel, där kateterna a och b är givna. Om vinkeln betecknas θ, a är den närliggande kateten och b den motstående kateten, så gäller som bekant cos θ = a a2 + b 2 sin θ = b a2 + b 2 tan θ = b a Om a = 3 och b = 4 (t.ex.) så kan programmet skrivas PROGRAM Triangle IMPLICIT NONE REAL :: A,B,C,COST,SINT,TANT A = 3.0 B = 4.0 C = (A*A + B*B)**0.5 COST = A/C SINT = B/C TANT = B/A PRINT *,COST,SINT,TANT STOP END PROGRAM Triangle Man kunde ha skrivit programmet kortare, men så här är det lättare att förstå. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 17
Som vi ser, har kvadratroten (som ger triangelns hypotenusa) räknats skilt för sig. Istället för att räkna den med potensräkning, kunde vi ha räknat den med kvadratrotsfunktionen, men i detta fall har det inte så stor betydelse. Ofta går det snabbare att räkna en kvadrat via multiplikation, såsom i detta fall (A*A istället för A**2). Om man märker att en del av ett uttryck förekommer på flere ställen i programmet (såsom i detta fall uttrycket för hypotenusan) lönar det sig att räkna ut det skilt för sig. Ett annat exempel är lösningen till en andra gradens ekvation. Om ekvationen är ax 2 + bx + c = 0, så vet vi att rötterna kan skrivas x = b 2a ± 1 b2 4ac 2a Ett program som löser en andra gradens ekvation med reella rötter visas på nästa sida. Programmet kontrollerar också ifall diskriminanten är större, eller lika med noll. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 18
PROGRAM Rot IMPLICIT NONE REAL :: A,B,C,D,X1,X2 PRINT *, "Ange koefficienterna A,B,C:" READ *, A,B,C D = B*B - 4.*A*C IF (D >= 0) THEN X1 = (-B + SQRT(D))/(2.*A) X2 = (-B - SQRT(D))/(2.*A) PRINT *, "Rötterna är:" PRINT *, "X1 = ",X1, "X2 = ",X2 ELSE PRINT *, "Inga reella rötter!" END IF STOP END PROGRAM Rot Här ser vi hur man kan testa värdet av diskriminanten (d = b 2 4ac) för att ta reda på, om rötterna är reella eller ej. IF-instruktionen innehåller här ett logiskt uttryck D >= 0 (d 0) som antingen är sant eller falskt. Ett problem i Fortran har varit att den uppnåeliga precisionen är maskinberoende. I Fortran 90 är det numera möjligt att välja den precision man önskar. Detta åstadkoms med den inbyggda funktionen KIND, som vi senare skall beskriva. Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 19
Vi skall se på ett annat exempel, där vi skall beräkna summan av serien s = i=1 1 i 2 = π2 6 1.6449341 Programmet ser ut på följande sätt: PROGRAM serie IMPLICIT NONE REAL :: s = 0. INTEGER :: i, n PRINT *, Ange antalet termer: READ *, n PRINT *, Antalet termer:,n DO i=1,n s = s + 1./REAL(i)**2 END DO PRINT *, Termernas summa:,s END PROGRAM serie Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 20
Programmet visar en enkel slinga, som man kan bilda med en DO-loop. Variabeln s är, som synes, initialiserad till 0. Funktionen REAL förvandlar ett heltal till ett reellt tal. Vi kan testa programmet t.ex. genom att räkna summan av 100 termer: Ange antalet termer: 100 Antalet termer: 100 Termernas summa: 1.634984 Om vi gör räkningen med 1000 termer blir resultatet 1.643935, och med 10000 termer 1.644725. Samma resultat fås för 100000 termer. Vi tappar tydligen noggrannhet (en lösning är att summera termerna i motsatt ordning, kan du se varför?). Introduktion till vetenskapliga beräkningar II, Tom Sundius 2009 21