programmering för fysiker - Åbo akademi

22
Programmering för fysiker Höstterminen 2008 Institutionen för fysik Innehåll Lektion 1 for-loop 2 Funktionen printf 2 Division av heltal 2 Programmets struktur 2 Kompilering 2 Kompilering i Linux 2 Kompilering i Windows 2 Förkortad notation 3 if-satser 3 Jämförelseoperatorer 3 Mer information 3 Lektion 2 Funktioner 4 Variablers synlighet (scope) 4 Call by value 4 Globala variabler 4 Matematik 4 Lista över matematikbibliotekets funktioner 4 Datatyper 5 Kommandot make 5 Introduktion till pekare 5 Lektion 3 Filhantering (Input/Output) 6 Input med scanf 6 Arrayer 6 Numerisk integrering 7 Gnuplot 7 Komplexa tal i C 8 Arrayer och pekare 8 Listor Funktioner 9 Datatyper 9 Nyckelord 9 Argument för printf 9 Bibliotek 9 Kompileringsflaggor 9 Lektion 4 Fraktaler 10 Grafik med biblioteket SDL 10 Kompilering 11 Strukturer 11 Ett grafiskt mandelbrotprogram 12 Färger och pixlar i SDL 12 Fler färger 12 Lektion 5 Ordinära differentialekvationer 13 Differentialekvationer av högre ordning 13 Exempel: Rörelse i en dimension 13 Kopplade differentialekvationer 13 En pendel 14 Mer information 14 Lektion 6 Diskretisering 15 Diskretisering av derivator 15 Schrödingerekvationen 15 Numerisk lösning av Schrödingerekvationen 15 Lektion 7 Slumptal 18 Slumpvandring 18 Olika fördelningar 18 Definitioner 18 Likformig fördelning i ett intervall 18 Exponentiell fördelning 18 Gaussisk fördelning / normalfördelning 19 Diffusion-Limited Aggregation 19 Kvaliteten på slumptal 19 Lektion 8 Linjär algebra med LAPACK 20 Egenvärden och egenvektorer 20 Att anropa FORTRAN-funktioner från C 20 Egenfunktioner för Schrödingerekvationen 20 Referenser 22

Upload: others

Post on 27-Nov-2021

13 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Programmering för fysiker - Åbo Akademi

Programmering för fysikerHöstterminen 2008

Institutionen för fysik

Innehåll

Lektion 1

for-loop 2

Funktionen printf 2

Division av heltal 2

Programmets struktur 2

Kompilering 2

Kompilering i Linux 2

Kompilering i Windows 2

Förkortad notation 3

if-satser 3

Jämförelseoperatorer 3

Mer information 3

Lektion 2

Funktioner 4

Variablers synlighet (scope) 4

Call by value 4

Globala variabler 4

Matematik 4

Lista över matematikbibliotekets funktioner 4

Datatyper 5

Kommandot make 5

Introduktion till pekare 5

Lektion 3

Filhantering (Input/Output) 6

Input med scanf 6

Arrayer 6

Numerisk integrering 7

Gnuplot 7

Komplexa tal i C 8

Arrayer och pekare 8

Listor

Funktioner 9

Datatyper 9

Nyckelord 9

Argument för printf 9

Bibliotek 9

Kompileringsflaggor 9

Lektion 4

Fraktaler 10

Grafik med biblioteket SDL 10

Kompilering 11

Strukturer 11

Ett grafiskt mandelbrotprogram 12

Färger och pixlar i SDL 12

Fler färger 12

Lektion 5

Ordinära differentialekvationer 13

Differentialekvationer av högre ordning 13

Exempel: Rörelse i en dimension 13

Kopplade differentialekvationer 13

En pendel 14

Mer information 14

Lektion 6

Diskretisering 15

Diskretisering av derivator 15

Schrödingerekvationen 15

Numerisk lösning av Schrödingerekvationen 15

Lektion 7

Slumptal 18

Slumpvandring 18

Olika fördelningar 18

Definitioner 18

Likformig fördelning i ett intervall 18

Exponentiell fördelning 18

Gaussisk fördelning / normalfördelning 19

Diffusion-Limited Aggregation 19

Kvaliteten på slumptal 19

Lektion 8

Linjär algebra med LAPACK 20

Egenvärden och egenvektorer 20

Att anropa FORTRAN-funktioner från C 20

Egenfunktioner för Schrödingerekvationen 20

Referenser 22

Page 2: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 1.

Lektion 1

Exempelprogram 1. Ett program som räknar ihop en summa.#include <stdio.h>#include <math.h>

int main (){ int n; double resultat; resultat = 0;

for (n = 1; n < 10000; n = n + 1) { resultat = resultat + 1.0/(n*n); }

printf ("Svaret är: %f\n", resultat); printf ("pi^2/6 = %f\n", M_PI*M_PI / 6.0); return 0;}

Kommandot #include <stdio.h> läser in en fil som heter "stdio.h". Filen

innehåller bl.a. printf-funktionen. Filen math.h innehåller matematiska

funktioner och konstanter.

Märk att så gott som alla rader avslutas med semikolon ;

Variablerna i exempelprogrammetint n;double resultat;

n är en variabel av typ int (står för integer), ett positivt eller negativt

heltal.

resultat är en variabel av typ double, ett reellt tal (s.k. floating point,

ca 15 decimalers noggrannhet. double står för "double precision").

Variabler får inte något startvärde automatiskt, de måste initialiseras.

Variabeln resultat tilldelas värdet 0 med kommandot resultat = 0;

for-loop

En for-loop används för att upprepa ett block så länge ett villkor är

uppfyllt. En vanlig användning är att upprepa något ett visst antal

gånger.

OBS! inget semikolon efter for(...) !

for (n = 1; n < 10000; n = n + 1) { ... }

Uttrycket i parentesen har tre delar: initialisering "n=1", villkor

"n<10000" och inkrementering "n=n+1". Initialiseringen (n ges värdet 1)

utförs en gång. Blocket (koden mellan klamrarna) utförs så länge vill-

koret är sant. Inkrementeringen (värdet på n ökas med ett) utförs

varje gång blocket har utförts.

Funktionen printf

Funktionen printf kan användas för att skriva ut text och variablers

värden på skärmen.printf ("Svaret är: %f\n", resultat);

kommer att skriva ut texten Svaret är: 1.644834

Funktionen ges i det här fallet två argument, separerade med

kommatecken. Texten i det första argumentet skrivs ut, och %f byts ut

mot det andra argumentet. \n betyder radbyte. För att skriva ut heltal

(int) används %d istället för %f.

Exempel.

En printf med bara ett argument.printf("Här är en text. Den byter inte rad i slutet.");

Kommer att skriva ut:Här är en text. Den byter inte rad i slutet.

Exempel.

Flera värden kan skrivas ut med samma printf.int a = 3;double b = 4.0;printf ("a har värdet %d och b har värdet %f", a, b);

Programmet kommer att skriva:a har värdet 3 och b har värdet 4.000000

Exempel.printf ("%d %d %d %f %f %f", a, a*a, 2*a*a, b, b*b, a*b*b);

Kommer att skriva ut:3 9 18 4.000000 16.00000 48.00000

Division av heltal

Beräkningar som innehåller endast heltal ger heltal som resultat. Vid

division leder det här till avrundning, t.ex. 2/3 ger resultatet 0. Om

man vill ha ett resultat av typ double, måste en av operanderna vara av

typ double, t.ex. 2.0/3 ger 0.66666666666667. "2" är alltså av typen int,

"2.0" av typen double.

M_PI är en konstant som deklareras i math.h och är en approximation

av ¼.

Programmets struktur

int main (){ ... return 0;}

Det här deklarerar funktionen main. Varje C-program har en main-

funktion som körs då programmet startas. I vårt exempel ger man inga

argument åt main, så den har tomma argumentparenteser efter sig.

Mer om funktioner senare.

Kompilering

Programkoden skrivs i en textfil, som oftast ges suffixet ".c". För att få

en fil som kan köras måste .c-filen kompileras, detta görs med en

kompilator. Här använder vi kompilatorn gcc.

Kompilering i LinuxI en terminal, kör gcc exempel1.c (i den katalog där filen exempel1.c

finns). Om allt fungerar skapas den körbara filen a.out, som kan köras

med kommandot ./a.out I linux finns gcc ofta färdigt installerat, om

inte kan det installeras med distributionens pakethanterare (t.ex. yum i

Fedora). För att skriva koden behövs någon texteditor, t.ex. kedit, kate

eller emacs.

Kompilering i WindowsKör gcc exempel1.c i ett kommandofönster (i den katalog där filen

exempel1.c finns). Det körbara programmet heter a.exe. Kör program-

met med kommandot "a". gcc för windows kallas MinGW, det finns på

www.mingw.org. Koden kan skrivas i någon texteditor, Notepad duger,

2

Page 3: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 1.

Word rekommenderas inte. Se också kurshemsidan för mer diskussion

om editorer och om hur man installerar MinGW.

Man kan ge den körbara filen ett annat namn än a.out / a.exe med

flaggan -o på gcc:s kommandorad:gcc exempel1.c -o exempel.exe

En annan nyttig flagga är -Wall, som gör att gcc skriver ut varnings-

meddelanden om programmet verkar suspekt.gcc exempel1.c -Wall

Förkortad notation

För ofta förekommande matematiska operationer finns en förkortad

notation:

n = n + 1; n++; öka med 1

n = n - 1; n--; minska med 1

n = n + a; n += a; öka med a

b = b * 1.34; b *= 1.34; multiplicera med 1.34

Om ett block består av endast en sats, kan klamrarna lämnas bort.

if-satser

if-satser används då något ska utföras om ett villkor är uppfyllt.

Exempel:int a = 8;if (a > 5) { printf ("a är större än fem\n"); }

Om villkoret innanför parentesen är sant utförs blocket efter if.

Med else kan ett annat block specificeras, som utförs om vilkoret är

falskt. else-blocket behövs inte.

Exempel.int a = 3;if (a > 5) { printf ("a är större än fem\n"); }else { printf ("a är inte större än fem\n"); }

Märk! Likhet testas med operatorn == . Enkelt likhetstecken = används

endast för tilldelning.if (a == 5) ...

Jämförelseoperatorer

== lika med

!= inte lika med

> större än

>= större än eller lika med

< mindre än

<= mindre än eller lika med

Exempelprogram 2.//Skriver ut alla primtal < 1000.#include <stdio.h>

int main (){ int t; //talet som testas int f; //en faktor i talet? int prime;

for (t = 2; t < 1000; t++)

{ prime = 1; for (f = 2; f < t; f++) { if (t % f == 0) { prime = 0; } } if (prime == 1) printf ("%d ", t); } printf ("\n"); return 0;}

% är modulo-operatorn. Den ger resten vid en heltalsdivision, t.ex 11 %

3 är 2. t är delbart med f om resten vid division av t med f är 0, dvs

om t % f är 0.

Programmets funktionsprincip: Gå igenom heltalen från 2 till 1000.

För varje tal t, prova om talet är delbart med något mindre heltal f.

Om ett sådant f hittas är t ett sammansatt tal. Om inget f hittades är

t ett primtal.

Mer information

En C-tutorial:

http://www.cprogramming.com/tutorial.html (Se en bit ner på sidan)

http://www.cppreference.com/

Länkarna finns också på kurshemsidan,

www.abo.fi/~fjansson/progkurs/

En bok (finns på insten): Brian W. Kernighan och Dennis M. Ritchie:

The C programming Language (Prentice Hall, 1988)

Linux har ett manualprogram, man, som innnehåller dokumentation för

kommandon och c-funktioner.

t.ex. man 3 printf

Programmet avslutas genom att trycka på q.

3 anger sektionen som man vill söka i, sektion 3 innehåller dokumen-

tation om funktioner som kan anropas från ett C-program. Sektionen

måste anges i det här fallet, eftersom det också finns ett program med

namnet printf. Ett alternativ är att använda flaggan -a, som gör att

alla manualsidor som hittas med ett visst namn visas.

info libc visar dokumentation för c-standardbiblioteket.

info gcc visar dokumentation för c-kompilatorn

Exempelprogram 3.

Ett minimalt C-program, som bara skriver ut en rad text.

Lämpligt för att prova om man installerat kompilatorn rätt.

#include <stdio.h>int main () { printf ("Hallå!\n"); return 0; }

3

Page 4: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 2.

Lektion 2

Funktioner

main och printf är exempel på funktioner.

Man kan också definiera egna funktioner, för att dela upp ett program

i hanterliga delar. Om någonting ska göras på flera ställen i ett

program, är det ofta en god idé att definiera en funktion för det.

Exempelprogram 1. En enkel funktion#include <stdio.h>int cube (int x);

int main (){ int i, V; for (i = 0; i < 4; i++) { V = cube(i); printf ("cube(%d) är %d\n", i, V); } return 0;}

int cube (int x){ int volume; volume = x * x * x; return volume;}

int cube (int x); i början av programmet är en funktionsprototyp,

som anger vilken datatyp funktionen returnerar, hur många para-

metrar den ska anropas med, och vilka datatyper parametrarna har.

Funktionens definition finns sist i programmet. return avslutar funk-

tionen, och anger funktionens resultat, dess returvärde. Observera att

funktionsprototypen avslutas med ett semikolon, medan det inte finns

ett semikolon i definitionen av funktionen.

En funktion måste inte returnera något värde. En funktion som inte

returnerar ett värde bör definieras med returtypen void:void funk (int x){ ... return;}

En void-funktion kan (men måste inte) avslutas med return, utan

returvärde.

En funktion behöver inte heller ta emot några parametrar, det kan

specificeras med void i parameterlistan:int f (void){ ...}

Exempelprogram 2. En funktion som beräknar fakultet. Den är av

typen double, eftersom svaret kan bli stort och kanske inte ryms i en

int.#include <stdio.h>double fakultet (int n);

int main (){ int i; printf ("20! = %f\n\n", fakultet (20)); for (i = 0; i <= 10; i++) printf ("%d! = %f\n", i, fakultet(i));

return 0;

}

//Beräkna n! = 1 * 2 * 3 * ... * ndouble fakultet (int n){ double f = 1; int i; for (i = 1; i <= n; i++) f = f * i;

return f;}

Variablers synlighet (scope)I exempelprogrammet ovan finns det en variabel som heter i både i

main och i fakultet. De är helt orelaterade, en variabel som deklareras

inne i en funktion är synlig bara innanför funktionen. Det här är en

bra sak, eftersom man kan skriva en funktion, och vara säker på att

ingen annan del av programmet ändrar på funktionens variabler.

Call by valueDå man skickar parametrar till en funktion, skickas parametrarnas

värden. Om parametern ändras inne i funktionen, syns ändringen inte

utanför. (Om man vill att ändringen ska synas också utanför, kan man

skicka en referens i stället, tas upp senare).

Globala variablerOm man vill att en variabel ska "synas" i alla fuktioner i ett program,

kan den deklareras utanför alla funktioner, typiskt någonstans i början

av programmet. Sådana globala variabler kan göra programmet svår-

are att förstå och förändra, eftersom det är svårt att hålla reda på alla

ställen där de används.

Matematik

C har många matematiska funktioner inbyggda. För att använda dem

ska man ha #include <math.h> i början av programmet. Programmet

ska sedan kompileras med flaggan -lm, för att funktionerna ska länkas

med i programmet. (-l är en flagga för att länka till ett bibliotek och m

är namnet på matematikbiblioteket.)

Lista över matematikbibliotekets funktioner (ej fullständig)

Trigonometriska och hyperboliska funktioner, inversercos sinh tan sin cosh atan, atan2(x, y); asin asinh tanh acos acosh atanh

pow(x, y) xy

sqrt kvadratrotcbrt kubikrothypot(x,y) sqrt(x2 + y2)exp(x) ex

exp2(x) 2x

log() logaritm (bas e)log10() logaritm (bas 10)log2() logaritm (bas 2)

remainder(x, y) rest vid divisionfabs(x) absolutvärde |x|

Avrundingceil avrunda uppåt till ett heltalfloor avrunda nedåt till ett heltalround avrunda till närmaste heltalnearbyint avrunda till närmaste heltaltrunc avrunda mot 0, till närmaste heltal

4

Page 5: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 2.

Besselfunktioner j0(x) j1(x) jn(n, x)

y0(x) y1(x) yn(n, x)

tgamma gammafunktionenerf errorfunction

Exempel. Användning av matematikfunktioner.double x = 0.32;double a = pow(sin(x), 2) + pow(cos(x), 2);

Datatyper

Hittills har vi använt datatyperna int och double.

Det finns fler:

char ett tecken / en bokstav

int ett heltal

float ett decimaltal (floating point, flyttal)

double double precision floating point

Dessutom finns det "modifiers": short, long, signed, unsigned. short och

long ändrar på hur mycket plats (i bytes) en variabel tar och därmed

på hur stora tal som kan uttryckas. signed anger att negativa värden är

tillåtna, unsigned att de inte är det. Om ingendera anges är signed

standard. Det finns inga unsigned flyttal.

typ bytes bits värdeområde (signed)

char 1 8 -128..127

short int 2 16 -32768..32767

int 4 32 -2147483648..+2147483647

long long int 8 64 -263..263-1

float 4 32

double 8 64

Kommandot make

make är ett program som underlättar kompileringen. Det är speciellt

nyttigt för större program som är delade i flera filer. make läser en

textfil (ofta med namnet Makefile) som beskriver hur programmet ska

kompileras.

Exempel: Vad det kan stå i filen Makefilefakultet : fakultet.c

gcc fakultet.c -o fakultet -Wall -lm

På den första raden står namnet på den fil som ska skapas (fakultet),

ett kolon och en lista på de filer som behövs. På den andra raden finns

kommandot som ska köras om någon av filerna till höger om kolon är

nyare (har en senare modifierad-tid) än den till vänstra. Märk! Före

kommandot (här gcc), ska det finnas exakt ett TAB-tecken, mellan-

rum/space duger inte. Då makefilen är gjord kan man kompilera

programmet genom att ge kommandot make.

Mer information om make finns i manualen:

http://www.gnu.org/software/make/manual/make.html

eller info make i en linuxterminal.

Exempel: Makefileprogrammet : del1.o del2.o

gcc del1.o del2.o -Wall -lm

del1.o : del1.cgcc del1.c -c -o del1.o -Wall

del2.o : del2.cgcc del2.c -c -o del2.o -Wall

Flaggan -c gör att gcc inte skapar ett körbart program, utan endast

producerar ett mellanresultat, en s.k. objektfil, som oftast ges ett

namn som slutar med ".o". Flera objektfiler kan sedan länkas ihop till

ett körbart program, den första raden är kommandot för det.

Introduktion till pekare

Variablerna som används i ett program lagras i datorns minne (RAM).

Minnet kan ses som en sekvens av bytes, där varje byte har en egen

adress. C kan använda de här adresserna för att hantera data, med

hjälp av s.k. pekare, pointers.

Pekare deklareras på samma sätt som variabler, men med * framför

namnet:int *p1; //En pekare till en intdouble *p2; //En pekare till en double

Operatorer:

& "address of"

* "object at"

Exempel. Användning av pekare.int a; int *p; //en pekare till en integer

a = 5;p = &a; //p pekar nu till aprintf ("%d\n", *p);

*p = 3; //sätt värdet 3 i den integer som p pekar påprintf ("%d\n", a);

Resultat:53

Precis som variabler måste pekare ges ett värde innan de används,

annars pekar de "vart som helst", och det är fel att använda dem.

5

Page 6: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 3.

Lektion 3

Filhantering (Input/Output)

Om man har stora mängder data kan det vara opraktiskt att skriva ut

dem på skärmen. Då kan man skriva i en fil i stället.

Exempel. Skriver text i en fil kallad "filen.txt"#include <stdio.h>#include <stdlib.h>#include <math.h>

int main (){ FILE *out; int i;

out = fopen ("filen.txt", "w"); if (out == NULL) { perror ("Cannot open file"); exit (1); }

fprintf (out, "Test! "); for (i = 1; i <= 3; i++) fprintf (out, "%d ", i); fprintf (out, "\n");

fclose (out); return 0;}

fopen öppnar en fil, så att man kan skriva till och/eller läsa från

den. Funktionen tar två argument: filens namn och en

annan sträng som beskriver vad filen ska användas till.

"r" read

"w" write (om filen finns, skrivs den över)

"a" append (lägger till ny data i slutet av filen)

"rb" "wb" read/write binary. Behövs i windows om filen

innehåller data (dvs. inte läsbar text) för att förhindra att

ny-rad-tecknen konverteras.

fopen returnerar en FILE pointer, eller NULL vid fel.

fclose stänger en fil. Öppna filer stängs automatiskt då

programmet avslutas, men det är bättre att stänga filer då

de inte längre behövs.

fprintf Skriver till en fil. Fungerar som printf, men det första

argumentet är en FILE * till filen.

perror Skriver ut ett felmeddelande. Texten i argumentet skrivs ut,

följt av ett felmeddelande från den senaste biblioteksfunk

tionen som misslyckats.

exit avslutar programmet. Return kunde ha använts här (i

main), men för att avsluta programmet ifrån någon annan

funktion kan exit användas. Parametern blir programmets

"exit code". Typiskt retureras 1 eller -1 om programmet

avslutas pga. ett fel och 0 om programmet avslutas

normalt.

Input med scanf

scanf används för att läsa in input från terminalen eller från en fil.

Hurdana värden som ska behandlas anges med en formatsträng, på

liknande sätt som för printf. Efter formatsträngen finns en lista med

adresser där de inlästa värdena ska sparas. Om scanf inte kan läsa in

ett värde (t.ex. om den väntar sig ett tal men läser en bokstav) lämnar

den resten av värdena obehandlade. scanf returnerar antalet värden

som lästes.

Exempelprogram. Läser två heltalsvariabler från terminalen med

scanf.#include <stdio.h>int main (){ int a, b, n; a = -1; b = -1; n = scanf ("%d%d", &a, &b); printf ("%d values read.\n", n); printf ("a = %d b = %d\n", a, b); return 0;}

För att läsa in doubles används %lf. Om man vill läsa från en fil i

stället för från terminalen kan man använda fscanf. fscanf fungerar

precis som scanf, men tar en FILE * som sitt första argument.

Arrayer

(kallas också räckor på svenska)

En array är en samling av variabler med samma typ och namn, som

skiljs åt genom ett index. Matematiskt betecknas det här xi, i C anges

index innanför hakparenteser: x[i]. Det första elementet i en array har

alltid index 0.

Arrayer är nyttiga då man ska behandla data eller skicka mycket data

till en funktion. Textsträngar i C är arrayer av typen char, det finns

alltså ingen egen datatyp för textsträngar som i många andra prog-

rammeringsspråk. char är en datatyp som innehåller ett tecken, en

char tar 1 byte minne.

Exempel 1. Användning av en arrayint main (){ int i; int x[10]; //10 element, index går från 0 till 9 for (i = 0; i < 10; i++) x[i] = 0; x[1] = 3; x[8] = 2; for (i = 0; i < 10; i++) printf ("%d ", x[i]); printf ("\n"); return 0;}

Man kan också definiera flerdimensionella arrayer, t.ex. 2-dimension-

ella matriser:

int row, col;double x;double m [10][5];m[row][col] = x;

6

Page 7: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 3.

Numerisk integrering

Integralen av en funktion f från a till b kan approximeras genom att

dela intervallet i N lika stora bitar, evaluera f i mitten av varje bit och

summera:

mittpunkten

för det i:te intervallet.

W. H. Press et al.:

Numerical Recipes in C - The art of scientific computing, 2:nd edition

(Cambridge university press 1992)

(Elektronisk version på www.nr.com)

Exempelprogram. Numerisk integrering.

#include <stdio.h>#include <math.h>double integrate (double xmin, double xmax, int steps);double f(double x);

int main (){ double I, R; R = sqrt(M_PI)/2; I = integrate (0, 10, 500); printf ("I =%.15f fel:%e\n", I, I-R); printf ("R =%.15f (exakt svar)\n", R); return 0;}

double integrate (double xmin, double xmax, int steps){ double A = 0; double x; double dx = (xmax - xmin)/steps; int i; x = xmin + dx/2;

for (i = 0; i < steps; i++) { A += dx * f(x); x += dx; } return A;}

double f(double x){ return exp(-x*x);}

Gnuplot

Gnuplot är ett program för att rita grafer av funktioner och data. Det

finns till både linux och windows. Graferna kan visas på skärmen eller

sparas, som bilder eller postscript, för att användas t.ex. i publika-

tioner. Gnuplot kan också anpassa funktioner till data.

http://www.gnuplot.info/

http://t16web.lanl.gov/Kawano/gnuplot/index-e.html

Gnuplot läser kommandon i en terminal eller från en fil. För kompli-

cerade figurer med flera kurvor, med namn på koordinataxlarna etc,

lönar det sig att skriva plottningskommandon i en fil så att man

slipper upprepa allt för varje liten förändring som görs.

Exempel. Kommandon i Gnuplot.

Plotta en funktion:plot 0.02*x**2 + cos(x)

Plotta data från en fil:plot "data.dat" using 2:3 with linespoints

using anger vilka kolumner i filen som ska användas. Den första kol-

umnen är nummer 1.

Man kan ange någon beräkning som ska utföras på datat innan det

plottas:plot "data.dat" using ($2**2):($3/$1) with linespoints

Uttrycken efter using är nu innanför parenteser, och kolumnnummer

anges med $.

Behåll den tidigare bilden och lägg till en ny kurva:replot 2*x+1

Ställ in intervall för koordinataxlarna:set xrange [-2:1]set yrange [-1:1]

I graf-fönstret kan man zooma mad högra musknappen. 'a' återställer

axlarna så att all data syns. 'h' ger en lista på andra knappkom-

mandon. Dessa kommandon fungerar då plot-fönstret (och inte ter-

minalen) är aktivt.

Exempelprogram. Numerisk integrering, skriver resultatet i en fil för

plottning med gnuplot.

#include <stdio.h>#include <stdlib.h>#include <math.h>

double integr (double xmin, double xmax, int steps, FILE *out);double f(double x);

int main (){ FILE *out; double I, R;

out = fopen ("int.dat", "w"); if (out == NULL) { perror("Cannot open file"); exit (1); }

I = integrate (0, 10, 500, out); fclose (out);

R = sqrt(M_PI)/2; printf ("I =%.15f fel:%e\n", I, I-R); printf ("R =%.15f (exakt svar)\n", R); return 0;}

7

Page 8: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 3.

double integr (double xmin, double xmax, int steps, FILE *out){ double A = 0; double x; double dx = (xmax - xmin)/steps; int i; x = xmin + dx/2;

for (i = 0; i < steps; i++) { A += dx * f(x); fprintf (out, "%.15f %.15f %.15f %.15f\n", x+dx/2, f(x+dx/2), A, erf(x+dx/2)*sqrt(M_PI)/2); x += dx; } return A;}

double f(double x){ return exp(-x*x);}

Komplexa tal i C

Komplexa tal finns med i den nya C-standarden C99.

Exempel. Användning av komplexa tal.#include <math.h>#include <complex.h>#include <stdio.h>

int main (){ _Complex double x, y; x = 1 + 2 * I; printf ("x = (%f, %f)\n", creal(x), cimag(x)); y = 1 - 2 * I; printf ("y = (%f, %f)\n", creal(y), cimag(y)); printf ("x*y = (%f, %f)\n", creal(x*y), cimag(x*y)); printf ("\n");

_Complex double z = cexp(I * M_PI); printf("z = e^(pi*I) = %f + %f * i\n", creal(z), cimag(z));

return 0;}

De flesta matematiska funktionerna i c-biblioteket har en komplex

motsvarighet:

cabs(z) absolutbelopp |z|

carg(z) argument (vinkel)

cexp(z) ez

cpow(z,w) zw

creal(z) realdel

cimag(z) imaginärdel

Arrayer och pekare

Pekare och arrayer i C har ett starkt samband. Det här är viktigt då

man har data i en array och vill anropa en funktion som behandlar

datan, eftersom man inte direkt kan ge en array som parameter till en

funktion. I stället ger man adressen där arrayen börjar, dvs. en pekare

till dess första element, och ofta också antalet element.

Exempel. Att skicka en array till en funktion.#include <stdio.h>#include <math.h>void skriv (int *a, int n);

int main (){ int i; int x[10];

for (i = 0; i < 10; i++) x[i] = 0;

x[1] = 3; x[8] = 2; skriv (x, 10); //"x" ger adressen av det första // elementet return 0;}

void skriv (int *a, int n){ int i; for (i = 0; i < n; i++) printf ("%d ", a[i]);

printf ("\n");}

Två viktiga egenskaper hos arrayer används i exemplet.

1) Namnet på en array, utan [], är adressen av dess första ele-

ment. Funktionen skriv ska ges adressen av det första elementet som

argument, här "x".

2) En pekare följd av [i] är det i:te elementet räknat från pekaren.

I funktionen skriv är a en pekare till det första elementet av arrayen.

a[i] är arrayens i:te element.

Ännu en egenskap:

*(a+i) betyder detsamma som a[i]. Alltså, en pekare a plus ett heltal

i ger en pekare till det i:te följande elementet.

8

Page 9: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Listor.

ListorNärmare beskrivningar finns i den löpande texten.

Funktioner

exit avslutar programmetfclose stänger en filfopen öppnar en fil

fprintf skriver till en filfscanf läser från en filperror skriver ut ett felmeddelandeprintf skriver till terminalenscanf läser från terminalen

Datatyper

char teckendouble decimaltal

_Complex double komplext decimaltal, a + I*bfloat decimaltalint heltallong

short

signed

unsigned

void "ingenting"

Nyckelord

break avbryter den innersta loopenelse

for

if

return

while

Argument för printf

\n ny rad\t tab%d en int%f en double

%.3f en double med 3 decimaler%e en double i exponentiell form

Bibliotek

#include <complex.h> komplexa tal#include <math.h> matematikfuntioner#include <stdio.h> input och output#include <stdlib.h> standardbiblioteket (innehåller exit)

Kompileringsflaggor

-c endast kompilering (länkar inte)-o .exe-filens namn efter -o-lm länka till matematikbiblioteket

-Wall visa alla varningar-O3 optimera räknehastigeten

9

Page 10: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 4.

Lektion 4

Fraktaler

Låt och vara komplexa tal. Definiera

För vissa är följden begränsad, för andra är den obegränsad.

Mandelbrotmängden är mängden av de , för vilka följden är be-

gränsad.

Exempelprogrammet nedan beräknar följden för olika genom att an-

ropa funktionen iterate, och visar om sekvensen är begränsad eller

inte. Programmet skriver ut en bild av det komplexa planet, där x mot-

svarar realdelen och y imaginärdelen av . Bilden visar området Rmin

Re(c) Rmax, Imin Im(c) Imax, som en ruta av tecken med

måtten X*Y. Ett "X" skrivs ut om följden är begränsad för det som

motsvarar tecknets position, annars skrivs ett mellanrum ut.

Funktionen iterate (z0, c, maxIter) returnerar det minsta n sådant att

, eller maxIter, ifall hållits innanför gränsen i maxIter iter-

ationer. Om för något , är serien obegränsad, och beräk-

ningen kan avbrytas. Om inte gått utanför gränsen efter maxIter

iterationer, antar man att följden är begränsad och beräkningen av-

bryts.

Exempelprogram. Ritar Mandelbrotmängden på skärmen.#include <math.h>#include <complex.h>#include <stdio.h>

int iterate (_Complex double z0, _Complex double c, int maxIter);

int main (){ double r = -.7; //mittpunktens realdel double i = 0; //mittpunktens imaginärdel double s = 3; //sidlängd double Rmin = r - s/2; //det minsta värdet på Re(c) double Rmax = r + s/2; double Imin = i - s/2; //det minsta värdet på Im(c) double Imax = i + s/2;

int X = 80; //antal kolumner i bilden int Y = 40; //antal rader int x, y; _Complex double c; for (y = 0; y < Y; y++) { for (x = 0; x < X; x++)

{ c = (double)x/X * (Rmax-Rmin) + Rmin; c += ((double)y/Y * (Imax-Imin) + Imin) * I;

int k = iterate(0, c, 1000); if (k == 1000) printf ("X"); else printf (" ");}

printf ("\n"); } return 0;}

int iterate (_Complex double z0, _Complex double c, int maxIter){ double R = 2; int i; _Complex double z = z0; for (i = 0; i < maxIter; i++) { z = z*z + c; if (cabs(z) > R)

break; } return i;}

Grafik med biblioteket SDL

Med biblioteket SDL kan man relativt enkelt skriva program som visar

grafik på skärmen. SDL (Simple DirectMedia Layer) är planerat för

främst för spelprogrammering. Målet är att spel som använder biblio-

teket ska vara portabla till alla system som SDL finns till. SDL fungerar

bl.a. på Linux, MacOS och Windows. För dokumentation och installa-

tionsinstruktioner, se länkarna på kurshemsidan.

Exempelprogram. Grafik med SDL.#include <SDL/SDL.h> #include <stdio.h>#include <stdlib.h>#include <math.h>

void dieSDL (char *reason);void putPixel (SDL_Surface *s, int x, int y, int c);SDL_Surface * makeWindow (int width, int height);void waitAndClose ();

int main(int argc, char *argv[]) { int width = 500; // fönstrets bredd i pixlar int height = 500; SDL_Surface * screen; screen = makeWindow (width, height); SDL_LockSurface (screen); //Nu får vi rita... putPixel (screen, 10, 10, 255); putPixel (screen, 11, 10, 255); putPixel (screen, 10, 11, 255); putPixel (screen, 11, 11, 255); int x, y; for (y = 0; y < 256; y++) for (x = 0; x < 256; x++) putPixel (screen, x+10, y+100, y*256 + x); for (y = -10; y < 10; y++) for (x = -10; x < 10; x++) if (hypot(x, y) < 10)

putPixel (screen, x+50, y+200, 255*256*256);

SDL_UnlockSurface (screen); //Visa den nya bilden: SDL_Flip(screen); waitAndClose (); return 0;}

10

Page 11: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 4.

//Endast för 32 bits per pixelvoid putPixel (SDL_Surface *s, int x, int y, int c){ int bpp = s->format->BytesPerPixel; /* Here p is the address to the pixel we want to set */ int *p = (int *)(s->pixels + y * s->pitch + x * bpp); *p = c;}

void dieSDL (char *reason){ fprintf(stderr, reason, SDL_GetError()); exit (1);}

SDL_Surface * makeWindow(int width, int height){ SDL_Surface *screen; //Skapa ett fönster att rita i //SetVideoMode tar parametrarna: bredd, höjd, bitar/pixel, flaggor if( SDL_Init(SDL_INIT_VIDEO) < 0 ) dieSDL ("SDL init failed: %s\n"); screen = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE|

SDL_DOUBLEBUF); if (screen == NULL) dieSDL ("SDL_SetVideoMode failed: %s\n"); return screen;}

void waitAndClose (){ //Väntar på att man stänger fönstret SDL_Event event; int quit = 0; while (!quit) { SDL_WaitEvent(&event); if (event.type == SDL_QUIT)

{ quit = 1; break;}

} SDL_Quit();}

KompileringI linux kan programmet kompileras med flaggorna: -Wall -O3 -lm -lSDL

I windows kan man använda -Wall -O3 -lm -lmingw32 -lSDLmain -lSDL

Observera att -l flaggorna måste vara i denna ordning.

Funktionen main är definierad såhär, int main(int argc, char *argv[]),

eftersom kompileringen inte fungerar i Windows med en tom para-

meterlista. Parametrarna till main är antalet argument på kommando-

raden och en array av pekare till dem, men de används inte här.

Strukturer

SDL_Surface är ett exempel på en struktur. Den är definierad få man

har inkluderat SDL.h. typedef definierar en ny datatyp, här med nam-

net SDL_Surface. struct definierar en struktur, dvs. en sammansatt

datatyp som innehåller flera variabler. Strukturer används för att

gruppera data som hör ihop.

typedef struct SDL_Surface { Uint32 flags; /* Read-only */ SDL_PixelFormat *format; /* Read-only */ int w, h; /* Read-only */ Uint16 pitch; /* Read-only */ void *pixels; /* Read-write */ SDL_Rect clip_rect; /* Read-only */ int refcount; /* Read-mostly */ /* This structure also contains private fields not shown here */} SDL_Surface;

Här är SDL_PixelFormat och SDL_Rect andra strukturer som också defi-

nieras i SDL. Uint16 och Uint32 är definierade i SDL som 16 och 32

bitars unsigned int. Deklarationen void* pixels definierar en pekare

utan typ. Den måste explicit konverteras till rätt typ innan den kan

användas. Det här behövs, eftersom pixeldatat kan lagras på olika sätt,

med olika antal bitar per pixel (se nedan).

Exempel. Operatorerna "." och "->"SDL_surface s; //s är av typen SDL_SurfaceSDL_surface *p; //en pekare till en SDL_Surfacep = &s;s.w // "." används för att komma åt ett element i sp->w // "->" används då man har en pekare till en struktur

11

Page 12: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 4.

Ett grafiskt mandelbrotprogram

Exempelprogram. Ritar mandelbrotfraktalen i ett fönster med SDL.#include <SDL/SDL.h> #include <stdio.h>#include <stdlib.h>#include <math.h>#include <complex.h>

void dieSDL (char *reason);int iterate (_Complex double z0, _Complex double c, int maxIter);void putPixel (SDL_Surface *s, int x, int y, int c);SDL_Surface * makeWindow (int width, int height);void waitAndClose ();

int main(int argc, char *argv[]) { int width = 800; // fönstrets bredd i pixlar int height = 800; SDL_Surface * screen; screen = makeWindow (width, height);

double r = -.7; //mittpunkt double i = 0; double s = 3; //storlek double Rmin = r - s/2; double Rmax = r + s/2; double Imin = i - s/2; double Imax = i + s/2; int x, y, iterations, color; int MAXiter = 1000; _Complex double c;

for (y = 0; y < height; y++) { SDL_LockSurface (screen); for (x = 0; x < width; x++)

{ c = (double)x/width * (Rmax-Rmin) + Rmin; c += ((double)y/height * (Imax-Imin) + Imin) * I;

iterations = iterate(0, c, MAXiter); if (iterations == MAXiter) color = 0; else color = 255*log(iterations)/log(MAXiter); putPixel (screen, x, y, color);}

SDL_UnlockSurface (screen); SDL_Flip(screen); } if (SDL_SaveBMP(screen, "mandel.bmp") < 0) dieSDL ("SDL_SaveBMP failed: %s\n");

waitAndClose (); return 0;}

Funktionerna iterate, dieSDL, putPixel, makeWindow och waitAndCloseär definierade som tidigare.

Färger och pixlar i SDL

I anropet av SDL_SetVideoMode ska man ange i vilket format bilderna

behandlas. Här använder vi 32 bits per pixel, dvs. färgen för varje pixel

lagras i ett 32 bits tal. Också 8, 16 eller 24 bitar per pixel används

ibland. Färgerna beskrivs med tre komponenter: röd, grön och blå.

Hur mycket av varje färg som ingår anges med ett heltal mellan 0 och

255 (8 bitar per färg). De 32 bitarna för en pixel används såhär: 00000000 rrrrrrrr gggggggg bbbbbbbb

Det här lagringssättet slösar 8 bitar för varje pixel, men det lönar sig

eftersom 32 bitar är en mer praktisk storlek än 24. För att få ett

färgvärde c då man har värden på komponenterna r, g, och b kan man

använda c = r*256*256 + g*256 + b;

eller c = (r << 16) + (g << 8) + b;

"<<" är en vänster-shift-operation.

Pixlarna i en bild lagras radvis, efter varandra, börjande från det övre

vänstra hörnet av bilden. Den första pixelns adress anges i elementet

pixels. För varje rad i bilden reserveras det antal bytes som anges i

pitch. pitch är minst lika stor som antalet byte per pixel * bildens

bredd, men kan vara större av tekniska orsaker. För att komma åt

pixeln på koordinaterna (x, y) kan man använda: int *p = (int *)(s->pixels + y * s->pitch + x * bpp);

*p = c;

Beräkningar med void-pointers görs i bytes, därför multipliceras y med

antalet bytes per rad och x med antalet bytes per pixel. Då adressen är

beräknad konverteras den till en int pointer med (int *), eftersom vi i

det här fallet vill använda adressen för att skriva en 32 bits pixel.

Fler färgerFör att få fler färger i bilden av mandelbrotfraktalen, kan man färg-

lägga det yttre området, där följden är obegränsad, enligt hur många

iterationer som behövdes innan gick utanför gränsen. Ett förslag:color = 255 * log(k) / log(MAX);

12

Page 13: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 5.

Lektion 5

Ordinära differentialekvationer

En ordinär differentialekvation av första ordningen kan skrivas i

formen , där är någon given funktion. Lösningen till

ekvationen är funktionen , som kan bestämmas entydigt, om man

har ett randvillkor, t.ex. .

En ekvation av den här typen kan lösas med Eulers metod:

Man startar alltså i med (randvillkoret). Sedan

beräknar man approximativa värden på i punkterna ,

osv. Då steglängden blir det här en allt

noggrannare approximation av .

Exempelprogram. Löser x(t) med Eulers metod, då

#include <stdio.h>#include <math.h>

double time_derivative (double x, double t);double step (double x, double t, double dt);

int main (){ double t, dt = 0.1, tmax = 8; double x, x0 = 0; x = x0; for (t = 0; t <= tmax; t += dt) { printf ("%f %f\n", t, x); x = step (x, t, dt); } return 0;}

double time_derivative (double x, double t){ return cos(t);}

double step (double x, double t, double dt){ return x + dt * time_derivative (x, t);}

Differentialekvationer av högre ordning

Differentialekvationer av högre ordning är viktiga i fysiken, t.ex.

Newtons lagar kan skrivas som andra ordningens differentialekva-

tioner. Differentialekvationer av högre ordning kan skrivas om som ett

system av kopplade första ordningens ekvationer.

Exempel: Rörelse i en dimension

En partikel med massan m som påverkas av en kraft F accelererar med

accelerationen a, enligt Newtons andra lag . Accelerationen

är positionens andra derivata:

Det här kan skrivas om som två första ordningens ekvationer:

, ,

där v är partikelns hastighet.

Ekvationerna skrivs alltså som derivatan av en funktion uttryckt med

funktionerna utan derivator. Ett ekvationssystem av den här typen kan

lösas med Eulers metod, se nedan.

Kopplade differentialekvationer

Betrakta ett system av kopplade första ordningens differentialekva-

tioner:

, , osv,

där , , ... är givna funktioner, och kan bero av och alla .

Det här systemet kan lösas med Eulers metod på samma sätt som en

ensam ekvation:

I ett program beräknar man alltså derivatan för alla och adderar

sedan till varje dess derivata gånger .

Ekvationerna för ett tidssteg för rörelseekvationen i exemplet ovan

blir:

där kan bero av t, x och v, beroende på problemet som studeras.

13

Page 14: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 5.

En pendel

Differentialekvationen för pendelns rörelse är

.

För att kunna lösa ekvationen analytiskt, antar man att vinkeln är

liten, och att . Lösningen blir då , där

. Exempelprogrammet nedan löser numeriskt, utan

approximationen att vinkeln är liten.

Exempelprogram. En pendel.#include <stdio.h>#include <math.h>void time_derivative (int N, double *x, double *dx, double t);void step (int N, double *x, double t, double dt);

double g = 9.81; // m/s^2 gravitationeaccelerationendouble l = 1; // m pendelns längddouble A = .1; // rad. utgångsvinkel int main (){ double t, dt = 0.001, tmax = 10; double x[2]; x[0] = A; //theta x[1] = 0; //theta'

for (t = 0; t <= tmax; t+= dt) { printf ("%f %f %f\n", t, x[0], x[1]); step (2, x, t, dt); } return 0;}

void time_derivative (int N, double *x, double *dx, double t){ dx[0] = x[1]; dx[1] = -g/l*sin(x[0]);}

void step (int N, double *x, double t, double dt){ int i; double dx [N];

time_derivative (N, x, dx, t);

for (i = 0; i < N; i ++) x[i] += dx[i] * dt;}

Differentialekvationen skrivs om till två kopplade första ordningens

ekvationer. Variablerna lagras i arrayen x, så att vinkeln finns i x[0]

och vinkelhastigheten i x[1]. Ekvationerna har nu formen

, .

Derivatorna beräknas i funktionen time_derivative. Tidssteget utförs

i funktionen step. Variablerna är placerade i en array på det här sättet

för att programmet ska vara mer allmänt. Om man vill lösa ett annat

problem måste man skriva in ekvationerna för det nya problemet i

funktionen time_derivative och möjligen ändra längden på arrayen x i

main, men funktionen step kan användas som den är. En array är också

praktisk om man har många likadana variabler, t.ex. ett system av

kopplade pendlar. I det här exemplet kunde man istället ha använt två

skilda variabler för vinkeln och dess derivata, men då hade också

funktionen för tidssteget blivit specifik för just det här problemet.

För varje tidssteg skriver programmet ut t, och . Värdena skrivs

till skärmen, men kan fås i en fil om man kör programmet såhär:./pendel > filnamn.dat

Resultatet kan plottas med gnuplot:

plot "filnamn.dat" using 1:2 with lines

Fler exempel finns på kurshemsidan.

Mer information om lösning av differentialekvationer finns i

W. H. Press et al.: Numerical Recipes in C - The art of scientific com-

puting, 2:nd edition (Cambridge university press 1992)

Gnu Scientific Library (GSL) innehåller funktioner för lösning av

ODE:r: http://www.gnu.org/software/gsl/

14

Page 15: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 6.

Lektion 6

Diskretisering

Om man vill behandla en kontinuerlig funktion av , , kan man

diskretisera den, så att man håller reda på funktionens värden vid

vissa diskreta -värden. Typiskt väljer man dem med jämna mellan-

rum i något intervall. Om man är intresserad av funktionen mellan

och , och vill beräkna i N punkter , kan

man välja

där är avståndet mellan två punkter. Med det här valet är

och , dvs. ändpunkterna av intervallet

finns med som diskretiseringspunkter.

Funktionsvärdena kan nu sparas i en array. Det kan löna

sig att ha en array också för -värdena, så att man slipper beräkna

dem varje gång de behövs.

Exempel. Diskretisering i ett intervall. double Xmax = 2; //meter double Xmin = 1; //meter int N = 20; int i; double dx = (Xmax - Xmin) / (N-1);

//Gör en array för x-värden for (i = 0; i < N; i++) x[i] = Xmin + i * dx;

Diskretisering av derivator

Om man har en diskretisering av en funktion , kan man använda

den för att approximera funktionens derivata.

Derivatan definieras som gränsvärdet

,

men kan approximeras så att man använder ett ändligt

Det lönar sig i allmänhet att använda ett symmetriskt uttryck, även om

man kunde ha använt skillnaden i stället. På motsvarande

sätt kan den andra derivatan beräknas:

Exempel. Funktioner för att beräkna derivatan och andra derivatan

för en diskretiserad funktion fdouble derivata (int i, double deltax, double *f){ return (f[i+1] - f[i-1]) / (2*deltax);}double andraderivata (int i, double deltax, double *f){ return (f[i+1] + f[i-1] - 2*f[i]) / (deltax*deltax);}

Schrödingerekvationen

I kvantmekaniken beskrivs en partikel med en vågfunktion, ofta

beteknad med (psi). Vågfunktionen har ett komplext värde i varje

punkt av rymden, och är i allmänhet tidsberoende. Sannolikhetstät-

heten för att hitta partikeln i punkten x vid tiden t är:

Hur vågfunktionen utvecklas i tiden beskrivs av Schrödingerekvationen,

här given för en dimension:

,

där V(x) är potentialen. Schrödingerekvationen är en partiell differen-

tialekvation, eftersom den innehåller derivator både med avseende på

x och t.

Den första termen på höger sida beskriver partikelns kinetiska energi,

och får partiklarna att röra sig. Utan den här termen kommer

sannolikhetstätheten P(x,t) inte att ändras i tiden.

Numerisk lösning av SchrödingerekvationenVi ska nu lösa Schrödingerekvationen för en partikel som rör sig i en

endimensionell potential. Vi diskretiserar x-koordinaten, och byter ut

andraderivatan med avseende på x mot den diskretiserade versionen

som gavs ovan. Nu har vi ett system av kopplade första ordningens

ordinära differentialekvationer, som kan lösas med Eulers metod från

förra föreläsningen. (Ekvationerna är ordinära nu, eftersom det endast

finns en derivata med avseende på tiden.)

Exempelprogram. Löser Schrödingerekvationen, ritar med SDL. #include <stdio.h>#include <math.h>#include <complex.h>#include <SDL/SDL.h>#include <string.h>

_Complex double E_kin_psi (int i, double deltax, _Complex double *psi);void time_derivative (int N, _Complex double *psi, _Complex double *dpsi, double *V, double dx, double t);void step (int N, _Complex double *psi, double *V,

double t, double dx, double dt);void discretize (int N, double *x);void initialize (int N, double *x, _Complex double *psi);void normalize (int N, double deltax, _Complex double *psi);void potential (int N, double *x, double *potential);

void rita (SDL_Surface *screen, int N, double ymax, double *x, _Complex double *psi, double *V);void graph (SDL_Surface *screen, double xmin, double xmax, double ymin, double ymax, int N, double *x, double *y, int col);double max_sannolikhet(int N, _Complex double *psi);void skriv (int N, double *x, _Complex double *psi);void putPixel (SDL_Surface *s, int x, int y, int c);int RGBtoInt (int r, int g, int b);void clearScreen (SDL_Surface *s);SDL_Surface * makeWindow (int width, int height);void dieSDL (char *reason);int testIfQuit ();

#define Hbar 1.054e-34 //Js#define Me 9.109e-31 //kg

double Xmin = -1e-7; //mdouble Xmax = 1e-7; //mdouble m = Me;

int main (){ int width = 500, height = 500, frame = 0; SDL_Surface * screen; screen = makeWindow (width, height); double ymax;

double t, dt = .02e-15, tframe = 1e-14;

15

Page 16: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 6.

int N = 80; //Antal punkter i x-led double dx = (Xmax - Xmin) / (N-1); double x[N], V[N]; _Complex double psi[N];

discretize (N, x); initialize (N, x, psi); normalize (N, dx, psi); potential(N, x, V); ymax = max_sannolikhet(N, psi); t = 0; while (1) { frame++; for (; t <= tframe*frame; t+= dt)

step (N, psi, V, t, dx, dt); rita (screen, N, ymax, x, psi, V); if (testIfQuit())

break; }

SDL_Quit(); return 0;}

void discretize (int N, double *x){ int i; double dx = (Xmax - Xmin) / (N-1);

for (i = 0; i < N; i++) x[i] = Xmin + i * dx;}

void initialize (int N, double *x, _Complex double *psi){ int i; double sigma = .3e-7; //initialbredd för gaussisk vågfunktion double x0 = Xmax/7; double k = 0; // 1/m

for (i = 0; i < N; i++) { psi[i] = cexp (- pow((x[i]-x0)/sigma, 2) + I*k*x[i]) ; }}

void normalize (int N, double deltax, _Complex double *psi){ int i; double summa = 0; for (i = 0; i < N; i++) summa += deltax*cabs(psi[i] * psi[i]);

for (i = 0; i < N; i++) psi[i] /= sqrt(summa);}

void potential (int N, double *x, double *V){ int i; for (i = 0; i < N; i++) { V[i] = sin(x[i]/2/Xmax*M_PI)*sin(x[i]/2/Xmax*M_PI)*1e-22; }}

_Complex double E_kin_psi (int i, double deltax, _Complex double *psi){ return -Hbar*Hbar/(2*m)*(psi[i+1] + psi[i-1] - 2*psi[i]) / (deltax*deltax);}

void time_derivative (int N, _Complex double *psi, _Complex double *dpsi, double *V, double dx, double t)

{ int i; for (i = 1; i < N-1; i++) dpsi[i] = -I/Hbar * ( E_kin_psi(i, dx, psi) + V[i]*psi[i] );

dpsi [0] = 0; //specialfall för kanterna. dpsi[N-1] = 0;}

void step (int N, _Complex double *psi, double *V, double t, double dx, double dt){

int i; _Complex double dpsi [N];

time_derivative (N, psi, dpsi, V, dx, t);

for (i = 0; i < N; i ++) psi[i] += dpsi[i] * dt;}

void rita (SDL_Surface *screen, int N, double ymax, double *x, _Complex double *psi, double *V){ double p[N]; int c; int j; for (j = 0; j < N; j++) { p[j]= cabs(psi[j]*psi[j]); } SDL_LockSurface (screen); clearScreen (screen); c = RGBtoInt (0, 255, 128); graph (screen, Xmin, Xmax, 0, 3*ymax, N, x, p, c); //ritar sannolikhetstätheten

c = RGBtoInt (255, 128, 64); graph (screen, Xmin, Xmax, 0, 2*V[0], N, x, V, c); //ritar potentialen

SDL_UnlockSurface (screen); SDL_Flip(screen);}

void graph (SDL_Surface *screen, double xmin, double xmax, double ymin, double ymax, int N, double *x, double *y, int col){ int i; int width = screen->w; int height = screen->h; for (i = 0; i < N; i++) { int xs, ys; //Koordinater på skärmen xs = (x[i] - xmin)/(xmax - xmin) * (width-1); ys = height - 1 - (y[i] - ymin)/(ymax - ymin) * (height-1); if (xs >= 0 && ys >= 0 && xs < width && ys < height)

putPixel (screen, xs, ys, col); }}

double max_sannolikhet(int N, _Complex double *psi){ int i; double ymax = 0; for (i = 0; i < N; i++) { if (cabs(psi[i]*psi[i]) > ymax)

ymax = cabs(psi[i]*psi[i]); } return ymax;}

int RGBtoInt (int r, int g, int b){ return (r << 16) + (g << 8) + b;}

void skriv (int N, double *x, _Complex double *psi){ int i; for (i = 0; i < N; i++) printf ("%.10g %.10g\n", x[i], cabs(psi[i]*psi[i])); printf ("\n\n");}

SDL_Surface * makeWindow(int width, int height){ SDL_Surface *screen; if( SDL_Init(SDL_INIT_VIDEO) < 0 ) dieSDL ("SDL init failed: %s\n"); screen = SDL_SetVideoMode(width, height, 32, SDL_SWSURFACE|SDL_DOUBLEBUF); if (screen == NULL) dieSDL ("SDL_SetVideoMode failed: %s\n"); return screen;}

void dieSDL (char *reason){ fprintf(stderr, reason, SDL_GetError()); exit (1);}

16

Page 17: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 6.

void putPixel (SDL_Surface *s, int x, int y, int c){ int bpp = s->format->BytesPerPixel; int *p = (int *)(s->pixels + y * s->pitch + x * bpp); *p = c;}

void clearScreen (SDL_Surface *s){ int bpp = s->format->BytesPerPixel; int x, y; int *p;

for (y = 0; y < s->h; y++) for (x = 0; x < s->w; x++) {

p = (int *)(s->pixels + y * s->pitch + x * bpp);*p = 0;

}}

int testIfQuit (){ SDL_Event event; while (SDL_PollEvent(&event)) if (event.type == SDL_QUIT) return 1; return 0;}

17

Page 18: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 7.

Lektion 7

Slumptal

Funktionen random() i stdlib.h returnerar ett "slumpmässigt" heltal

mellan 0 och konstanten RAND_MAX. Om man vill ha ett slumpmässigt

tal mellan 0 och R kan man skala resultatet från random().r = random ();s = (double) R * r / RAND_MAX;

Konverteringen till double görs för att undvika fel pga. avrundning.

Exempelprogram. Genererar några slumptal.#include <stdio.h>#include <stdlib.h>#include <math.h>

int main (){ int i, r, s; printf ("RAND_MAX är %d\n", RAND_MAX); printf ("2^31 = %.0f\n", pow(2, 31));

for (i = 0; i < 20; i++) { r = random (); s = 10.0 * r / RAND_MAX; printf ("r = %d, s = %d\n", r, s); } return 0;}

Varje gång programmet körs genereras samma sekvens av tal. Om man

vill ha en ny sekvens varje gång kan man använda funktionen srandom,

som sätter slumptalsgeneratorn i ett givet tillstånd. Startvärdet kan tas

t.ex. från klocktiden, med kommandot srandom(time(NULL)).

Slumpvandring

Med slumpvandring avses en process där en partikel förflyttar sig med

slumpmässigt valda steg. Slumvandring kan användas som modell för

Brownsk rörelse och diffusion. Exempelprogrammet nedan simulerar

slumpvandring i ett tvådimensionellt gitter, dvs. alla steg har längden

1 och görs i en av de fyra riktningarna upp, ner, vänster eller höger.

Exempelprogram. Slumpvandring i ett 2D-gitter#include <stdio.h>#include <stdlib.h>#include <math.h>#include <time.h>

int main (){ srandom (time(NULL));

int x = 0, y = 0; int r; int i; double d;

for (i = 0; i < 10000; i++) { r = 4.0 * random () / RAND_MAX; switch (r)

{case 0: x++; //höger break;case 1: x--; //vänster

break;case 2: y++; //upp break;case 3: y--; //ner break;default: printf ("Omöjligt!\n"); break;}

d = sqrt(x*x + y*y); printf ("%d %d %d %f\n", i, x, y, d); } return 0;}

Här introducerades structuren switch - case, som kan användas för att

välja ett av flera alternativ, beroende på värdet av en variabel. Varje

fall avslutas med break. Utan kommandot break skulle programmet

fortsätta med koden för nästa fall, vilket är tillåtet men oftast inte vad

som önskas. Fallet default utförs om inget av de givna alternativen

utfördes. Observera att varje case-rad avslutas med ett kolon.

Olika fördelningar

Funktionen random returnerar ett heltal mellan 0 och RAND_MAX, med en

likformig fördelning. Ofta behöver man slumptal med andra egen-

skaper, t.ex. ett decimaltal i ett givet intervall eller med någon annan

fördelning än den likformiga.

DefinitionerOm är en stokastisk variabel med någon fördelning, definieras dess

fördelningsfunktion

Om fördelningen är kontinuerlig, definieras sannolikhetstätheten

så att

Likformig fördelning i ett intervall

Funktionen uniform (a, b) returnerar en slumpmässig double mellan a

och b, med likformig fördelning.

double uniform (double a, double b){ return (b-a) * random() / RAND_MAX + a;}

Exponentiell fördelningEn exponentiell fördelning har fördelningsfunktionen

och sannolikhetstätheten

.

18

Page 19: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 7.

Den exponentiella fördelningen förekommer till exempel vid

radioaktivt sönderfall. Tiden mellan två händelser ("klick från

Geigerräknaren") är exponentiellt fördelad.

Om stokastisk variabel som är likformigt fördelad mellan 0 och 1,

kan man få en exponentiellt fördelad variabel genom att beräkna

.

Motivering:

,

dvs. har en exponentiell fördelning med . Om man vill ha ett

annat , kan man skala genom att multiplicera med en konstant.

//Slumptal med exponentiell fördelning. Lambda = 1. double exponential () { return -log(uniform(0,1)); }

Samma metod kan användas för att generera andra fördelningar, om

man känner fördelningsfunktionen och om den inversa funktionen går

att beräkna.

Gaussisk fördelning / normalfördelning

För en Gaussisk fördelning ges sannolikhetstätheten av

.

Slumptal med en Gaussisk fördelning kan genereras med Box-Müller-

metoden, som är enklare och effektivare än metoden med invers

funktion som presenterades ovan. Metoden använder två likformigt

fördelade slumptal och producerar två oberoende slumptal med en

Gaussisk fördelning. I ett polärt koordinatsystem väljs vinkeln lik-

formigt i intervallet och radien väljs så att har en

exponentiell fördelning (med metoden ovan). x- och y-koordinaterna

för den valda punkten har nu en Gaussisk fördelning, med väntevärdet

och standardavvikelsen .

double gaussian () { static double next; static int gotOne = 0; double fi, x;

if (gotOne) { gotOne = 0; return next; }

fi = uniform(0, 1) * 2 * PI; x = sqrt(-2*log(uniform(0,1))); next = x * sin (fi); //Two independent numbers are generated gotOne = 1; return x*cos(fi); }

Versionen nedan undviker de trigonometriska funktionerna, genom att

välja x- och y-koordinater för punkten. Koordinaterna väljs i intervallet

, med kravet att punkten ligger innanför enhetscirkeln. Om

punkten ligger utanför enhetscirkeln genereras koordinaterna pånytt. // Gaussian random number, average=0, variance=1 double gaussian() { static double next; static int gotOne = 0; double x, y, r;

if (gotOne) { gotOne = 0; return next; } do { x = uniform(-1,1); y = uniform(-1,1); r = x*x + y*y; } while (r > 1); r = sqrt(-2 * log (r) / r);

gotOne = 1; next = y * r; return x * r; }

Diffusion-Limited Aggregation

Vi ska se på en slumpvandringsprocess med flera partiklar, där

partiklarna kan fastna. En partikel i gången får slumpvandra omkring i

ett gitter (här i två dimensioner). Partikeln fastnar om den kommer

till en gitterpunkt bredvid en partikel som redan fastnat. Beräkningen

börjar från ett läge där det endast finns en fixerad partikel, den kan

placeras i origo.

Kvaliteten på slumptal

Hur man genererar en sekvens av ”slumpmässiga” tal på en

deterministisk dator är ett komplext ämne. I allmänhet vill man ha en

sekvens av tal där varje tal förekommer lika ofta och där korrelationen

mellan talen är möjligast liten. Standardbibliotekets random()-funktion

är inte den bästa, och kvaliteten kan variera beroende på vilken C-

kompilator och vilket bibliotek man använder. Se Knuth, The Art of

Computer Programming vol 2 för en grundlig behandling av slumptal.

En bra slumptalsgenerator är Mersenne Twister, se Wikipedia och

http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html .

19

Page 20: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 8.

Lektion 8

Linjär algebra med LAPACK

LAPACK är ett programbibliotek för linjär algebra. Det innehåller t.ex.

funktioner för att lösa ekvationer i formen

,

där är en matris och och är vektorer. LAPACK innehåller också

funktioner för att hitta egenvärden och egenvektorer för en matris.

Programpaketen Matlab och Octave bygger på LAPACK-biblioteket, så

om man har ett specifikt problem som man vill lösa en enda gång kan

det vara enklare att använda något av dem i stället för att själv skriva

ett program som anropar funktionerna i LAPACK.

Egenvärden och egenvektorer

En egenvektor för matrisen är någon vektor som uppfyller

ekvationen

,

där är ett tal. kallas egen-

värde till matrisen (som

motsvarar egenvektorn ). T.ex.

har en tre-gånger-tre-matris

(oftast) tre olika egenvektorer, och

dessa har varsitt egenvärde.

Egenvärdena och -vektorena ger en

massa nyttig information om matrisens egenskaper. Mer om sådant

här kommer det t.ex. i kursen Linjär algebra på matematikinsten.

Att anropa FORTRAN-funktioner från C

LAPACK är skrivet i FORTRAN. Funktionerna i biblioteket kan ändå

anropas från C, bara man följer reglerna nedan.

- Byt alla parametrar till pointers. FORTRAN använder call-by-

reference.

- Skriv en funktionsprototyp.

- Lägg till ett understreck efter funktionsnamnet.

- Transponera matriserna. FORTRAN sparar matriser så att elementen

i en kolumn lagras efter varandra i minnet. I C lagras elementen

radvis.

Exempel. Funktionsprototyp för dgeev, som löser egenvärdesproblem.void dgeev_ (char *jobvl, char *jobvr, int *n, double *A, int *lda,

double *wr, double *wi, double *vl, int *ldvl, double *vr, int *ldvr, double *work, int *lwork, int *info ); //Prototyp

Egenfunktioner för

Schrödingerekvationen

Schrödingerekvationen, för vilket som helst:

Om är ett s.k. egentillstånd gäller dessutom:

där är ett energiegenvärde. För att ränkna ut vilka egenenergier

och egentillstånd som är möjliga i något givet system (dvs. för någon

potential ), löser man ekvationen

för och . Det här kan göras t.ex. numeriskt - då diskretiserar man

x-rymden på samma sätt som tidigare. blir en vektor, psi[i], och

högra sidan av ekvationen ovan kan skrivas som en matris gånger

vektorn psi. I matematisk vektor- och matrisnotation kan man skriva

ekvationen på det här sättet:

Det här är en egenvärdesekvation, där matrisen som kallades mot-

svaras av matrisen innanför de stora hakparenteserna. motsvaras av

. Ettorna och tvåorna i den första termen i det högra ledet kommer

från diskretiseringen av den andra derivatan i x, som beskrevs i

Lektion 6.

Exempelprogram

Ett program som löser alla egenvärden och -funktioner för en

potentialbrunn och för en parabolisk potential.

Egenfunktionerna beräknas genom att anropa LAPACK-funktionen

dgeev. Egenvärdena och -vektorerna sorteras, så att egenvärdena är i

stigande ordning.

#include <stdio.h>#include <stdlib.h>#include <math.h>#include <complex.h>

#define Hbar 1.054e-34 //Js#define Me 9.109e-31 //kg

double Xmin = -1e-7; //mdouble Xmax = 1e-7; //mdouble m = Me;#define k (1e-21/(Xmax*Xmax))//konstanten i den paraboliska potentialen

void dgeev_ (char *jobvl, char *jobvr, int *n, double *A, int *lda, double *wr, double *wi, double *vl, int *ldvl, double

*vr, int *ldvr, double *work, int *lwork, int *info );

void solve (double *M, int N, double *E, double *V);

void discretize (int N, double *x);void potential (int N, double *x, double *potential);void hamiltonian (int N, double *H, double *V, double dx);void analytisktE (int N, double *x);

20

Page 21: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 8.

double *energies; //Används för att sortera egenärdena

int main (){ int i, j; int N = 200; //Antal punkter i x-led double dx = (Xmax - Xmin) / (N-1); double x[N], V[N]; double H[N*N]; double E[N]; double vectors [N*N]; discretize (N, x); potential(N, x, V); hamiltonian (N, H, V, dx); solve(H, N, E, vectors);

FILE *out; //Skriv egenvärdena (endast realdelen) out = fopen("E.dat", "w"); if (out == NULL) { perror ("E.dat"); exit (1); } for (i = 0; i < N; i++) { fprintf (out, "%d %g\n", i, E[i]); } fclose (out);

//Skriv egenvektorerna out = fopen("psi.dat", "w"); if (out == NULL) { perror ("psi.dat"); exit (1); } for (j = 0; j < N; j++) { for (i = 0; i < N; i++)

fprintf (out, "%g %g\n", x[i], vectors[i+N*j]); fprintf (out, "\n\n"); }

fclose (out);

analytisktE (N, x); return 0;}

/*skriv ut de teoretiska energinivåernaför en potentialbrunn och för en parabolisk potentialsom jämförelse.*/void analytisktE (int N, double *x){ int i; double Ewell, Eparable; double omega = sqrt(k/m); double a = Xmax - Xmin;

FILE *out; out = fopen ("E2.dat", "w"); if (out == NULL) { perror ("E2.dat"); exit (1); } for (i = 0; i < N; i++) { Ewell = Hbar*Hbar * M_PI*M_PI * i*i / (2*m*a*a); Eparable = Hbar*omega*(i+.5); fprintf (out, "%d %g %g\n", i, Ewell, Eparable); } fclose (out);}

void hamiltonian (int N, double *H, double *V, double dx){ int i, j; for (j = 0; j < N; j++) for (i = 0; i < N; i++) H[i + N*j] = 0;

//first row H[0 + N*0] = V[0] + 2 * Hbar * Hbar / (2 * m * dx * dx); H[N*(0+1) + 0] = - Hbar * Hbar / (2 * m * dx * dx);

//last row int n = N-1;

H[n + N*n] = V[n] + 2 * Hbar * Hbar / (2 * m * dx * dx); H[N*(n-1) + n] = - Hbar * Hbar / (2 * m * dx * dx); for (i = 1; i < N-1; i++) { H[i + N*i] = V[i] + 2 * Hbar * Hbar / (2 * m * dx * dx); H[N*(i-1) + i] =

H[N*(i+1) + i] = - Hbar * Hbar / (2 * m * dx * dx); }}

//jämförelsefunktion för qsortint comp (const void *p1, const void *p2){ int *a, *b; a = (int*) p1; b = (int*) p2; if (energies[*a] > energies[*b]) return 1; return -1;}

//Find eigenvalues and eigenvectors for the matrix M.//The eigenvectors are real. The eigenvalues might be complex//but ONLY THE REAL PART IS RETURNED//Sort the eigenvalues in increasing order,//and order the vectors accordingly.void solve (double *M, int N, double *E, double *V){ char jobvl, jobvr; //left,right eigenvectors? //'N'=no, 'V'=yes int lda, ldvl, ldvr, lwork, info;

double *vl; //left eigenvectors, not used double *vr; //right eigenvectors double wr[N], wi[N]; //real and imaginary part of eigenvalues. lwork = 100000+4*N; double work[lwork]; //Temporary workspace, 4*N is the minimum

jobvl ='N'; //No left eigenvectors jobvr ='V'; //Calculate right eigenvectors lda = N; ldvr = ldvl = N;

vl = NULL; vr = malloc (N*N*sizeof(double)); if (vr == NULL) { perror ("Cannot allocate memory"); exit (1); }

dgeev_ (&jobvl, &jobvr, &N, M, &lda, wr, wi, vl, &ldvl, vr, &ldvr,work, &lwork, &info);

//The matrix is overwritten!

if (info == 0) printf ("Success!\n"); else { printf ("ERROR: info =%d\n", info); exit (2); } printf ("Optimal lwork: %5.0f, used lwork: %d\n\n", work[0], lwork);

//Sort the eigenvalues in increasing order. //Permute the eigenvectors accordingly. int index [N]; energies = wr; //till jämförelsefunktionen int i, j;

for (i = 0; i < N; i++) index[i] = i; qsort (index, N, sizeof(index[0]), comp); for (i = 0; i < N; i++) { E[i] = wr[index[i]]; for (j = 0; j < N; j++)

V[j+N*i] = vr[j+N*index[i]]; }

free (vr);}

21

Page 22: Programmering för fysiker - Åbo Akademi

Programmering för fysiker. Lektion 8.

void discretize (int N, double *x){ int i; double dx = (Xmax - Xmin) / (N-1);

for (i = 0; i < N; i++) x[i] = Xmin + i * dx;}

void potential (int N, double *x, double *V){ int i; for (i = 0; i < N; i++) { //V[i] = 0; //Potentialbrunn

//Parabolisk potential V[i] = .5 * k * (x[i] * x[i]); }}

Referenser

LAPACK:

http://www.netlib.org/lapack/

Funktionen dgeev

http://www.netlib.org/lapack/double/dgeev.f

Om att anropa LAPACK från C

http://www.physics.orst.edu/~rubin/nacphy/lapack/cprogp.html

22