Springer-Lehrbuch

Werbung
Springer-Lehrbuch
3
Berlin
Heidelberg
New York
Hongkong
London
Mailand
Paris
Tokio
Harald Wiedemann
Numerische
Physik
Mit 72 Abbildungen, zahlreichen Übungen
und einer CD-ROM
mit Beispielprogrammen und Programmpaketen
13
Dr. Harald Wiedemann
Hattsteinstr. 6
79423 Heitersheim, Deutschland
e-mail: [email protected]
ISBN 3-540-40774-x Springer-Verlag Berlin Heidelberg New York
Bibliografische Information Der Deutschen Bibliothek.
Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über <http://dnb.ddb.de> abrufbar.
Dieses Werk ist urheberrechtlich geschützt. Die dadurch begründeten Rechte, insbesondere die der Übersetzung, des
Nachdrucks, des Vortrags, der Entnahme von Abbildungen und Tabellen, der Funksendung, der Mikroverfilmung oder
der Vervielfältigung auf anderen Wegen und der Speicherung in Datenverarbeitungsanlagen, bleiben, auch bei nur
auszugsweiser Verwertung, vorbehalten. Eine Vervielfältigung dieses Werkes oder von Teilen dieses Werkes ist auch
im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtsgesetzes der Bundesrepublik
Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zulässig. Sie ist grundsätzlich vergütungspflichtig. Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes.
Springer-Verlag ist ein Unternehmen von Springer Science+Business Media
springer.de
© Springer-Verlag Berlin Heidelberg 2004
Printed in Germany
Die dem Buch beigelegten Programme des Autors unterliegen der GPL (Gnu General Public License).
r
r
r
Die nachfolgenden Namen sind eingetragene Warenzeichen: Linux
by Linus Torvalds; Intel
und Pentium
by
r
r
r
by Advanced Micro Devices, Inc.; Windows
by Microsoft Corp.; Visual Numerics
und IMSL
Intel Corp.; AMD
r
r
r
r
by Visual Numerics; NAG by The Numerical Algorithms Group Ltd.; Adobe , Acrobat Reader , Postscript by
r
r
Adobe Systems Inc.; Red Hat
by Red Hat Software Inc.; SUSE
by SuSE Linux AG
Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch
ohne besondere Kennzeichnung nicht zu der Annahme, daß solche Namen im Sinne der Warenzeichen- und
Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.
Satz: Druckfertige Daten vom Autor erstellt unter Verwendung eines Springer LATEX2e Makropakets
Einbandgestaltung: design & production GmbH, Heidelberg
Gedruckt auf säurefreiem Papier
SPIN: 10934104
56/3141/jl - 5 4 3 2 1 0
Meinen Eltern
Vorwort
Die letzten Jahre waren vom ungeheuren Siegeszug des Computers geprägt:
Er hielt zunächst Einzug in große Computersäle der Konzerne, dann drang
er auch in kleinere Firmen vor, eroberte unsere Arbeitsplätze und findet sich
mittlerweile in fast jedem Haushalt. Er übernimmt verschiedenste Aufgaben,
wie die Verwaltung von Lagerbeständen oder von Konten, die Berechnung
der Sicherheit unserer Autos, dient als Schreibmaschine ebenso wie als Stereoanlage oder Fernseher – die Liste ließe sich fast beliebig fortsetzen. Schon
als Computer noch die Ausmaße von Schränken hatten, wurden Sie intensiv
in der Forschung eingesetzt, und das nicht nur als der schon oben erwähnte
Schreibmaschinenersatz, um Diplom- oder Doktorarbeiten sowie Veröffentlichungen niederzuschreiben. Auch bei der eigentlichen Forschung, also der
Gewinnung neuen Wissens, ist der Computer zu einem unentbehrlichen Hilfsmittel geworden.
In der Physik wird der Computer im Wesentlichen zu drei unterschiedlichen Aufgabengebieten herangezogen: zum einen für die Erstellung von Grafiken und Texten, zum zweiten zur Steuerung und Auswertung von Messabläufen in der Experimentalphysik und zum dritten zur numerischen Auswertung von Gleichungen in der Theoretischen Physik. In jüngster Zeit gewinnt darüber hinaus der Computer eine Bedeutung als Hilfsmittel bei der algebraischen Auswertung komplizierter Ausdrücke. In diesem Buch werden wir
uns lediglich mit dem dritten Aspekt, also der numerischen Physik, beschäftigen, wobei wir jedoch nicht umhin kommen, die so gewonnenen Ergebnisse
auch grafisch darzustellen, so dass der an erster Stelle genannte Gesichtspunkt
– wenn auch nur am Rande – ins Spiel kommt.
Wenn man ein Buch über numerische Physik schreiben will, sollte man
sich zunächst fragen, welche Hilfsmittel ein theoretischer Physiker in diesem
Aufgabengebiet einsetzt und welche Kenntnisse er für seine Arbeit benötigt.
Bei den Arbeitsmitteln ist in erster Linie neben einem möglichst leistungsfähigen Computer ein Compiler einer höheren Programmiersprache (z.B. C/C++
oder FORTRAN) zu nennen. Da der Physiker allerdings möglichst wenig seiner Zeit opfern möchte, um numerische Standardaufgaben zu programmieren,
benötigt er noch eine Numerik-Bibliothek, die ihm diese Aufgabe abnimmt.
Und schließlich benötigt er noch ein Grafikprogramm, mit dessen Hilfe er die
gewonnenen Ergebnisse visualisieren kann.
VIII
Vorwort
Die Frage nach speziellen Kenntnissen, die der numerische Physiker
benötigt, ist weit schwieriger zu beantworten. Auf den ersten Blick scheinen
Programmierkenntnisse und ein Handbuch der verwendeten Numerikbibliothek sowie des eingesetzten Grafikprogramms auszureichen. Die Realität aber
zeigt, dass diese beiden Dinge zwar einen guten Programmierer, aber keinen
guten Wissenschaftler ausmachen – für letzteren sind die Kenntnisse seines
Fachgebietes weitaus wichtiger, auch wenn er den größeren Teil seiner Zeit
mit dem Suchen von Programmierfehlern zubringt. Nun könnte man meinen,
dass diese Kenntnisse doch hinreichend in den Vorlesungen zur Experimentalphysik und Theoretischen Physik vermittelt würden, aber die Erfahrung
zeigt, dass die Sichtweise physikalischer Sachverhalte in der numerischen Physik oft eine andere ist: Im konventionellen Lehrplan zentrale Dinge verlieren
an Bedeutung, während andere Gesichtspunkte ins Blickfeld geraten. Probleme, die vorher zu schwierig waren, sind nun verhältnismäßig einfach, während
es sich bei anderen genau umgekehrt verhält. Dies ist die Lücke, die dieses
Buch schließen will. Und dieses Buch möchte den Studenten einladen, die
aufregende Welt der numerischen Physik selbst zu entdecken.
Um dieses zu ermöglichen, müssen wir noch einmal auf die materiellen
Voraussetzungen zurückkommen, die für numerische Physik benötigt werden.
Computer sind seit ihrer Erfindung kontinuierlich billiger und leistungsfähiger
geworden, so dass mittlerweile fast jeder Student einen besitzt oder wenigstens Zugang zu einem hat. Compiler sind zwar auch nicht unerschwinglich,
aber bei der Numerik-Bibliothek erreichen wir im Allgemeinen ein Preisniveau, das für Studenten nicht mehr akzeptabel ist. Deshalb kommen bislang
viele Studenten erst zum Beginn der Diplomarbeit mit einem umfangreicheren Einsatz numerischer Methoden in Berührung, oder die Erfahrungen
vorher beschränken sich auf eine Vorlesung mit eventuellem Praktikum. Angesichts der zunehmenden Bedeutung der numerischen Physik ist diese Situation unbefriedigend, und eine Aufgabe dieses Buches ist, dies zu ändern.
Einen Ausweg aus dieser Situation bietet mittlerweile sogenannte freie
Software, also Software die umsonst ist. Neben dem weithin bekannten freien
Unix-Betriebssystem Linux gibt es nämlich inzwischen für fast jede SoftwareSparte auch freie Programme, die den Vergleich mit ihren kommerziellen
Konkurrenten nicht zu scheuen brauchen – von der Textverarbeitung bis hin
zum von uns benötigten Compiler. Und wer ein wenig sucht, findet sogar frei
verfügbare Numerikbibliotheken. Da auch für unsere Zwecke taugliche Visualisierungsprogramme frei erhältlich sind, können alle Voraussetzungen für
numerische Physik auch zum Nulltarif erfüllt werden, und man hat bei allen
drei Software-Paketen (Compiler, Numerikbibliothek und Grafikprogramm)
sogar noch verschiedene Programme zur Auswahl – eine Auswahl, die zumindest zum Teil von mir eingeschränkt werden musste, da dieses Buch nicht
alle Möglichkeiten abdecken kann.
Beim Compiler haben Sie noch weitgehend freie Wahl, sofern Sie sich
auf die Programmiersprachen C++ oder FORTRAN beschränken – andere
Vorwort
IX
Programmiersprachen finden in der Numerik auch praktisch keine Anwendung. An freien Compilern stehen hier z.B. die Compiler der GNU-Familie
gcc/g++ bzw. g77 zur Verfügung. Diese Compiler gibt es in Varianten für
Windows und für Linux sowie andere Betriebssysteme. Wenn Sie als Betriebssystem Linux installiert haben, stehen Ihnen darüber hinaus noch z.B.
die Intel-Compiler zur Verfügung (die auch auf AMD-Systemen hervorragend
laufen) – aus Lizenzgründen sind diese Compiler aber nicht auf der beiliegenden CD-ROM vorhanden, sondern müssen nach einer Registrierung direkt
bei Intel von deren Webseiten geholt werden. Neben diesen frei verfügbaren
Compilern ist aber selbstverständlich auch jeder kommerzielle C++- oder
Fortran-Compiler für unsere Zwecke geeignet.
Eine Auswahl von Numerik-Bibliotheken finden Sie in Anhang C. Deren
Routinen werden von unseren C++- bzw. Fortran-Programmen aufgerufen,
so dass ein Programm immer nur mit einer bestimmten Bibliothek übersetzt
werden kann. Hier muss also eine Festlegung erfolgen und wenn Sie eine andere Numerik-Bibliothek verwenden wollen, müssen Sie die Programme auf
der beigelegten CD-ROM entsprechend anpassen. Alle Programme werden
im Buch als C++-Quellcode in ihrer für die GNU Scientific Library [1], kurz
GSL, ausgelegten Variante ausführlich vorgestellt und Sie finden diese Programme auf der CD-ROM im Verzeichnis programme\gsl. Darüber hinaus
finden Sie im Verzeichnis programme\slatec die meisten dieser Programme
als FORTRAN-Quellcode für SLATEC. Alle diese Programme wurden wie
die zugehörigen Bibliotheken, unter die Gnu General Public License, kurz
GPL, gestellt. Das bedeutet, dass Sie diese Programme verwenden, verändern
und an Dritte weitergeben dürfen. Ein entsprechender Hinweis findet sich im
Quelltext der Programme am Dateiende – diese Zeilen geben wir aus Platzgründen bei der Besprechung der Programme im Buch nicht wieder. Für
die Weiterverteilung von GPL-lizensierten Programmen gibt es einige Auflagen, die Sie bitte Anhang G entnehmen. Der dort wiedergegebene Text
der GPL ist Englisch, da nur dieser rechtliche Relevanz besitzt – Sie finden im Internet unter http://www.gnu.org/licenses/translations.html
aber auch Übersetzungen in andere Sprachen, die allerdings keinen offiziellen
Charakter haben.
Als letzten Punkt müssen wir auf die Visualisierung unserer Ergebnisse
eingehen. Die von uns geschriebenen Programme erzeugen zunächst einmal
nur eine Menge Zahlen, die grafisch dargestellt werden müssen, damit man
mit diesen etwas anfangen kann. Auch hier stehen wieder eine ganze Reihe
von Programmen zur Auswahl (gnuplot, GLE, kplot), und es ist Ihnen überlassen, welches Sie benutzen wollen. Die Programme dieses Buches sind zwar
insofern auf GLE oder gnuplot ausgelegt, dass Kommentarzeilen mit Parametern mit einem # beginnen – diese Zeilen lassen sich jedoch leicht entfernen
oder an ein anderes Grafikprogramm anpassen.
Die Gliederung des Buches entspricht dem Kursus Theoretischer Physik,
wie er an Universitäten gelehrt wird: Mechanik, Elektrodynamik, Optik, Sta-
X
Vorwort
tistische Physik sowie Quantenmechanik. Jedes Kapitel beginnt mit einem
Abriss der Grundlagen – dieser soll dem Leser, der bereits über die entsprechenden Kenntnisse verfügt, die wesentlichen Punkte noch einmal in Erinnerung rufen. Sollte der Leser dabei feststellen, dass er keine ausreichenden
Grundlagenkenntnisse hat, müssen wir ihn an ein konventionelles Lehrbuch
verweisen, da das vorliegende Buch ein solches weder ersetzen kann noch
will. Empfehlenswerte Werke, die alle wesentlichen Teilgebiete der Physik
abdecken, sind [2–4]. Darüber hinaus kann der Leser auf [5] für Elektrodynamik, [6] für Optik und [7] für Quantenmechanik zurückgreifen. Durch die
Fülle an detailliert besprochenen Aufgaben und Problemen zeichnet sich [8]
aus – diese Aufgaben bieten sich auch für eine numerische Lösung an, wobei die analytische Lösung zur Kontrolle herangezogen werden kann. Als
ergänzende bzw. weiterführende Literatur in Richtung Numerische Physik
empfehle ich [9–13].
Überhaupt nicht angesprochen wird in diesem Buch der Themenbereich
Dynamik von Flüssigkeiten, da dies den Rahmen einer Einführung in die
Numerische Physik sprengen würde. Für diese Disziplin kann der Leser auf
eine umfangreiche Spezialliteratur zurückgreifen [14–17].
Wie in einem konventionellen Lehrbuch in Theoretischer Physik werden zu
jedem Kapitel eine Reihe von Problemstellungen vorgestellt und ausführlich
besprochen. Jedes dieser Probleme wird zunächst in einem Grundlagenabschnitt diskutiert, in dem wir dieses soweit wie möglich analytisch lösen und
damit den Grundstein für die numerische Lösung legen, die im Anschluss
daran besprochen wird. Mir liegt daran, dass Sie die Numerik nicht als Alternative zur analytischen Lösung begreifen, sondern eher als Ergänzung, und
Sie werden feststellen, wie diese beiden an sich grundverschiedenen Zugänge
ineinander greifen. So eingesetzt, erweist sich Numerik als ein äußerst leistungsfähiges Werkzeug in der Hand des Theoretischen Physikers.
Zu jedem Kapitel dieses Buches gibt es Übungsaufgaben, bei denen Sie
die Programme, die im Text besprochen werden und die Sie auf der CDROM finden, an neue Aufgabenstellungen anpassen müssen. Fassen Sie diese
Aufgaben als Anregungen auf – sein Ziel hat das Buch erreicht, wenn Sie
über diese Aufgaben hinausgehen und Probleme numerisch angehen, die Sie
interessieren, die Sie aber nicht analytisch lösen können.
Zu guter Letzt möchte ich mich bei Gert-Ludwig Ingold und Michael
Weingärtner für das unermüdliche Korrekturlesen und zahlreiche Vorschläge,
die diesem Buch zu Gute kamen, bedanken. Darüber hinaus bin ich Fritz
Haake, Holger Schanz und den Mitgliedern der GSL-Newsgroup zu Dank
verpflichtet.
Heitersheim,
Januar 2004
Harald Wiedemann
Inhaltsverzeichnis
Mechanik der Massenpunkte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1 Die Newtonschen Gesetze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Das Fadenpendel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.1 Verifizierung des Programms . . . . . . . . . . . . . . . . . . . . . . .
1.2.2 Graphische Darstellung und Interpretation
der Ergebnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.3 Verbesserung des Algorithmus . . . . . . . . . . . . . . . . . . . . .
1.3 Das Doppelpendel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4 Integrable und nicht integrable Dynamiken . . . . . . . . . . . . . . . .
1.5 Reguläre und chaotische Dynamiken . . . . . . . . . . . . . . . . . . . . . .
1.6 Das Teilchen in der Schachtel . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7 Hamilton-Formalismus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8 Attraktoren in dissipativen Systemen . . . . . . . . . . . . . . . . . . . . .
1.9 Das periodisch angetriebene Pendel . . . . . . . . . . . . . . . . . . . . . . .
1.10 Der schiefe Wurf mit Luftwiderstand . . . . . . . . . . . . . . . . . . . . . .
Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12
14
26
32
40
45
52
52
55
61
66
2
Elektrodynamik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1 Die Maxwellschen Gleichungen . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Felder von Punktladungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 Multipole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4 Berechnung von Feldlinien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5 Magnetfelder stationärer Ströme . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6 Hysterese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
71
72
73
76
80
92
96
3
Optik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1 Historischer Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Grundbegriffe der Strahlenoptik . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Brechung und Reflektion von Licht . . . . . . . . . . . . . . . . . . . . . . .
3.4 Brechung an einer Linsenfläche . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5 Bild durch eine Linse – Linsenfehler . . . . . . . . . . . . . . . . . . . . . .
3.6 Entstehung eines Regenbogens . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.6.1 Qualitative Erklärung des Regenbogens . . . . . . . . . . . . .
3.6.2 Quantititave Vorüberlegungen . . . . . . . . . . . . . . . . . . . . .
99
99
100
101
105
111
116
117
118
1
1
1
3
11
XII
Inhaltsverzeichnis
3.6.3 Programm zur Berechnung eines Regenbogens . . . . . . .
3.6.4 Der Regenbogen bei ellipsoidförmigen Regentropfen . . .
3.7 Grundlagen der Wellenoptik . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.8 Ebene Wellen und Kugelwellen . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.9 Interferenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.10 Das Huygenssche Prinzip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.11 Berechnung von Beugungsmustern . . . . . . . . . . . . . . . . . . . . . . . .
3.12 Kohärenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.13 Beugung bei endlicher Kohärenzlänge . . . . . . . . . . . . . . . . . . . . .
Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
120
124
125
126
127
128
129
134
137
142
4
Statistische Physik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.1 Grundbegriffe der Statistik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Bestimmung von Wahrscheinlichkeiten . . . . . . . . . . . . . . . . . . . .
4.3 Mittelwerte und Momente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4 Bedingte Wahrscheinlichkeiten und Korrelationen . . . . . . . . . . .
4.5 Dynamik bei statistischen Problemen . . . . . . . . . . . . . . . . . . . . .
4.6 Der Random-Walk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.6.1 Stochastische Differentialgleichung des Random-Walk .
4.6.2 Mastergleichung des Random-Walks . . . . . . . . . . . . . . . .
4.6.3 Verbesserung des Random-Walk-Modells . . . . . . . . . . . .
4.7 Thermisches Hüpfen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.8 Thermalisierung in Gasen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.8.1 Energieerhaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
147
147
148
150
150
152
153
154
158
160
170
176
177
189
5
Quantenmechanik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1 Die mathematische Struktur der Quantenmechanik . . . . . . . . .
5.2 Operationen im Hilbertraum . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Eigenzustände und ihre Verwendung als Koordinatensysteme .
5.4 Orts- und Impulsdarstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.5 Die Kopenhagener Interpretation der Quantenmechanik . . . . .
5.6 Schrödingergleichung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.7 Bestimmung des Hamilton-Operators . . . . . . . . . . . . . . . . . . . . .
5.8 Das freie Teilchen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.9 Eigenzustände des Hamiltonoperators . . . . . . . . . . . . . . . . . . . . .
5.10 Variationsmethoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.11 Quantentunneln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.12 Einführung in die Quantenstatistik . . . . . . . . . . . . . . . . . . . . . . .
5.13 Ein Zwei-Niveau-System mit äußerer Anregung . . . . . . . . . . . . .
5.14 Messprozess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.15 Der Zeno-Effekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.16 Ein Ein-Atom-Laser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
193
193
193
195
197
199
199
200
200
213
219
226
233
235
241
242
246
254
Inhaltsverzeichnis
XIII
Anhang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
A
Installation der Pakete unter Linux . . . . . . . . . . . . . . . . . . . . . . . 257
B
Installation der Pakete unter Windows . . . . . . . . . . . . . . . . . . . 261
C
Mathematische Bibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
D
Fortran und C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
E
Filterprogramme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
F
Fouriertransformation und FFT-Routinen . . . . . . . . . . . . . . . . 285
G
Die GPL-Lizenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
1 Mechanik der Massenpunkte
Der Siegeszug der mathematischen Beschreibung physikalischer Gesetzmäßigkeiten begann vor rund 330 Jahren mit der Formulierung der Newtonschen
Gesetze [18], die die Bewegung von Massenpunkten unter dem Einfluss von
Kräften beschreiben. Dieser Schritt war gleichermaßen revolutionär wie ungeheuer erfolgreich. So erfolgreich, dass in der Folgezeit versucht wurde, die
gesamte Physik mechanisch zu interpretieren und zu beschreiben. Auch wenn
wir heute wissen, dass diese Sichtweise falsch ist und die Mechanik nur ein
Teilgebiet der Physik darstellt, folgt der Unterricht in Theoretischer Physik
weitgehend der geschichtlichen Chronologie und beginnt mit der Mechanik –
schon wegen der Anschaulichkeit der dabei besprochenen Problemstellungen.
1.1 Die Newtonschen Gesetze
Die Aufstellung der Newtonschen Gesetze bedeutete, wie eben schon ausgeführt, in der Geschichte der Physik einen entscheidenden Wendepunkt:
erstmals wurden Gesetzmäßigkeiten so formuliert, dass man mit den Methoden der Mathematik daraus andere Gesetzmäßigkeiten herleiten konnte.
Und auch für uns sollen die Newtonschen Gesetze am Anfang unseres Weges
durch die klassische Mechanik stehen:
In Ineratialsystemen gilt:
F = ma .
(1.1)
In Worten ausgedrückt besagt dieses Gesetz, dass man, um einem Körper der
Masse m eine Beschleunigung a zu geben, eine Kraft F benötigt, die gerade
gleich dem Produkt aus der Masse und der Beschleunigung ist. Insbesondere
ist die Beschleunigung null, wenn keine Kraft wirkt – ein Spezialfall, den
Newton aufgrund seiner Bedeutung in einem eigenen Gesetz formulierte:
Ein Massenpunkt, auf den keine Kraft wirkt, bewegt sich gleichförmig.
Noch bevor wir uns der Frage widmen, woher Kräfte zwischen Körpern
kommen, wollen wir ein weiteres – und letztes – Newtonsches Gesetz postulieren:
Übt ein Körper 1 auf einen anderen Körper 2 eine Kraft F aus, so übt
umgekehrt letzterer (2) auf ersteren (1) die Kraft −F aus.
Dieses Gesetz fasst man häufig kurz zusammen durch: Kraft gleich Gegenkraft, oder mit lateinischen Ausdrücken: actio gleich reactio.
2
1 Mechanik der Massenpunkte
Kommen wir jedoch zur Gleichung (1.1) zurück. Als erstes müssen wir
uns fragen, wann dieses Gesetz gilt – und diese Frage bringt uns schon in
Probleme. Die Antwort, die Sie in den Lehrbüchern finden, nämlich dass das
Newtonsche Grundgesetz in Inertialsystemen gilt, führt uns zu der Frage, was
ein Inertialsystem ist. Und die Antwort darauf ist, dass ein Inertialsystem
dadurch definiert ist, dass in ihm das Newtonsche Grundgesetz gilt. Wir sind
also in einen Zirkelschluss geraten. Wir müssen uns an dieser Stelle mit der
Annahme begnügen, dass es Inertialsysteme zumindestens gibt und dass wir
hinreichend gut in einem Inertialsystem sind, wenn wir (salopp gesagt) mit
beiden Füßen auf der Erde stehen.
Ein weiterer Punkt, den wir zu Gleichung (1.1) anmerken wollen, ist die
Tatsache, dass die Beschleunigung ein Vektor ist, und deshalb auch die Kraft
vektoriell sein muss.
Newton macht keine Aussage darüber, woher die Kraft F kommt und
wie man sie berechnet. Er impliziert aber, dass jede Kraft eine irgendwie
geartete Ursache hat und für den Fall der Gravitation konnte Newton auch
den mathematischen Ausdruck finden – eben das Gravitationsgesetz:
m 1 m2
F = −G 3 r .
(1.2)
r
Die Gravitationskraft zwischen zwei Körpern ist nach diesem Gesetz proportional zum Produkt der zwei beteiligten Massen m1 und m2 und umgekehrt
proportional zum Quadrat des Abstandes r zwischen den Körpern. Außerdem zeigt die Kraft immer in Richtung der Verbindungslinie zwischen den
Körpern und ist immer anziehend. Die Gravitionskonstante G schließlich ist
eine Naturkonstante und daher für alle Körper die selbe.
Mit dem Gravitationsgesetz lässt sich schon eine ganze Menge machen:
Planetenbewegungen fallen ebenso unter dessen Zuständigkeit wie Pendel
oder der schiefe Wurf. Um z.B. Federn in die Betrachtungen einzubeziehen,
müssen wir jedoch auch empirische Gesetze zulassen, denen zufolge wir wissen, dass die Federkraft proportional zur Auslenkung s der Feder ist:
F = −Ds .
(1.3)
Dieses Gesetz (das Hookesche Gesetz) ist empirisch und bei einer anderen Feder kann es sein, dass nicht nur die Proportionalitätskonstante D eine andere
ist, sondern der Zusammenhang zwischen der Kraft F und der Auslenkung
s grundsätzlich ein anderer ist (im Prinzip ist es sogar so, dass keine Feder
das Gesetz (1.3) exakt befolgt). In diesem Sinne handelt es sich also bei (1.3)
nicht um ein Gesetz, sondern eher um eine Regel.
Zusammenfassend haben wir bis jetzt das Newtonsche Grundgesetz (1.1),
das durch andere physikalische Gesetze sowie durch empirische Regeln mit
Leben gefüllt wird.
Zum Schluss dieses Abschnitts wollen wir uns noch überlegen, was passiert, wenn wir nicht einen Massenpunkt haben, sondern mehrere. Der Einfachheit halber nummerieren wir die Massenpunkte mit 1, 2, 3 u.s.w. durch
1.2 Das Fadenpendel
3
und bezeichnen die zugehörigen Massen mit m1 , m2 , m3 und so weiter. In
dieser Situation wirkt auf jeden Massenpunkt auch eine eigene Kraft, die wir
konsequenterweise F1 , F2 , . . . nennen. Und Newtons Gesetz (1.1) gilt nun für
jeden einzelnen Massenpunkt, also
F i = mi ai ,
(1.4)
wobei ai die Beschleunigung des Massenpunkts mit der Nummer i ist.
1.2 Das Fadenpendel
In diesem ersten Beispiel untersuchen wir einen Massenpunkt, der mittels
einer Stange an einem Punkt befestigt ist (siehe Abb. 1.1).
Abb. 1.1. Fadenpendel
Wir setzen voraus, dass die Bewegung ausschließlich in der Zeichenebene
erfolgen kann und da durch die Stange außerdem der Abstand zum Aufhängepunkt fixiert ist, ist die Position des Massenpunktes eindeutig durch den Winkel φ festgelegt. Aufgrund dieses eindimensionalen Charakters der Bewegung
können wir den Vektorcharakter der Beschleunigung a und der Kraft F außer
Acht lassen und stattdessen mit skalaren Größen rechnen.
Unabhängig von der Position wirkt auf den Massenpunkt die Schwerkraft −mg, wobei allerdings nur die Komponente senkrecht zur Stange den
Massenpunkt beschleunigen bzw. verzögern kann, der Rest wird von einer
entsprechenden Gegenkraft in Richtung der Stange kompensiert: In unserem
Fall ist die wirksame Komponente −mg sin φ. Nun benötigen wir noch die
Beschleunigung, ausgedrückt durch die dynamische Variable φ sowie die fixe
Länge l der Stange:
d2
(1.5)
a = l 2φ .
dt
Damit haben wir auch schon unsere Bewegungsgleichung
ml
d2
φ = −mg sin φ .
dt2
(1.6)
4
1 Mechanik der Massenpunkte
Nach dem Kürzen der Masse m erhalten wir eine Differentialgleichung, in die
noch die Größe l/g eingeht, die, wie wir leicht feststellen, die DimensionZeit
zum Quadrat hat. Diese charakteristische Zeit definieren wir als τ = l/g
und skalieren die Zeit t gemäß
t =
t
.
τ
(1.7)
Dadurch erhalten wir die skalierte Differentialgleichung
d2
φ = − sin φ .
dt2
(1.8)
Diese Vorgehensweise hat gleich mehrere Vorteile:
–
Wir haben die Zeitskala, auf der die Bewegung des Pendels stattfindet,
bereits identifiziert, obwohl wir die Bewegungsgleichung noch nicht gelöst
haben.
– Wir haben die Parameter l und g eliminiert, so dass wir im Falle einer numerischen Lösung die Differentialgleichung nicht für mehrere Werte dieser
Parameter lösen müssen.
Diese Differentialgleichung sieht ziemlich einfach aus, wir werden jedoch
sehen, dass sie bereits zu schwierig ist, um mit Papier und Bleistift geschlossen gelöst zu werden. Ohne Computer müssten wir uns deswegen mit mehr
oder weniger guten Näherungslösungen zufrieden geben – eine Situation, die
leider bei sehr vielen Problemen auftritt. Die Entwicklung leistungsfähiger
Computer hat uns zu dieser klassischen Vorgehensweise noch einen zweiten
Zugang zur Verfügung gestellt, um den es in diesem Buch geht: die numerische Behandlung des Problems. Vorher jedoch wollen wir uns trotzdem mit
einer Näherungslösung beschäftigen, dem sogenannten mathematischen Pendel, das Sie bereits aus der Schule kennen.
Beim mathematischen Pendel geht man davon aus, dass die Auslenkungen
φ aus der Ruhelage sehr klein sind (das Pendel also weit davon entfernt ist,
überzuschlagen). In diesem Fall können wir den Sinus auf der rechten Seite
von (1.8) in guter Näherung durch dessen Argument ersetzen, wodurch sich
unsere Differentialgleichung vereinfacht zu:
d2
φ = −φ .
dt2
(1.9)
Diese Gleichung nun können wir direkt lösen, ihre Lösung lautet
φ = A sin t + B cos t ,
(1.10)
wobei A und B beliebige Werte annehmen dürfen, die wir zum Beispiel durch
die Anfangsbedingungen bestimmen können. Wenn wir nun wieder zu der
unskalierten Zeit t übergehen, erhalten wir
1.2 Das Fadenpendel
φ = A sin ωt + B cos ωt ,
5
(1.11)
mit ω = g/l. Aus dieser Kreisfrequenz ω bekommen wir die Frequenz f ,
indem wir ω durch 2π dividieren. Die Periodendauer schließlich ist das Inverse
der Frequenz f , also 2π/ω.
Nach diesem kleinen Ausflug kehren wir jedoch zu unserem ursprünglichen Problem (1.8) zurück. Nachdem wir uns vielleicht eine Weile erfolglos
daran versucht haben, eine Funktion zu finden, die diese Bewegungsgleichung
erfüllt, geben wir diese Bemühungen auf und wenden uns einer numerischen
Lösung zu.
Im ersten Schritt machen wir uns klar, dass diese Differentialgleichung
zweiter Ordnung (die gesuchte Größe φ tritt maximal in zweiter Ableitung
nach τ auf) auch als zwei gekoppelte Differentialgleichungen erster Ordnung
aufgefasst werden kann:
d
φ = vφ
dτ
d
vφ = − sin φ .
dτ
(1.12)
(1.13)
In der ersten der beiden Gleichungen steht, dass die zeitliche Änderung von φ
gleich einer Winkelgeschwindigkeit vφ ist; in der zweiten Gleichung wird die
Änderung dieser Größe gleich der Beschleunigung − sin φ gesetzt. Wenn Sie
die erste Gleichung nach τ ableiten und darin die zweite Gleichung einsetzen,
erhalten Sie wieder die ursprüngliche Gleichung (1.8).
Nehmen wir nun an, dass wir die Werte von φ und vφ zu einem Zeitpunkt
τ0 kennen. Entwickeln wir zunächst φ(τ ) und vφ (τ ) in Potenzreihen um τ0 :
φ(τ ) = φ(τ0 + ∆τ ) = φ(τ0 ) +
∞
1 (n)
φ (∆τ )n
n!
n=1
vφ (τ ) = vφ (τ0 + ∆τ ) = vφ (τ0 ) +
(n)
∞
1 (n)
vφ (∆τ )n .
n!
n=1
(1.14)
(1.15)
Wenn wir alle Koeffizienten φ(n) und vφ kennen würden, hätten wir immerhin eine Potenzreihendarstellung der gesuchten Lösung unseres Problems
gefunden. Aber was ist, wenn ∆τ sehr klein ist, sagen wir 0.001. Dann sind
die weitaus größten Terme auf der rechten Seite von (1.14) und (1.15) φ(τ0 )
und vφ (τ0 ). Der nächstfolgende Term ist bereits etwa tausendmal kleiner, der
nächste noch einmal tausendmal kleiner und so weiter. Diese Beobachtung
ist keineswegs überraschend, besagt sie doch nur, dass der Zustand zum Zeitpunkt τ0 sich nur wenig unterscheidet von einem Zustand kurz vor und kurz
nach τ0 . Wenn wir uns also auf solche kleinen Zeitschritte ∆τ beschränken,
kommen wir mit sehr wenigen Termen aus den Potenzreihen (1.14) und (1.15)
aus! Der Trick besteht nun darin, eine längere Zeit in entsprechend kleine
Zeitschritte zu zerteilen und sich so langsam in der Zeit entlangzuhangeln.
6
1 Mechanik der Massenpunkte
Nur die beiden Anfangsterme φ(τ0 ) und vφ (τ0 ) zu berücksichten, macht
offensichtlich keinen Sinn, da sich dann überhaupt nichts ändern würde. Die
einfachst mögliche Vorgehensweise macht also die folgende Näherung:
φ(τ0 + ∆τ ) ≈ φ(τ0 ) + φ(1) ∆τ
vφ (τ0 + ∆τ ) ≈ vφ (τ0 ) +
(1)
vφ ∆τ
(1.16)
.
(1.17)
Wenn Sie nun in einem Mathematiklehrbuch den Abschnitt über Potenzreihen konsultieren, werden Sie feststellen, dass die beiden fehlenden Terme φ(1)
(1)
und vφ durch die Ableitungen der betreffenden Funktionen, also durch unsere Differentialgleichungen (1.12) und (1.13), gegeben sind. Auf diese Weise
erhalten wir also
φ(τ0 + ∆τ ) ≈ φ(τ0 ) + vφ (τ0 )δτ
vφ (τ0 + ∆τ ) ≈ vφ (τ0 ) − sin(φ(τ0 ))δτ .
(1.18)
(1.19)
Damit haben wir – endlich – zwei Gleichungen, bei denen alle Größen auf
der rechten Seite bekannt sind. Nun können wir daran gehen, das Ganze in
einem Computerprogramm zu implementieren.
Wie in der Einleitung bereits erläutert, werden wir die Programme jeweils
in C++ vorstellen und besprechen, einfach weil diese Sprache der derzeitige Standard ist und sich – nach langen Geburtswehen – auch in der Physik
durchsetzt. Alternativ stehen jedoch auf der beigefügten CD auch Quellprogramme für Fortran zur Verfügung.
Doch nun zu dem Programm:
1
2
3
4
5
6
/**************************************************************************
* Name:
faden1.cpp
*
* Zweck:
Simuliert die Dynamik eines Fadenpendels
*
* Gleichung: (dˆ2/d tˆ2) phi + sin(phi) = 0
*
* Methode:
1.Ordnung
*
**************************************************************************/
7
8
9
10
11
#include
#include
#include
#include
<iostream>
<fstream>
<stdio.h>
<string>
12
13
14
//-- Definition der globalen Variablen
const int n_start_max = 10;
15
16
main( int argc, char *argv[] )
17
18
19
20
21
22
23
24
{
//-- Definition der Variablen
int
n, m, n1, nmax, nout, n_start;
double
t, tend, dt, phi, phi_neu, v_phi, v_phi_neu, phi_0, v_phi_0;
char*
resfile;
ifstream in_stream;
ofstream out_stream;
25
26
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
1.2 Das Fadenpendel
27
28
29
30
31
if (argc<3)
{
cout << " Aufruf: faden1 infile outfile\n";
exit(1);
}
32
33
34
35
36
37
38
39
//-- Einlesen der Parameter -in_stream.open(argv[1]);
in_stream >> tend;
in_stream >> nmax;
in_stream >> nout;
in_stream >> n_start;
in_stream.close();
40
41
42
//-- Berechnung einiger benoetigter Parameter -dt = tend / nmax;
43
44
45
46
47
48
49
50
51
52
53
54
//-- Schleife ueber die Anfangsbedingungen
for (n1=0; n1<n_start; n1++)
{
sprintf(resfile,"%s.%i",argv[2],n1);
out_stream << "! Ergebnis-Datei generiert von faden1.cpp\n";
out_stream << "! tend = " << tend << "\n";
out_stream << "! nmax = " << nmax << "\n";
out_stream << "! nout = " << nout << "\n";
out_stream << "! n_start = " << n_start << "\n";
out_stream << "! Spalte 1: t
Spalte 2: phi(t)"
<< "
Spalte 3: v_phi(t)\n";
55
56
57
58
59
60
61
62
//-- Anfangsbedingungen
in_stream >> phi_0;
in_stream >> v_phi_0;
t
= 0;
phi
= phi_0;
v_phi = v_phi_0;
out_stream << t << " " << phi << " " << v_phi;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//-- Zeitschleife -for (n=1; n<=nout; n++)
{
for (m=1; m<=nmax/nout; m++)
{
//-----------t = t + dt;
phi_neu
= phi + v_phi * dt;
v_phi_neu = v_phi - sin(phi) * dt;
//-----------phi
= phi_neu;
v_phi = v_phi_neu;
//-----------}
out_stream << t << " " << phi << " " << v_phi;
}
out_stream.close();
}
82
83
}
7
8
1 Mechanik der Massenpunkte
Beginnen wir mit dem Programmkopf, der aus einigen Kommentarzeilen
besteht (alles hinter einem \\ wird vom Compiler als Kommentar interpretiert, desgleichen alles zwischen \* und *\). Aus diesen Zeilen geht hervor,
wie die Datei heißt, in welcher das Programm steht, und was es ganz grob
macht. Danach kommt der eigentliche Programmtext, zunächst einige Zeilen,
die verwendete Include-Dateien festlegen, danach die Definition einer globalen Konstante n start max, die festlegt, wie viele verschiedene Anfangsbedingungen in einem Programmlauf durchgerechnet werden können. Nach
der Deklaration der für das Programm benötigten Variablen (Zeile 19–24)
beginnt das eigentliche Programm.
In den Zeilen 26 bis 31 wird überprüft, ob das Programm korrekt aufgerufen wurde – wir werden bei diesem und allen folgenden Programmen die
Namen der Eingabe- und Ausgabedateien beim Programmaufruf als Argumente übergeben. Der Aufruf ist also sicher nicht korrekt, wenn nicht mindestens zwei Argumente übergeben wurden und da bei C/C++ immer auch der
Programmname selbst zu den Argumenten mitgerechnet wird, ergibt sich die
Bedingung argc<3 für einen Fehler. Nachdem nun die Eingabedatei bekannt
ist, werden in den folgenden Zeilen alle für das Programm nötigen Parameter eingelesen und gleichzeitig in der Ausgabedatei festgehalten. In unserem
Fall ist das die Zeit tend, bis zu der die Bewegungsgleichung gelöst werden
soll, die Zahl der Zeitschritte nmax, die Zahl der Zeiten, zu denen wir das
Ergebnis abspeichern wollen (nout). Schließlich kommt noch ein Parameter
n start, der angibt, mit wie vielen Anfangsbedingungen die Bewegungsgleichung gelöst werden soll:
1
2
3
4
5
6
7
10
1000000
100
3
0 1.
0 2.
0 2.1
Alle diese Parameter wollen wir in der bzw. in diesem Fall den Ausgabedateien festhalten, ansonsten ist später nicht mehr rekonstruierbar zu welchen
Parametern ein konkreter Datensatz gehört. Dabei müssen wir beachten, dass
das Grafikprogramm, mit dessen Hilfe später die Ergebnisse graphisch umgesetzt werden sollen, diese Zeilen ignoriert – im Falle von GLE oder von
gnuplot geschieht das wie hier gezeigt durch ein ! am Zeilenanfang. So erklären sich die Zeilen 48–53, in denen jeder Ausgabedatei ein Block der Form
!
!
!
!
!
!
Ergebnis-Datei generiert von faden1
tend = 10
nmax = 1000000
nout = 100
n_start = 3
Spalte 1: t
Spalte 2: phi(t)
Spalte 3: v_phi(t)
vorangestellt wird. Die Tatsache, dass wir für jede Anfangsbedingung eine eigene Ausgabedatei generieren, erfordert zwei Besonderheiten im Programm,
1.2 Das Fadenpendel
9
die Sie in den weiteren Programmen dieses Buches so nicht wiederfinden
werden (auf den normalen Aufbau gehen wir dann beim nächsten Beispielprogramm faden all.cpp ein):
– der Name der jeweiligen Ausgabedatei wird in der Zeile 47 zusammengesetzt,
– das Schreiben des oben beschriebenen Blocks geschieht nicht unmittelbar
nach dem Einlesen der Parameter, sondern erst innerhalb der Schleife
über n1.
Anschließend werden in Zeile 59–61 die Werte phi und v phi auf diese Anfangswerte und t auf null gesetzt.
Was jetzt noch fehlt, ist lediglich die Zeitschleife, die in zwei Schleifen
geteilt wurde. Der Sinn dieser Aufteilung ist, dass wir dadurch die Möglichkeit
haben, nur zu einem Bruchteil der Zeiten, die wir durchlaufen, die Werte von
phi und v phi abzuspeichern. Ansonsten würden wir nämlich sehr, sehr große
Ausgabedateien erzeugen, die wir gar nicht benötigen.
Bei diesem ersten Programm wollen wir – ausnahmsweise – auch den
FORTRAN-Code besprechen, um dem FORTRAN-Programmierer einen Einblick in den Programmierstil zu geben, der in den Beispielprogrammen
gewählt wurde:
1
2
3
4
5
6
***************************************************************************
* Name:
faden1.f
*
* Zweck:
Simuliert die Dynamik eines Fadenpendels
*
* Gleichung: (dˆ2/d tˆ2) phi + sin(phi) = 0
*
* Methode:
1.Ordnung
*
***************************************************************************
7
program faden1
8
9
10
*
11
-- Definition der Variablen
implicit none
12
integer
13
n_start_max
14
parameter (n_start_max=10)
15
16
real*8
t, tend, dt, phi, phi_neu, v_phi, v_phi_neu, phi_0, v_phi_0
character*20 resfile(n_start_max)
integer n, m, n1, nmax, nout, n_start
17
18
19
20
21
*
-- Einlesen der Parameter -open(1,file="faden1.par")
read(1,*) tend
read(1,*) nmax
read(1,*) nout
read(1,*) n_start
*
-- Namen fuer die Ergebnisdateien
resfile(1) = "faden1.1"
resfile(2) = "faden1.2"
resfile(3) = "faden1.3"
resfile(4) = "faden1.4"
22
23
24
25
26
27
28
29
30
31
32
10
1 Mechanik der Massenpunkte
33
resfile(5) = "faden1.5"
resfile(6) = "faden1.6"
resfile(7) = "faden1.7"
resfile(8) = "faden1.8"
resfile(9) = "faden1.9"
resfile(10) = "faden1.10"
34
35
36
37
38
39
40
*
-- Berechnung einiger benoetigter Parameter -dt = tend / dfloat(nmax)
*
-- Schleife ueber die Anfangsbedingungen
do n1 = 1, n_start
41
42
43
44
45
open(2,file=resfile(n1))
46
47
48
*
-- Anfangsbedingungen
read(1,*) phi_0, v_phi_0
t
= 0.d0
phi
= phi_0
v_phi = v_phi_0
write(2,*) t, phi, v_phi
*
-- Zeitschleife -do n = 1, nout
do m = 1, nmax / nout
-t = t + dt
phi_neu
= phi + v_phi * dt
v_phi_neu = v_phi - sin(phi) * dt
-phi
= phi_neu
v_phi = v_phi_neu
-enddo
write(2,*) t, phi, v_phi
enddo
49
50
51
52
53
54
55
56
57
58
*
59
60
61
62
*
63
64
65
66
67
68
*
69
70
close(2)
71
72
enddo
73
74
close(1)
75
76
77
STOP
end
Wie im vorher besprochenen C++-Programm haben wir dem Programm
einen erklärenden Kopf mit Informationen zum Programm vorangestellt. Danach kommt zuerst eine Zeile, die den Programmnamen festlegt, dann wird
in Zeile 11 festgelegt, dass keine implizite Variablendeklaration durchgeführt
werden soll. Normalerweise nimmt FORTRAN nämlich bei nicht deklarierten
Variablen an, dass sie – je nach deren Anfangsbuchstaben entweder integer
oder real sein sollen. Dies ist auf der einen Seite zwar bequem, hat aber die
fatale Folge, dass versehentlich nicht deklarierte Variablen nicht vom Compiler moniert werden, was wiederum dazu führt, dass Variablen einfach statt
doppelt genau sind. Ein weiteres Problem dieser impliziten Variablendeklara-
1.2 Das Fadenpendel
11
tion ist, dass Tippfehler bei Variablennamen nicht erkannt werden. Aus diesen
Gründen empfehlen wir, diese Zeile grundsätzlich einzufügen. Nun kommt in
den Zeilen 13 bis 19 die Definition einiger Variablen, sowie der Konstanten
n start max.
Im eigentlichen Programm schließlich werden zunächst aus einer Datei
faden1.par Parameter eingelesen, die das Programm benötigt – im Gegensatz zum C++-Programm ist hier also der Name der Eingabedatei, wie übrigens auch der Ausgabedatei, festgelegt, weil FORTRAN nicht standardmäßig
die Übergabe von Parametern unterstützt. Es schließt sich ein Block an, in
dem daraus Größen berechnet werden, die im weiteren Verlauf noch gebraucht
werden – in unserem Fall ist dies nur die Größe eines Zeitschritts dt. Nun
kann die Ausgabedatei geöffnet werden und eine äußere Schleife durchläuft
alle Anfangsbedingungen. Innerhalb dieser Schleife wird jede Anfangsbedingung jeweils aus der Parameterdatei faden1.par gelesen.
Das Lösungsverfahren, mit dem wir vom Zeitpunkt t zum Zeitpunkt t+dt
kommen, ist denkbar primitiv, d.h. wir haben die grobe Näherung (1.18) und
(1.19) verwendet. Wir werden jedoch sehen, dass wir uns diese Einfachheit
mit einer großen Zahl benötigter Zeitschritte und damit einer langen Rechenzeit erkaufen. Bevor wir uns jedoch um eine Verbesserung des Algorithmus’
bemühen, ist es Zeit, ein bisschen mit dem Programm zu spielen. Auf der
beiliegenden CD finden Sie in der Datei faden1.par einen Parametersatz,
mit dem wir anfangen wollen.
Wenn Sie das Programm faden1.cpp bzw. faden1.f komplilieren und
mit diesen Parametern starten, sehen Sie, dass das Programm sehr schnell
durchläuft – der Grund liegt nicht daran, dass es so effizient programmiert
wäre, sondern schlicht und einfach daran, dass das Problem sehr einfach
ist. Um nennenswerte Rechenzeiten zu bekommen, müssen wir statt zwei
Trajektorien, sagen wir, 1000 anfordern und die betrachtete Zeit bis auf τ =
1000 ausdehnen, aber dies ist Gegenstand von Übung 1.1. An dieser Stelle
wollen wir uns damit begnügen, dass auch dieses Programm bei geeigneter
Wahl der Parameter etwas länger braucht, und es deswegen Sinn machen
kann, den Algorithmus zu verbessern. Darauf kommen wir in Abschn. 1.2.3
zurück.
1.2.1 Verifizierung des Programms
Zunächst jedoch müssen wir uns davon überzeugen, dass dieses Programm
keine Fehler enthält; das können einfache Programmierfehler sein, oder aber
auch ein Fehler im Algorithmus, den wir uns überlegt haben. Dazu müssen
wir die gewonnenen Ergebnisse mit einer Referenz vergleichen. Wenn unser
Problem analytisch lösbar wäre, könnten wir diese analytische Lösung als
Referenz verwenden, aber leider ist das in unserem Beispiel ja nicht der Fall.
Überhaupt müssen wir im Allgemeinen davon ausgehen, dass uns eine solche
analytische Lösung nicht zur Verfügung steht, da wir ansonsten das Problem
gar nicht numerisch gelöst hätten. Ein möglicher Ausweg besteht in der Suche
12
1 Mechanik der Massenpunkte
nach Grenzfällen oder Sonderfällen, in denen wir das Problem lösen können –
in unserem Fall bietet sich der Grenzfall kleiner Auslenkungen an, für den das
Fadenpendel in das mathematische Pendel übergeht, dessen Lösung wir kennen (1.10). Das einzige, was wir hierfür tun müssen ist, in der Parameterdatei
faden1.par z.B. eine sehr kleine Auslenkung ohne Anfangsgeschwindigkeit
einzustellen und uns die Zahlenwerte für die Lösung (1.10) zu besorgen. Letztere können wir durch ein kleines, separates Programm berechnen lassen –
einfacher ist es jedoch, zwei entsprechende Zeilen
phi_an
= phi_0 * cos(t) + v_phi_0 * sin(t);
v_phi_an = v_phi_0 * cos(t) - phi_0 * sin(t);
in unser bestehendes Programm einzubauen. Wir dürfen nicht vergessen, diese beiden neuen Variablen im Deklarationsblock am Anfang des Programms
zu ergänzen! Und wir müssen diese Werte natürlich ausgeben lassen, was wir
am Einfachsten bewerkstelligen, indem wir sie an den bestehenden Schreibbefehl anhängen:
out_stream << t << " " << phi << " " << v_phi << " " << phi_an
<< " " << v_phi_an << " " << sqr(phi-phi_an) << " "
<< sqr(v_phi-v_phi_an) << "\n";
Für einen schnelleren Überblick haben wir nicht nur die analytische Lösung
selbst herausschreiben lassen, sondern auch deren Abweichung von der numerischen Lösung – diese Abweichung sollte möglichst klein sein, was wir dann
nach einem Programmlauf auch verifizieren können.
Eine andere Möglichkeit, das Programm faden1.cpp zu testen, bestünde
darin, einen Alternativalgorithmus zu entwickeln, der uns ebenfalls das gestellte Problem löst. Das hätte gegenüber dem Vergleich in einem analytisch
lösbaren Grenzfall den Vorteil, dass wir uns sicher wären, dass die Lösung
auch außerhalb dieses Grenzfalls richtig ist. Der Nachteil, den wir dabei in
Kauf nehmen, ist, dass wir, wenn die beiden gewonnenen Lösungen nicht
übereinstimmen, nicht wissen, in welchem der beiden Programme der Fehler
steckt, oder ob gar beide Lösungen falsch sind.
Eine weitere wichtige Verifikationsmöglichkeit bieten Erhaltungssätze. In
unserem Beispiel ist die Energie eine Erhaltungsgröße, was wir leicht im Programm überprüfen können:
E = 0.5 * sqr(v_phi) - cos(phi);
berechnet die Energie, die wir der Ausgabe einfach anhängen können. Zeigt
diese Größe eine signifikante Zeitabhängigkeit, enthält das Programm noch
einen Fehler.
1.2.2 Graphische Darstellung und Interpretation der Ergebnisse
Nachdem wir nun einigermaßen sicher sind, dass die gewonnene Lösung richtig ist, wollen wir sie uns ansehen, d.h. aus dem Zahlenwust, der in der Datei
faden1.res enthalten ist, Diagramme machen, die uns etwas sagen.
1.2 Das Fadenpendel
13
Das Einfachste ist natürlich die Darstellung von φ bzw. vφ als Funktion der skalierten Zeit τ . Eingabedateien für die verschiedenen in der Einleitung besprochenen Grafikprogramme finden Sie auf der beiliegenden CD – da
es nicht Thema dieses Buchs sein soll, diese Grafikprogramme zu erklären,
werden wir hier nicht näher darauf eingehen. Worauf wir jedoch eingehen
wollen, ist die geeignete Wahl von Anfangsbedingungen, um letztendlich eine
aussagekräftige Graphik zu bekommen. Dazu müssen wir unser physikalisches
Verständnis des Problems bemühen. Das genannte Problem hat zwei verschiedene Lösungstypen: zum einen Lösungen, bei denen der Massenpunkt um
den unteren Umkehrpunkt schwingt; zum anderen Lösungen, bei denen die
Energie zum Überschlagen ausreicht. Bei Anfangsbedingungen mit E = mgl
befinden wir uns gerade im Grenzfall zwischen diesen beiden Lösungstypen.
Für eine aussagefähige Grafik sollten wir darauf achten, dass beide Typen
und nach Möglichkeit auch der Grenzfall zwischen ihnen vertreten ist. Schön
ist es außerdem, wenn wir bei der Auswahl der Anfangsbedingungen eine
gewisse Systematik walten lassen – in diesem Fall könnten wir z.B. Anfangsbedingungen wählen, die alle φ0 = 0 gemeinsam haben und sich nur in vφ0
unterscheiden. Das entstehende Diagramm könnte dann etwa so aussehen:
(a)
v
3
(b)
2
10
1
5
0
0
-1
0
2
4
6
8 10
t
0
2
4
6
8 10
t
Abb. 1.2. Auslenkung φ und deren zeitliche Ableitung vφ beim Fadenpendel als
Funktion der Zeit
Wir erkennen im unteren Bereich von Abb. 1.2(a) die oszillatorische
Lösung (durchgezogene Kurve), bei der das Fadenpendel um die Ruhelage
φ = 0 pendelt. Bei der Überschlagslösung im oberen Bereich der Abbildung
(gestrichelte Kurve) hingegen wächst φ kontinuierlich an. Und schließlich
haben wir zwischen den beiden die Trajektorie des asymptotischen Grenzfalls (gepunktete Kurve), bei der der Massenpunkt zwar nicht zur Ruhelage
zurückkehrt, allerdings auch nie den Punkt φ = π erreicht bzw. überwindet.
Im rechten Diagramm (Abb. 1.2(b)) sehen wir die Winkelgeschwindigkeit
als Funktion der Zeit. Die oszillatorische Lösung (wieder die durchgezogene Kurve) bietet ein ähnliches Bild wie im vorangegangenen Diagramm. Die
Überschlagslösung (gestrichelt) sieht jedoch ähnlich wie die oszillatorische
Lösung aus, nur dass sie nicht um vφ = 0, sondern um einen von null verschiedenen Mittelwert pendelt. Wenn man genau hinsieht, stellt man außerdem
fest, dass die Periodendauer dieser Kurve kleiner ist, als bei der oszillato-
14
1 Mechanik der Massenpunkte
rischen Lösung. Ein völlig anderes Bild hingegen liefert der asymptotische
Grenzfall, bei dem vφ kontinuierlich abnimmt und für t → ∞ den Wert null
annimmt.
Das Problem aus einer etwas anderen Sicht beleuchtet das sogenannte
Phasendiagramm, bei dem die Ortsvariable φ gegen die Impulsvariable vφ
aufgetragen wird (Beachten Sie, dass wir in den skalierten Variablen nicht
zwischen Geschwindigkeit und Impuls unterscheiden müssen). Das Phasendiagramm für die selben Trajektorien wie in der vorangegangen Abb. 1.2 sieht
so aus:
v
2
1
0
-1
-2 0 2 4 6 8 10 12 14
Abb. 1.3. Phasendiagramm des Fadenpendels
Die geschlossene Trajektorie links unten entspricht der oszillatorischen
Lösung, die Trajektorie im oberen Bereich des Diagramms der Überschlagslösung, bei der der Impuls niemals null wird. Die dazwischenliegende Kurve
repräsentiert den asymptotischen Grenzfall, bei dem der Punkt φ = π erst
für τ = ∞ mit der Geschwindigkeit vφ = 0 erreicht wird. Diese Trajektorie
heißt Separatrix, da sie den Bereich der oszillatorischen vom Bereich der
Überschlagslösung abtrennt (separiert).
1.2.3 Verbesserung des Algorithmus
Wie bereits angedeutet, ist unser bisheriges Programm einfach, aber in puncto Rechengeschwindigkeit nicht sonderlich effektiv. Der Grund hierfür ist,
dass unser Algorithmus mit dem wir uns von einem Zeitpunkt zum nächsten
hangeln, außerordentlich kleine Zeitinkremente delta t verlangt, was wiederum zu einer sehr großen Zahl von Zeitschritten führt.
Die nächstliegende Methode, den bisherigen Algorithmus zu verbessern,
besteht darin, die Näherung (1.18), (1.19) um die nächsten Terme in den Potenzreihen (1.14), (1.15) zu erweitern. Die so gewonnene verfeinerte Näherung
lautet
φ(τ0 + ∆τ ) = φ(τ0 ) + vφ (τ0 )∆τ −
−
1
sin(φ(τ0 ))(∆τ )2
2
1
vφ (τ0 ) cos(φ(τ0 ))(∆τ )3 . . .
6
(1.20)
1.2 Das Fadenpendel
15
1
vφ (τ0 + ∆τ ) = vφ (τ0 ) − sin(φ(τ0 ))∆τ − vφ (τ0 ) cos(φ(τ0 ))(∆τ )2
2
1
sin(φ(τ0 )) cos(φ(τ0 )) + vφ2 (τ0 ) sin(φ(τ0 )) (∆τ )3 . . . .
+
6
(1.21)
Auf diese Weise bekommen Sie nur durch den Austausch der Zeilen
phi_neu
= phi + v_phi * dt;
v_phi_neu = v_phi - sin(phi) * dt;
durch
phi_neu
= phi + v_phi * dt - 0.5 * sin(phi) * sqr(dt);
v_phi_neu = v_phi - sin(phi) * dt - 0.5 * v_phi * cos(phi) * sqr(dt);
für die 2. Ordnung bzw. durch
phi_neu
= phi + v_phi * dt - 0.5 * sin(phi) * sqr(dt)
- 1/6 * v_phi * dcos(phi) * pow(dt,3);
v_phi_neu = v_phi - sin(phi) * dt - 0.5 * v_phi * dcos(phi) * sqr(dt)
+ 1./6 * (sin(phi)*cos(phi)+sqr(v_phi)*sin(phi)) * pow(dt,3);
für die 3. Ordnung zwei Programme faden2.cpp und faden3.cpp. Wir vereinfachen aber die weitere Vorgehensweise indem wir alle drei Algorithmen
(also erster, zweiter und dritter Ordnung) in einem einzigen Programm vereinigen:
1
2
3
4
5
6
/**************************************************************************
* Name:
faden_all.cpp
*
* Zweck:
Simuliert die Dynamik eines Fadenpendels
*
* Gleichung: (dˆ2/d tˆ2) phi + sin(phi) = 0
*
* Methode:
1. bis 3.Ordnung
*
**************************************************************************/
7
8
9
10
11
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
"tools.h"
12
13
using namespace std;
14
15
16
17
//-- Definition der globalen Variablen
const int n_start_max = 10;
const int n_fehler
= 17;
18
19
main( int argc, char *argv[] )
20
21
22
23
24
25
26
27
28
29
{
//-- Definition der Variablen
int
n, m, n1, n2, nmax, n_start, exp_min;
double t, tend, dt, phi_0, v_phi_0, phi_ref, v_phi_ref;
double phi1, phi_neu1, v_phi1, v_phi_neu1;
double phi2, phi_neu2, v_phi2, v_phi_neu2;
double phi3, phi_neu3, v_phi3, v_phi_neu3;
double fehler1, fehler2, fehler3, fehler[n_fehler][3];
ifstream in_stream;
16
30
1 Mechanik der Massenpunkte
ofstream out_stream, ref_stream;
31
32
33
34
35
36
37
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<4)
{
cout << " Aufruf: faden_all infile outfile reference-file\n";
exit(1);
}
38
39
40
41
42
43
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
ref_stream.open(argv[3]);
inout(tend,"tend");
inout(n_start,"n_start");
44
45
46
47
48
49
//-- Berechnung einiger benoetigter Parameter -dt = tend / nmax;
exp_min = 5;
for (n1=0; n1<n_fehler; n1++)
for (n2=0; n2<3; n2++) fehler[n1][n2] = 0;
50
51
52
53
54
55
56
//-- Schleife ueber die Anfangsbedingungen
for (n1=1; n1<=n_start; n1++)
{
//-- Anfangsbedingungen
in_stream >> phi_0;
in_stream >> v_phi_0;
57
58
59
60
61
62
63
//-- Schleife ueber die Zeitinkremente (sie werden jedesmal verdoppelt)
nmax = pow(2,n_fehler+exp_min) * 2;
for (n2=0; n2<n_fehler; n2++)
{
nmax = nmax / 2;
dt = tend / nmax;
64
65
66
67
68
69
70
71
t
= 0;
phi1
= phi_0;
v_phi1 = v_phi_0;
phi2
= phi_0;
v_phi2 = v_phi_0;
phi3
= phi_0;
v_phi3 = v_phi_0;
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//-- Zeitschleife
for (n=1; n<=nmax; n++)
{
t = t + dt;
phi_neu1 = phi1 + v_phi1 * dt;
v_phi_neu1= v_phi1 - sin(phi1) * dt;
phi_neu2 = phi2 + v_phi2 * dt - 0.5 *
v_phi_neu2= v_phi2 - sin(phi2) * dt;
- 0.5 * v_phi2 * cos(phi2) *
phi_neu3 = phi3 + v_phi3 * dt - 0.5 *
- 1/6 * v_phi3 * cos(phi3) *
v_phi_neu3= v_phi3 - sin(phi3) * dt
- 0.5 * v_phi3 * cos(phi3) *
+ 1/6 * (sin(phi3)*cos(phi3)
+ v_phi3*v_phi3*sin(phi3)) *
//--
sin(phi2) * dt*dt;
dt*dt;
sin(phi3) * dt*dt
dt*dt*dt;
dt*dt
dt*dt*dt;
1.2 Das Fadenpendel
phi1
phi2
phi3
}
89
90
91
92
= phi_neu1;
= phi_neu2;
= phi_neu3;
17
v_phi1 = v_phi_neu1;
v_phi2 = v_phi_neu2;
v_phi3 = v_phi_neu3;
93
if (n2 == 1)
{
phi_ref = phi3;
v_phi_ref = v_phi3;
ref_stream << phi_ref << " " << v_phi_ref << "\n";
}
94
95
96
97
98
99
fehler1 =
+
fehler2 =
+
fehler3 =
+
100
101
102
103
104
105
(phi1-phi_ref)*(phi1-phi_ref)
(v_phi1-v_phi_ref)*(v_phi1-v_phi_ref);
(phi2-phi_ref)*(phi2-phi_ref)
(v_phi2-v_phi_ref)*(v_phi2-v_phi_ref);
(phi3-phi_ref)*(phi3-phi_ref)
(v_phi3-v_phi_ref)*(v_phi3-v_phi_ref);
106
fehler[n2][0]
fehler[n2][1]
fehler[n2][2]
cout << n1 <<
}
107
108
109
110
111
=
=
=
"
fehler[n2][0] + fehler1;
fehler[n2][1] + fehler2;
fehler[n2][2] + fehler3;
" << n2 << "\n";
112
113
}
114
115
116
117
118
119
120
121
122
123
124
nmax = pow(2,n_fehler+exp_min);
for (n1=1; n1<n_fehler; n1++)
{
nmax = nmax / 2;
dt = tend / nmax;
out_stream << log(dt)/log(10) << " " << log(nmax)/log(10) << " "
<< log(fehler[n1][0])/log(10) << " "
<< log(fehler[n1][1])/log(10) << " "
<< log(fehler[n1][2])/log(10) << "\n";
}
125
126
127
128
129
out_stream.close();
ref_stream.close();
in_stream.close();
}
Exemplarisch für den Großteil der Programme dieses Buches ist hier die
Behandlung des Einlesens der Parameter aus der Eingabedatei und des Festhaltens derselben als Kommentare in der Ausgabedatei – siehe dazu auch das
vorangegangene Programm faden1.cpp. Im Gegensatz zu letzterem ist hier
nun nur eine Ausgabedatei vorhanden, so dass wir für jeden Parameter eine
Befehlszeile der Form
in_stream >> varname; out_stream << "! varname = " << varname << "\n";
haben. Da wir diese Syntax in jedem unserer Programme mehrfach brauchen,
wurde eine entsprechende Zeile in die Datei tools.h eingefügt, die in Zeile 11
eingebunden wird, so dass sich das Einlesen einer Variablen aus der Eingabedatei und das Herausschreiben derselben in die Ausgabedatei vereinfacht
zu
18
1 Mechanik der Massenpunkte
inout(varname,"varname");
In diese Datei tools.h können wir immer wiederkehrende einfache Aufgaben oder immer wieder gebrauchte Definitionen aufnehmen – ein Blick in
diese Datei auf der CD ist sicherlich sinnvoll.
Auch zu diesem Programm noch einige weitere Erläuterungen: Um den
Fehler zu ermitteln, benötigen wir die exakte, oder doch zumindest eine sehr
gute Lösung. Aus diesem Grund lösen wir die Bewegungsgleichung zunächst
mit einer sehr großen Zahl von Zeitinkrementen und reduzieren diese dann
von Schritt zu Schritt. Dies gibt uns die Möglichkeit die Lösung bei höchster
Ordnung und kleinstem Zeitinkrement als Referenzlösung zu definieren und in
phi ref und v phi ref abzuspeichern. Mittels dieser Referenzlösung können
wir den Fehler einer beliebigen Lösung bestimmen und summieren diesen in
einem Feld deviations(n1,n2) auf. Hierbei gibt n1 die Zahl der Zeitinkremente an, während n2 festlegt, ob wir den Fehler zur Lösung erster, zweiter
oder dritter Ordnung aufsummieren.
Da wir die Ergebnisse logarithmisch darstellen wollen, und das von uns
verwendete Grafikprogramm manchmal Probleme mit dieser Darstellung von
Daten hat, schreiben wir diese gleich logarithmisch in die Ausgabedatei, so
dass aus der Sicht des Grafikprogramms die Darstellung linear ist. Wenn wir
nun den Fehler als Funktion der Zahl der Einzelschritte auftragen, erhalten
wir das Diagramm in Abb. 1.4:
1
Fehler
10
-10
-20
10
-30
10
-2
10
10
-3
-4
10
10
-5
-6
10
dt
Abb. 1.4. Numerische Fehler beim Fadenpendel unter Verwendung verschiedener
Ordnungen bei der Lösung der Differentialgleichung, beginnend bei erster Ordnung
in der Abbildung ganz oben, dann zweiter und schließlich dritter Ordnung
Wir sehen, dass wie erwartet der Fehler durch die Hinzunahme des Terms
zweiter bzw. dritter Ordnung wesentlich kleiner geworden ist. Zudem stellen
wir fest, dass die Abnahme des Restfehlers mit der Zahl der Schritte bei einem
Algorithmus höherer Ordnung schneller erfolgt, als bei einem Algorithmus
niedrigerer Ordnung (in dem Diagramm ist die Steigung beim Algorithmus
dritter Ordnung am höchsten, beim Algorithmus erster Ordnung am niedrigsten). Die Tatsache, dass der Restfehler durch eine weitere Verkleinerung
der Schrittweite nicht auf beliebig kleine Werte gedrückt werden kann, liegt
an der endlichen Rechengenauigkeit durch das Zahlenformat – diese Grenz-
1.2 Das Fadenpendel
19
genauigkeit ließe sich nur durch den Wechsel von 16-Bit-Gleitkommazahlen
auf 32-Bit usw. verbessern. Wenn wir jedoch eine einigermaßen realistische
Genauigkeitsanforderung stellen, und sagen wir, Fehler von 10−7 tolerieren,
kommen wir in unserem Beispiel bei Verwendung nur der ersten Ordnung mit
ca. 2 Millionen Zeitschritten aus. Allein die Hinzunahme der nächsthöheren
Ordnung reduziert diesen Wert auf etwa 4000 Zeitschritte und bei Verwendung des Terms dritter Ordnung reichen sogar etwa 500 Zeitschritte. Diese
drastische Reduktion der Zahl nötiger Schritte macht die Tatsache, dass jeder
Einzelschritt eine unwesentlich längere Rechenzeit benötigt, unerheblich.
Nach dem bisher Gesagten scheint der Weg zu einem effektiven Algorithmus festzustehen: man verwende möglichst viele Terme der Potenzreihenentwicklung (1.14), (1.15). Es gibt jedoch auch Bewegungsgleichungen bei denen
die höheren Ordnungen zwar ohne prinzipielle Probleme berechnet werden
können, aber einen erheblichen Mehraufwand für den Computer darstellen.
Ein Beispiel hierfür sind alle Systeme mit vielen gekoppelten Teilchen. Betrachten wir eine Bewegungsgleichung der Form
d
xn = f (x1 , . . . , xN ) ,
dt
(1.22)
wobei N die Gesamtzahl der beteiligten Teilchen ist. Die zweiten Ableitungen
nach der Zeit erhalten wir durch
d
d2
xn = f (x1 , . . . , xN )
dt2
dt
N
df dxn
=
dxn dt
n=1
=
N
df
f (x1 , . . . , xN ) .
dx
n
n=1
(1.23)
(1.24)
(1.25)
Entsprechend erhalten wir die dritten Ableitungen nach der Zeit:
N
d2
d df
xn =
f (x1 , . . . , xN )
dt2
dt n=1 dxn
N
N d2 f
df df
=
f+
.
dxn1 dxn2
dxn1 dxn2
n =1 n =1
1
(1.26)
(1.27)
2
Sie sehen, dass die erste Ableitung eine Summe über alle Teilchen enthält,
die zweite Ableitung bereits eine Doppelsumme und Sie sehen vielleicht auch,
dass es entsprechend weitergeht, also die dritte Ableitung nach der Zeit bereits eine Dreifachsumme enthält. In diesem Fall führt die direkte Berechnung
der höheren Ableitungen zu einem sehr großen Rechenaufwand. Zum Glück
gibt es jedoch auch Algorithmen höherer Ordnung, bei denen diese höheren
Zeitableitungen gar nicht zur Verfügung gestellt werden müssen, sondern die
20
1 Mechanik der Massenpunkte
diese durch Differenzbildung an nahe beieinanderliegenden Punkten selbst
ausrechnen.
Solche Algorithmen müssen wir zudem nicht selbst programmieren, sondern wir können auf fertige Bibliotheken zurückgreifen, z.B. auf die beiliegende GSL-Bibliothek. Dies ist die Vorgehensweise, die ein Wissenschaftler
im Normalfall wählen wird, da sie gegenüber dem Programmieren eigener
Routinen einige wichtige Vorteile hat:
– eine erhebliche Reduktion der Entwicklungszeit
– eigene Routinen wären fast immer langsamer
– ein Wechsel des Algorithmus’ ist verhältnismäßig einfach,
– die Sicherheit einer gut getesteten Routine.
Alle diese Punkte werden wir auch am Beispiel des Doppelpendels bestätigt
finden.
Die Vorgehensweise, um das bestehende C++ bzw. FORTRAN-Programm
so zu modifizieren, dass die Lösung der Differentialgleichung durch eine Bibliotheksroutine erfolgt, erläutern wir bei diesem ersten Beispiel für alle drei
auf der CD-ROM beigefügten Bibliotheken GSL, MATPACK und SLATEC, da
diese bei den drei vorgestellten Bibliotheken sehr verschieden ist.
Beginnen wir jedoch mit einigen grundsätzlichen Überlegungen. Unabhängig von der verwendeten Bibliothek muss unser Computerprogramm
folgende Dinge festlegen bzw. zur Verfügung stellen:
–
–
–
–
–
–
die zu lösende Differentialgleichung,
das numerische Verfahren zur Lösung,
Anfangs- und Endzeit,
den Anfangsbedingungen
die Genauigkeitsanforderung an die gesuchte Lösung und
Speicherplatz, der für die Lösung benötigt wird.
Die verschiedenen Bibliotheken unterscheiden sich lediglich in der Art und
Weise, wie dies alles definiert wird.
Die GNU Scientific Library oder GSL legt diese Punkte jeweils einzeln
fest – dadurch wird das Programm zwar etwas länger, dafür erlaubt dieser modulare Aufbau aber einen noch leichteren Austausch insbesondere des
Lösungsalgorithmus’. Im Einzelnen sieht das dann so aus:
1
2
3
4
5
6
/**************************************************************************
* Name:
faden_lib.cpp
*
* Zweck:
Simuliert die Dynamik eines Fadenpendels
*
* Gleichung: (dˆ2/d tˆ2) phi + sin(phi) = 0
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
1.2 Das Fadenpendel
13
14
21
#include <math.h>
#include "tools.h"
15
16
using namespace std;
17
18
19
//---- globale Variablen
int icount1, icount2;
20
21
//-------------------------------------------------------------------------
22
23
int f(double t, const double x[], double dxdt[], void *params)
24
25
26
27
{
dxdt[0] = x[1];
dxdt[1] = -sin(x[0]);
28
29
30
31
icount1++;
return GSL_SUCCESS;
}
32
33
//-------------------------------------------------------------------------
34
35
36
int jac(double t, const double x[], double *dfdx, double dfdt[],
void *params)
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{
double mu = *(double *)params;
gsl_matrix_view dfdx_mat
= gsl_matrix_view_array (dfdx, 2, 2);
gsl_matrix * m = &dfdx_mat.matrix;
gsl_matrix_set (m, 0, 0, 0.0);
gsl_matrix_set (m, 0, 1, 1.0);
gsl_matrix_set (m, 1, 0, -cos(x[0]));
gsl_matrix_set (m, 1, 1, 0);
dfdt[0] = 0.0;
dfdt[1] = 0.0;
icount2++;
return GSL_SUCCESS;
}
52
53
//-------------------------------------------------------------------------
54
55
int main( int argc, char *argv[] )
56
57
{
58
59
60
61
62
63
64
65
66
//-- Definition der Variablen
int
n, n1, n_start, n_out, max_fct, iaux, error, normal;
double t, tend, dt, t1, t2;
double rtol, atol, h;
double mu = 10;
double x[2], x_ref[2];
ifstream in_stream, ref_stream;
ofstream out_stream;
67
68
69
70
71
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: faden_lib infile referencefile outfile\n";
22
72
73
1 Mechanik der Massenpunkte
exit(1);
}
74
75
76
77
78
79
80
81
//-- Einlesen der Parameter -ref_stream.open(argv[2]);
in_stream.open(argv[1]);
out_stream.open(argv[3]);
out_stream << "! Ergebnis-Datei generiert von faden_lib.cpp\n";
inout(tend,"tend");
inout(atol,"atol");
inout(rtol,"rtol");
inout(n_start,"n_start");
82
83
84
85
//-- Berechnung einiger benoetigter Parameter -dt = tend;
h = 1.e-6;
86
87
88
89
90
//-- Schleife ueber die Anfangsbedingungen
for (n1=1; n1<=n_start; n1++)
{
icount1 = 0; icount2 = 0;
91
92
93
94
95
//-- Anfangsbedingungen
in_stream >> x[0]; ref_stream >> x_ref[0];
in_stream >> x[1]; ref_stream >> x_ref[1];
t = 0;
96
97
98
out_stream << t << " " << x[0] << " " << x[1]
<< " " << 0 << " " << 0 << "\n";
99
100
101
102
103
104
105
//-- Initialisierung der GSL-Routine
const gsl_odeiv_step_type *T = gsl_odeiv_step_rk4;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, 2);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(2);
gsl_odeiv_system sys = {f, jac, 2, &mu};
106
107
108
109
110
111
112
113
114
while (t<tend)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, tend, &h, x);
if (status != GSL_SUCCESS) break;
}
out_stream << t << " " << x[0] << " " << x[1]
<< " " << (x[0]-x_ref[0])*(x[0]-x_ref[0])
<< " " << (x[1]-x_ref[1])*(x[1]-x_ref[1]) << "\n";
115
116
117
cout << " Aufrufe von f bzw. jac: " << icount1 << " " << icount2;
cout << "
Fehler : " << sqr(x[0]-x_ref[0])+sqr(x[1]-x_ref[1]) << "\n";
118
119
120
121
122
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
}
123
124
125
126
127
out_stream.close();
in_stream.close();
ref_stream.close();
}
Die entscheidende Neuerung befindet sich in Zeile 109: die eigentliche
Lösung der Differentialgleichung erfolgt nun in einer einzigen Zeile durch
1.2 Das Fadenpendel
23
Aufruf der Routine gsl odeiv evolve apply, die von GSL zur Verfügung
gestellt wird. Vor dem Aufruf dieser Routine muss diese initialisiert werden,
was in den Zeilen 100 bis 105 geschieht, wobei für uns vor allem die Zeilen 101,
103 und 105 interessant sind. In ersterer wird das numerische Verfahren zur
Lösung der Differentialgleichung festgelegt – im Fall von gsl odeiv step rk4
ist dies ein Runge-Kutta-Verfahren 4. Ordnung. In der Zeile 103 wird die
Genauigkeitsanforderung (absolute und relative Toleranz) festgelegt. Und
schließlich wird in Zeile 105 das Differentialgleichungssystem selbst deklariert, wozu ein Unterprogramm f gehört, das der Programmierer schreiben
muss. Dieser Routine wird ein Unterprogramm subroutine f übergeben,
in der die Differentialgleichung ẋ = f (x) festgelegt wird. Sie finden dieses
Unterprogramm im Programmtext ab Zeile 23. Die genaue Form dieses Unterprogramms (also die zu übergebenden Parameter und deren Reihenfolge)
hängt von der verwendeten Bibliotheksroutine ab. Um mitzuzählen, wie oft
dieses Unterprogramm aufgerufen wird, wurde noch ein Zähler icount eingebaut, und um ein Maß für die erreichte Genauigkeit zu bekommen, lesen wir
die Referenzergebnisse von faden all.f ein und vergleichen diese mit den
neu errechneten Lösungen (was natürlich voraussetzt, dass beide Programme
mit identischen Eingabe-Parametern gestartet wurden).
Dieser modulare Aufbau, bei dem das Lösungsverfahren und die Toleranzen jeweils in einem einzelnen Befehl festgelegt werden, eine weitere Befehlszeile den benötigten Speicherplatz zur Verfügung stellt und eine Zeile für
das Ausführen eines Integrationsschrittes benötigt wird, ist eine Besonderheit
der GSL-Bibliothek. Bei allen mir bekannten anderen Numerik-Bibliotheken,
erfolgen alle diese Schritte in einem einzigen Befehl, z.B. unter MATPACK:
error = ODE_Bulirsch(tstart,x,f,tend,h,hmax,atol,rtol,max_fct,normal,
aux,iaux,daux);
Der entsprechende Aufruf unter der C-Version von Visual Numerics IMSL
lautet
imsl_ode_runge_kutta(neq,&tstart,tend,x,state,f);
während er bei der FORTRAN-Version von Visual Numerics IMSL so aussieht:
call divprk(ido,4,f,tstart,tend,param,x)
Zum Abschluss geben wir noch die Befehlszeile für SLATEC, eine frei
verfügbare FORTRAN-Bibliothek an:
&
call dderkf(f,4,tstart,x,tend,info,rtol,atol,idid,
rwork,lrw,iwork,liw,rpar,ipar)
Während die beiden IMSL-Routinen den benötigten Speicherplatz automatisch bereitstellen, geschieht dies bei den anderen beiden Routinen durch
Übergabe von Feldern (aux, iaux und daux bzw. rwork und iwork), deren Mindestgröße von der Zahl der zu integrierenden Differentialgleichungen
abhängt.
24
1 Mechanik der Massenpunkte
Zusätzlich müssen wir für dderkf noch Platz im Arbeitsspeicher reservieren, der während der Lösung der Differentialgleichungen benötigt wird. Dies
geschieht durch Übergabe der Felder rwork und iwork, deren Dimensionierung sich aus der Beschreibung der Routine dderkf ergibt.
Allen diesen Bibliotheksroutinen gemein sind die Parameter atol und
rtol, die angeben welche Genauigkeitsanforderung wir an die gewonnene
Lösung stellen: atol legt einen Grenzwert für den Absolutfehler, rtol einen
für den relativen Fehler fest. In der Praxis ist es sinnvoll zu verifizieren, dass
eine Verschärfung dieser beiden Parameter zu keiner signifikanten Änderung
des Ergebnisses führt, um, falls dies doch der Fall sein sollte, kleinere Werte für diese beiden Parameter zu wählen. Desgleichen darf die Einteilung
der Zeitachse in diskrete Abschnitte keinen Einfluss auf die Lösung haben,
ansonsten kann der gewonnenen Lösung kein Vertrauen geschenkt werden.
Setzt man einen dieser Parameter übrigens auf null, wird als alleiniges
Kriterium für das Erreichen der geforderten Genauigkeit der jeweils andere
Parameter herangezogen. Eine der jeweiligen Situation angemessene Wahl
dieser Parameter ist nicht immer einfach, insbesondere solange man keine
Informationen über die zu erwartende Lösung hat:
– Legt man nur den relativen Fehler fest, bekommt man Probleme, wenn
die Lösung einen Nulldurchgang hat, da an diesem Punkt ein relativer
Fehler nicht definiert ist.
– Legt man nur den absoluten Fehler fest, bekommt man hingegen Schwierigkeiten, wenn die Lösung sehr große Werte annimmt, da dann die Anforderungen – relativ gesehen – immer ansteigen und unter Umständen
aufgrund des endlichen Zahlenformats gar nicht mehr erreicht werden
können.
In solchen Situationen kann man entweder versuchen, durch eine Festlegung
beider Parameter Abhilfe zu schaffen, oder man tastet sich in mehreren Probeläufen mit unterschiedlicher Wahl von atol und rtol an das Verhalten der
Lösung heran, wonach eine sinnvolle Wahl dieser beiden Werte möglich ist.
Das Ergebnis unseres Programms ist
> faden_lib faden_lib.par faden_all.ref faden_lib.res
Aufrufe von f bzw. jac: 12480 0
Fehler : 9.50448e-14
Aufrufe von f bzw. jac: 16379 0
Fehler : 1.82071e-13
Aufrufe von f bzw. jac: 18972 0
Fehler : 2.05772e-13
Aufrufe von f bzw. jac: 21788 0
Fehler : 1.55255e-13
Aufrufe von f bzw. jac: 14852 0
Fehler : 2.8778e-13
Aufrufe von f bzw. jac: 15856 0
Fehler : 2.52265e-13
Aufrufe von f bzw. jac: 18116 0
Fehler : 1.34472e-13
Aufrufe von f bzw. jac: 19800 0
Fehler : 2.90902e-13
Aufrufe von f bzw. jac: 22261 0
Fehler : 3.15646e-14
Aufrufe von f bzw. jac: 23830 0
Fehler : 1.32739e-11
Im Vergleich zu den bisherigen Programmen mit selbst geschriebener Routine zur Lösung des Gleichungssystems kommt das eingesetzte Runge-KuttaVerfahren odeiv step rk4 mit weniger Stützpunkten aus und erreicht eine
höhere Genauigkeit! Interessant ist auch,
1.2 Das Fadenpendel
25
Ohne großen Aufwand können wir nun eine andere der vielen zur Auswahl stehenden Bibliotheksroutinen verwenden: wir tauschen lediglich in Zeile 101 odeiv step rk4 durch odeiv step rk8pd, das eine Implementierung
des Runge-Kutta-Prince-Dormand-Algorithmus’ darstellt, aus und erhalten
das Programm faden lib2.cpp, das dann mit noch weniger Stützstellen auskommt:
> faden_lib2
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
faden_lib.par faden_all.ref faden_lib.res
f bzw. jac: 272 0
Fehler : 4.88865e-10
f bzw. jac: 169 0
Fehler : 8.7534e-12
f bzw. jac: 207 0
Fehler : 2.53785e-12
f bzw. jac: 245 0
Fehler : 8.01659e-11
f bzw. jac: 193 0
Fehler : 1.6238e-10
f bzw. jac: 169 0
Fehler : 2.65704e-10
f bzw. jac: 207 0
Fehler : 1.51987e-13
f bzw. jac: 233 0
Fehler : 7.65308e-12
f bzw. jac: 245 0
Fehler : 1.07912e-10
f bzw. jac: 307 0
Fehler : 2.82351e-10
Zum Abschluss wollen wir noch eine Routine (odeiv step bsimp) verwenden, die neben der Funktion f auch Gebrauch von jac macht – diese
Routine stellt ∂(dfi /dt)/∂xj zur Verfügung.
> faden_lib3
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
Aufrufe von
faden_lib.par faden_all.ref faden_lib.res
f bzw. jac: 1820 13
Fehler : 1.08217e-09
f bzw. jac: 420 3
Fehler : 2.26444e-09
f bzw. jac: 420 3
Fehler : 4.64311e-09
f bzw. jac: 420 3
Fehler : 2.3433e-09
f bzw. jac: 420 3
Fehler : 2.71246e-08
f bzw. jac: 420 3
Fehler : 1.49442e-09
f bzw. jac: 420 3
Fehler : 2.50474e-09
f bzw. jac: 420 3
Fehler : 3.35529e-09
f bzw. jac: 420 3
Fehler : 3.9156e-09
f bzw. jac: 420 3
Fehler : 7.62995e-10
Gegenüber odeiv step rk8pd stellt dieses Verfahren jedoch keine Verringerung des Rechenaufwands dar.
Bei komplexeren Problemen, bei denen wir noch mehr Rechenzeit einsparen wollen (oder müssen), besteht eine weitere Optimierungsmöglichkeit in
der Speicherung immer wieder verwendeter Ausdrücke. Zum Beispiel berechnet das Programm faden all.cpp bei jedem Zeitschritt das Produkt (dt)3 /6
zweimal und das Produkt (dt)2 /2 sogar viermal, was insgesamt leicht mehrere
tausend- oder gar millionenmal der Fall sein kann. Das ist natürlich unsinnig,
und wir hätten gut daran getan, den Wert dieser Audrücke in Variablen, z.B.
dt3 und dt2 zu speichern. Viele moderne Compiler erledigen diese Aufgabe
selbsttätig, so dass man sich nicht weiter darum kümmern muss – in diesen
Fällen genügt es, beim Compilieren eine entsprechende Option zu spezifizieren, die den Optimierer einschaltet.
26
1 Mechanik der Massenpunkte
1.3 Das Doppelpendel
Hängt man an den Massenpunkt am Ende des Fadenpendels ein weiteres
Pendel erhält man das sogenannte Doppelpendel (siehe Abb. 1.5), dem wir
uns in diesem Abschnitt widmen wollen.
Abb. 1.5. Das Doppelpendel, bestehend aus einem Fadenpendel, an dem ein weiteres Pendel angehängt ist
Hier die Bewegungsgleichungen durch eine Betrachtung der wirkenden
Kräfte aufzustellen, ist zwar möglich, aber sehr unübersichtlich. Der Grund
hierfür ist die Tatsache, dass die Kräfte im Allgemeinen nicht in eine Richtung
zeigen, in die sich die Massenpunkte bewegen können. Das hat Zwangskräfte
zur Folge, die von den Stangen aufgebracht werden und die – obwohl zunächst
unbekannt – in die Betrachtungen miteinbezogen werden müssen. Deswegen
wollen wir an dieser Stelle den Lagrangeformalismus in Erinnerung rufen,
der diese Schwierigkeiten elegant umgeht. Ausgangspunkt des Lagrangeformalismus ist ein mathematisches Verfahren zum Auffinden von Extremwerten
unter Beachtung von Nebenbedingungen, das unter dem Stichwort Lagrangeparameter in der Literatur zu finden ist. Wir wollen im Rahmen dieses
Buches auf eine Herleitung des Lagrangeformalismus’ verzichten, ihn aber
jedoch soweit skizzieren, dass der Leser keine Schwierigkeiten bei der Anwendung dieses Verfahrens haben sollte.
Der erste Schritt zu den Bewegungsgleichungen besteht im Auffinden sogenannter generalisierter Koordinaten – dabei handelt es sich um einen Satz
von Koordinaten q, die so gewählt sind, dass sie bereits die Zwangsbedingungen berücksichtigen, in deren Richtung eine Verrückung also immer möglich
ist. Im zweiten Schritt stellt man nun die Lagrangefunktion auf, die für ein
nicht explizit zeitabhängiges Problem wie das Vorliegende gleich der Differenz
aus kinetischer und potentieller Energie ist:
L(q, q̇) = Ekin − Epot .
(1.28)
1.3 Das Doppelpendel
27
Zu beachten ist hierbei, dass die Lagrangefunktion als Funktion der im ersten
Schritt gefundenen generalisierten Koordinaten und deren Zeitableitungen
zu schreiben ist. Im letzten Schritt erhält man die gesuchten Bewegungsgleichungen durch partielle Differentiation der Lagrangefunktion nach diesen
Koordinaten bzw. deren Ableitungen nach der Zeit:
∂L
d ∂L
=
.
dt ∂ q̇
∂q
(1.29)
Wenn Sie den Lagrangeformalismus noch nicht kennen, betrachten Sie
bitte diese extrem kurze Skizzierung als Rezept und identifizieren Sie die
einzelnen Schritte bei diesem und den folgenden Beispielen.
Beginnen wir also mit Schritt 1, dem Auffinden generalisierter Koordinaten. Dies sind am Einfachsten die beiden Winkel φ1 und φ2 . Ganz offensichtlich unterliegen diese beiden Winkel keinen Beschränkungen durch die
Zwangsbedingungen, die die beiden Stangen darstellen.
Im zweiten Schritt stellen wir nun die Lagrangefunktion auf, wozu wir
zunächst die kinetische und die potentielle Energie – ausgedrückt durch φ1 ,
φ2 , φ̇1 und φ̇2 – benötigen. Der Ausdruck für die potentiellen Energie ist
verhältnismäßig einfach:
Epot (φ1 , φ2 ) = −m1 gl1 cos φ1 − m2 g(l1 cos φ1 + l2 cos φ2 ) .
(1.30)
Für die kinetische Energie erhalten wir:
Ekin =
2
1
m1 l1 φ̇1
2
1
+ m2 l12 φ̇21 + l22 φ̇22 + 2l1 l2 φ̇1 φ̇2 cos(φ1 + φ2 ) .
2
(1.31)
Daraus erhalten wir definitionsgemäß die Lagrangefunktion
L = Ekin − Epot
(1.32)
2 1
1
= m1 l1 φ̇1 + m2 l12 φ̇21 + l22 φ̇22 + 2l1 l2 φ̇1 φ̇2 cos(φ1 + φ2 )
2
2
(1.33)
+m1 gl1 cos φ1 + m2 g(l1 cos φ1 + l2 cos φ2 ) .
Im dritten und letzten Schritt erhalten wir daraus die Bewegungsgleichungen:
d ∂L
d =
m1 l12 φ˙1 + m2 l12 φ˙1 + m2 l1 l2 φ˙2 cos(φ1 + φ2 )
dt ∂ φ˙1
dt
(1.34)
= m1 l1 φ̈1 + m2 l12 φ̈1 + m2 l1 l2 φ̈2 cos(φ1 + φ2 )
− m2 l1 l2 φ̇1 φ̇2 cos(φ1 + φ2 ) − m2 l1 l2 φ̇22 cos(φ1 + φ2 )
∂L
=
∂φ1
(1.35)
(1.36)
28
1 Mechanik der Massenpunkte
= −m2 l1 l2 φ̇1 φ̇2 sin(φ1 + φ2 ) − m1 gl1 sin φ1
(1.37)
−m2 gl1 sin φ1
d ∂L
d =
(1.38)
m2 l22 φ˙2 + m2 l1 l2 φ˙1 cos(φ1 + φ2 )
dt ∂ φ˙2
dt
= m2 l1 l2 φ̈2 + m2 l1 l2 φ¨1 cos(φ1 + φ2 ) − m2 l1 l2 φ̇2 sin(φ1 + φ2 )
1
−m2 l1 l2 φ̇1 φ̇2 sin(φ1 + φ2 )
∂L
=
∂φ2
= −m2 l1 l2 φ̇1 φ̇2 sin(φ1 + φ2 ) − m2 gl2 sin φ2 .
(1.39)
(1.40)
(1.41)
Sie sehen, wie diese scheinbar harmlose Veränderung gegenüber dem Problem
des vorherigen Abschnitts die Sache erheblich komplizierter macht. Um die
Bewegungsgleichungen übersichtlicher zu gestalten, skalieren wir ähnlich wie
beim Fadenpendel
l1
τ =t
(1.42)
g
und führen die Abkürzungen
m2
m1
l2
β=
l1
α=
(1.43)
(1.44)
ein. Damit erhalten wir:
(1 + α)φ̈1 + αβ φ̈2 cos(∆φ) + αβ φ̇22 sin(∆φ) = −(1 + α) sin(φ1 ) (1.45)
β 2 φ̈2 + φ̈1 cos(∆φ) − φ̇21 sin(∆φ) = − sin(φ2 ) ,
(1.46)
wobei wir die Abkürzung
∆φ = φ1 − φ2
(1.47)
eingeführt haben.
Da die Herleitung dieser Bewegungsgleichungen länglich und damit auch
fehlerträchtig ist, wollen wir zumindest einen einfachen Test auf Richtigkeit
durchführen: Im Grenzfall α → 0 hängt an der ersten Masse m1 ein quasi
masseloses Teilchen, das deren Bewegung natürlich nicht beeinflussen kann.
Wir sollten in diesem Grenzfall also für das erste Teilchen die Bewegungsgleichung des einfachen Fadenpendels erhalten, was tatsächlich der Fall ist.
Glücklicherweise haben wir damit die Hauptarbeit getan und es sollte
uns keine sonderlichen Schwierigkeiten bereiten, diese Differentialgleichungen zu implementieren. Am Einfachsten nehmen wir das bestehende Programm faden lib.cpp als Ausgangspunkt, kopieren dieses in eine neue Datei
dpendel1.cpp und nehmen die nötigen Änderungen vor:
1.3 Das Doppelpendel
1
2
3
4
5
6
29
/**************************************************************************
* Name:
dpendel1.cpp
*
* Zweck:
Simuliert die Dynamik eines Doppelpendels
*
* Gleichung: siehe Text
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
14
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<math.h>
"tools.h"
15
16
using namespace std;
17
18
19
//---- globale Variablen
double alpha, beta;
20
21
//-------------------------------------------------------------------------
22
23
int f(double t, const double x[], double dxdt[], void *params)
24
25
26
27
28
29
30
31
32
33
{
dxdt[0] = x[1];
dxdt[1] = ( - alpha*beta*x[3]*x[3] * sin(x[0]-x[2]) (1+alpha)*sin(x[0]) - alpha*x[1]*x[1]*sin(x[0]
-x[2])*cos(x[0]-x[2]) + alpha*sin(x[2])*cos(x[2]-x[0]) )
/ ( 1 + alpha*pow(sin(x[0]-x[2]),2) );
dxdt[2] = x[3];
dxdt[3] = ( -dxdt[1] * cos(x[0]-x[2])
+x[1]*x[1] * sin(x[0]-x[2]) - sin(x[2]) ) / beta;
34
35
36
return GSL_SUCCESS;
}
37
38
//-------------------------------------------------------------------------
39
40
41
42
int jac(double t, const double x[], double *dfdx, double dxdt[],
void *params)
43
44
45
46
{
return GSL_SUCCESS;
}
47
48
49
//-------------------------------------------------------------------------
50
51
int main( int argc, char *argv[] )
52
53
{
54
55
56
57
58
59
//-- Definition der Variablen
int
n, neq, n1, n2, nout;
double t, tend, dt, dt1, dtmin, dtfirst, eps, t1, t2, E, x[4];
double phi1_0, phi2_0, v_phi1_0, v_phi2_0, rtol, atol, h;
double mu = 10;
30
60
61
1 Mechanik der Massenpunkte
ifstream in_stream;
ofstream out_stream;
62
63
64
65
66
67
68
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: dpendel1 infile outfile\n";
return 0;
}
69
70
71
72
73
74
75
76
77
78
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von dpendel1.cpp\n";
inout(tend,"tend");
inout(phi1_0,"phi1_0");
inout(phi2_0,"phi2_0");
inout(v_phi1_0,"v_phi1_0");
inout(v_phi2_0,"v_phi2_0"); inout(alpha,"alpha");
inout(beta,"beta");
inout(nout,"nout");
in_stream.close();
79
80
81
82
83
84
85
86
//-- Initialiserung der Bibliotheksroutine
atol = 1.e-6; rtol = 1.e-6;
const gsl_odeiv_step_type *T = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, 4);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(4);
gsl_odeiv_system sys = {f, jac, 4, &mu};
87
88
89
90
//-- Berechnung einiger benoetigter Parameter -dt = tend / nout;
h = 1.e-6;
91
92
93
x[0] = phi1_0;
x[2] = phi2_0;
x[1] = v_phi1_0;
x[3] = v_phi2_0;
94
95
96
97
98
99
E = 0.5 * x[1]*x[1] + 0.5 * alpha * (x[1]*x[1]+ pow(beta*x[3],2)
+ 2 * beta * x[1] * x[3] * cos(x[0]-x[2]))
- cos(x[0]) - alpha * (cos(x[0])+beta*cos(x[2]));
out_stream << 0 << " " << x[0] << " " << x[1] << " "
<< x[2] << " " << x[3] << " " << E << "\n";
100
101
102
103
104
105
106
107
108
109
110
111
112
t = 0;
for (n=1; n<=nout; n++)
{
t1 = n * dt;
while (t<t1)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t1, &h, x);
if (status != GSL_SUCCESS) break;
}
E = 0.5 * x[1]*x[1] + 0.5 * alpha * (x[1]*x[1]+ pow(beta*x[3],2)
+ 2 * beta * x[1] * x[3] * cos(x[0]-x[2]))
- cos(x[0]) - alpha * (cos(x[0])+beta*cos(x[2]));
113
114
115
116
out_stream << t << " " << x[0] << " " << x[1] << " "
<< x[2] << " " << x[3] << " " << E << "\n";
}
117
118
gsl_odeiv_evolve_free(e);
1.3 Das Doppelpendel
119
120
31
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
121
122
123
out_stream.close();
}
Zunächst hat sich natürlich die Zahl der Zustandsvariablen von 2 in
faden lib.cpp auf 4 erhöht (zwei für jede der beiden Massen) und dementsprechend musste auch die Größe des allozierten Speicherplatzes angepasst
werden. Auf die Möglichkeit, in einem Programmlauf die Trajektorien zu
mehreren Anfangsbedingungen zu berechnen, haben wir hier verzichtet, um
das Programm übersichtlicher zu gestalten. Und schließlich berechnen wir in
den Zeilen 95–97 bzw. 110–112 als weitere Kontrolle die Energie und geben
diese in der Ergebnisdatei mit aus. Die Tatsache, dass diese konstant ist, ist
eine Kontrolle für die Richtigkeit sowohl der analytischen Berechnung der
Bewegungsgleichung als auch der Implementierung in einem Computerprogramm.
Als Ergebnis erhalten wir die vier Zustandsvariablen φ1 , φ2 , p1 = φ̇1 und
p2 = φ̇2 als Funktion der Zeit (Abb. 1.6). Wenn wir die Ergebnisse für die hier
gewählten Parameter (α = 1, und β = 0.8) betrachten, so erkennen wir zwar
auf kurzen Zeitskalen eine Art immer wiederkehrende Struktur, aber – im
Gegensatz zu den Ergebnissen beim Fadenpendel – keine Regelmäßigkeiten
über lange Zeiträume. Diese Abwesenheit von Regularität wollen wir nun
genauer untersuchen, wozu wir jedoch zuerst einige theoretische Grundlagen
benötigen, die wir in den beiden folgenden Abschnitten legen wollen.
Abb. 1.6. Zeitverlauf der beiden Auslenkungen φ1 (a) und φ2 (b) sowie der kanonisch konjugierten Impulse p1 (c) und p2 (d) beim Doppelpendel (α = 1, β = 0.8)
32
1 Mechanik der Massenpunkte
1.4 Integrable und nicht integrable Dynamiken
Dieser Abschnitt enthält zwar keinerlei Formeln, erfordert dafür ein erhöhtes
Maß an räumlichem Vorstellungsvermögen, vor allem in höherdimensionalen
Räumen. Es ist daher erfahrungsgemäß sinnvoll, sich die Absätze langsam zu
Gemüte zu führen und gegebenenfalls noch einmal zu lesen.
Kommen wir zunächst zum Beispiel des einfachen Fadenpendels zurück.
In diesem Fall ist der Phasenraum zweidimensional (φ und φ̇ als unabhängige
Variable). Einer Trajektorie steht jedoch nicht der gesamte Phasenraum zur
Verfügung, da die Energieerhaltung die Trajektorie auf eine Energiefläche1
zwingt. Diese zusätzliche Einschränkung hat zur Folge, dass der effektiv zur
Verfügung stehende Unterraum eindimensional ist, das heißt dass die Trajektorie durch Erhaltungssätze vollständig festgelegt ist. Diesen einer Trajektorie
zur Verfügung stehende Teilraum des Phasenraumes werden wir im Folgenden
immer wieder benötigen, weswegen wir ihm ein Symbol zuordnen: U. Eine
Dynamik, bei der ebenso viele von einander unabhängige Erhaltungsgrößen
wie dynamische Variablen existieren, heißt integrabel .
Schauen wir uns nun unter diesem Aspekt das Doppelpendel an: die Zahl
dynamischer Variabler ist nun zwei, was bedeutet, dass der Phasenraum vierdimensional ist. Die einzige Erhaltungsgröße, die wir unmittelbar erkennen,
ist die Energie; wir können aber zunächst nicht sicher sein, dass wirklich keine
weitere Erhaltungsgröße existiert.
Im Fall von zwei dynamischen Variablen gibt es jedoch eine Methode, genau dies zu überprüfen: der sogenannte Poincaré-Plot. Dazu stellen wir uns
zunächst vor, es gäbe eine weitere Erhaltungsgröße, die wir jedoch nicht gefunden haben. In diesem Fall wäre der zur Verfügung stehende Unterraum des
Phasenraums zweidimensional (anstatt dreidimensional im Fall von nur einer
Erhaltungsgröße). Definieren wir einen beliebigen dreidimensionalen Unterraum V des Phasenraumes, so dass der Schnitt von U und V nicht leer ist
und stellen uns die Frage, welche Dimension diese Schnittmenge hat. Im Fall
der integrablen Dynamik ist dieser Schnitt eindimensional (die Dimension ist
eins niedriger als die Dimension von U); im Fall der nicht integrablen Dynamik ist der Schnitt zweidimensional. Und diese beiden Fälle lassen sich wie
folgt unterscheiden: Wir bestimmen die Schnittpunkte der Trajektorie mit
V und stellen diese in einer graphischen Projektion auf die zweidimensionale
Zeichenfläche dar. Im ersten Fall ordnen sich diese entlang einer Linie an, im
zweiten Fall verteilen sich die Schnittpunkte auf einer Fläche.
Nun wollen wir unser Programm dpendel1.cpp so erweitern, dass es statt
dem Zeitverlauf einer Trajektorie die Schnittpunkte dieser Trajektorie mit
einer festgelegten Hyperfläche V ausgibt. Diese definieren wir durch φ̇1 = 0
1
Der Begriff Fläche ist hier im Sinne einer Hyperfläche zu verstehen, d.h. ein
Unterraum, dessen Dimension gegenüber dem Phasenraum um eins erniedrigt
ist. Im hier vorliegenden Fall eines zweidimensionalen Phasenraums ist die Energiefläche also ein eindimensionales Gebilde.
1.4 Integrable und nicht integrable Dynamiken
33
(ein Wert, den φ̇1 sicher bei jeder Trajektorie immer wieder annimmt). Das
Ergebnis ist das Programm dpendel2.cpp:
1
2
3
4
5
6
/**************************************************************************
* Name:
dpendel2.cpp
*
* Zweck:
Poincare-Plot fuer ein Doppelpendel
*
* Gleichung: siehe Text
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
14
15
#include
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<gsl/gsl_roots.h>
<math.h>
"tools.h"
16
17
using namespace std;
18
19
20
21
//---- globale Variablen
double alpha, beta, tstart, tend, akt_f0, akt_f1, akt_f2, akt_f3;
double phi1_alt, phi2_alt, v_phi1_alt, v_phi2_alt;
22
23
//-------------------------------------------------------------------------
24
25
int f(double t, const double x[], double dxdt[], void *params)
26
27
28
29
30
31
32
33
34
35
{
dxdt[0] = x[1];
dxdt[1] = ( - alpha*beta*x[3]*x[3] * sin(x[0]-x[2]) - (1+alpha)*sin(x[0])
- alpha*x[1]*x[1]*sin(x[0]-x[2])*cos(x[0]-x[2])
+ alpha*sin(x[2])*cos(x[2]-x[0]) )
/ ( 1 + alpha*pow(sin(x[0]-x[2]),2) );
dxdt[2] = x[3];
dxdt[3] = ( -dxdt[1] * cos(x[0]-x[2])
+x[1]*x[1] * sin(x[0]-x[2]) - sin(x[2]) ) / beta;
36
37
38
return GSL_SUCCESS;
}
39
40
//-------------------------------------------------------------------------
41
42
43
44
int jac(double t, const double x[], double *dfdx, double dxdt[],
void *params)
45
46
47
48
{
return GSL_SUCCESS;
}
49
50
//-------------------------------------------------------------------------
51
52
double funktion(double t, void *params)
53
54
55
56
{
double y[4], rtol, atol, t1, t2;
double mu = 10;
34
57
1 Mechanik der Massenpunkte
double h = 1.e-8;
58
59
60
61
62
y[0]
y[2]
t1 =
t2 =
= phi1_alt;
= phi2_alt;
tstart;
t;
y[1] = v_phi1_alt;
y[3] = v_phi2_alt;
63
64
65
66
67
68
69
atol = 1.e-7; rtol = 1.e-7;
const gsl_odeiv_step_type *T2 = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s2 = gsl_odeiv_step_alloc(T2, 4);
gsl_odeiv_control *c2 = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e2 = gsl_odeiv_evolve_alloc(4);
gsl_odeiv_system sys2 = {f, jac, 4, &mu};
70
71
72
73
74
75
while (t1<t2)
{
int status = gsl_odeiv_evolve_apply(e2,c2,s2,&sys2,&t1,t2,&h,y);
if (status != GSL_SUCCESS) break;
}
76
77
78
79
gsl_odeiv_evolve_free(e2);
gsl_odeiv_control_free(c2);
gsl_odeiv_step_free(s2);
80
81
82
83
84
akt_f0 = y[0];
akt_f2 = y[2];
return y[1];
}
akt_f1 = y[1];
akt_f3 = y[3];
85
86
//-------------------------------------------------------------------------
87
88
int main( int argc, char *argv[] )
89
90
{
91
92
93
94
//-- Definition der Variablen
int
n, neq, n1, n2, nout, status;
int
iter, max_iter = 100;
95
96
97
98
99
100
double x[4], t, dt, dt1, dtmin, dtfirst, t1, t2;
double phi1_0, phi2_0, v_phi1_0, v_phi2_0, E, rtol, atol, h, r;
double mu = 10;
ifstream in_stream;
ofstream out_stream;
101
102
103
104
105
106
107
rtol = 1.e-10; atol = 1.e-10;
const gsl_odeiv_step_type *T = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, 4);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(4);
gsl_odeiv_system sys = {f, jac, 4, &mu};
108
109
110
111
112
const gsl_root_fsolver_type *T1;
gsl_root_fsolver *s1;
gsl_function F;
F.function = &funktion;
113
114
115
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
1.4 Integrable und nicht integrable Dynamiken
116
117
118
119
{
cout << " Aufruf: dpendel2 infile outfile\n";
return 0;
}
120
121
122
123
124
125
126
127
128
129
//-- Einlesen der Parameter -out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von dpendel2.cpp\n";
in_stream.open(argv[1]);
inout(tend,"tend");
inout(phi1_0,"phi1_0");
inout(phi2_0,"phi2_0");
inout(v_phi1_0,"v_phi1_0");
inout(v_phi2_0,"v_phi2_0"); inout(alpha,"alpha");
inout(beta,"beta");
inout(nout,"nout");
in_stream.close();
130
131
132
133
134
//-- Berechnung einiger benoetigter Parameter -t = 0;
dt = tend / nout;
h = 1.e-6;
135
136
137
x[0] = phi1_0;
x[2] = phi2_0;
x[1] = v_phi1_0;
x[3] = v_phi2_0;
138
139
140
141
142
E = -cos(x[0]) + 0.5*sqr(x[1]) - alpha*(cos(x[0])+beta*cos(x[2]))
+ 0.5*alpha*( sqr(x[1])+sqr(beta*x[3])
+ 2*beta*x[1]*x[3]*cos(x[0]+x[2]) );
cout << " E = " << E << "\n";
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
for (n=1; n<=nout; n++)
{
t1 = (n-1) * dt;
t2 = n * dt;
phi1_alt
= x[0];
v_phi1_alt = x[1];
phi2_alt
= x[2];
v_phi2_alt = x[3];
while (t<t2)
{
status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t2, &h, x);
if (status != GSL_SUCCESS) break;
}
if (v_phi1_alt*x[1] < 0)
{
tstart = t1; tend = t2;
T1 = gsl_root_fsolver_brent;
s1 = gsl_root_fsolver_alloc (T1);
gsl_root_fsolver_set (s1, &F, t1, t2);
iter = 0;
do
{
iter++;
status = gsl_root_fsolver_iterate(s1);
r = gsl_root_fsolver_root(s1);
t1 = gsl_root_fsolver_x_lower(s1);
t2 = gsl_root_fsolver_x_upper(s1);
status = gsl_root_test_interval(t1, t2, 0, 1.e-6);
}
while (status == GSL_CONTINUE && iter < max_iter);
out_stream << t1
<< " " << akt_f0 << " " << akt_f1 << " "
<< akt_f2 << " " << akt_f3 << "\n";
gsl_root_fsolver_free(s1);
}
35
36
175
1 Mechanik der Massenpunkte
}
176
177
178
179
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
180
181
182
out_stream.close();
}
Zunächst integriert das Programm wie dpendel1.cpp die Bewegungsgleichungen. Sobald v phi1 sein Vorzeichen wechselt, wird ein Nullstellensuchprogramm damit beauftragt, den exakten Schnittpunkt mit v phi1 = 0 zu
bestimmen. Hierzu muss eine Funktion funktion implementiert werden, die
für jeden Zeitpunkt den Wert von v phi1 zurückgibt. Diese Funktion arbeitet
ebenfalls mit der Bibliotheksroutine zur Lösung von Differentialgleichungen,
allerdings sind deren Aufrufe nicht wie bisher immer zu aufeinander folgenden
Intervallen, sondern zu Intervallen mit immer demselben Startwert. Aus diesem Grund müssen wir vor jedem Aufruf deren Workarrays neu initialisieren.
Außerdem achten wir auf eine strenge Trennung der Integration der Bewegungsgleichungen im Hauptprogramm (mit den Kontrollvariablen s, c, e und
sys) und derjenigen in der Funktion funktion (mit den Kontrollvariablen
s2, c2, e2 und sys2).
Starten wir zunächst das Programm mit den Parametern
> cat dpendel2.par
200000
0 1 1 0
1 1
1000000
und stellen die erhaltenen Schnittpunkte in der φ1 -φ2 -Ebene dar, so erhalten
wir:
Abb. 1.7. Poincaré-Plot für eine chaotische Dynamik: die Punkte füllen eine Fläche
aus
Dies ist das charakteristische Bild, wie wir es bei einem nicht integrablen System erwarten: die Punkte füllen (wenn auch nicht gleichmäßig) eine
1.4 Integrable und nicht integrable Dynamiken
37
Fläche. Diese Fläche weist in unserem Beispiel eine unegelmäßige Berandung
und darüber hinaus Löcher auf – es stellt sich die Frage, wie die Trajektorien aussehen, die durch diese Löcher gehen. Um diese Frage zu beantworten,
modifizieren wir lediglich die Anfangsbedingungen für unsere Trajektorie:
> cat dpendel2a.par
200000
-0.5 0.6 0 1.4423578
1 1
1000000
Der Wert von 1.442375 für v phi2 0 ergibt sich aus der Bedingung, dass
die Gesamtenergie aus (1.30) und (1.31) die selbe sein soll, wie bei der vorangegangen Parameter-Datei. Das Bild, das wir für diese Anfangsbedingungen
erhalten, ist völlig anders:
Abb. 1.8. Poincaré-Plot für eine integrable Dynamik: die Punkte ordnen sich entlang eines eindimensionalen Gebildes (in diesem Fall zweier Kurven) an
Hier liegt eindeutig die Situation einer integrable Dynamik vor, bei der
sich diese Durchstoßpunkte entlang einer oder mehrerer Kurven anordnen
müssen.
Wir könnten nun für eine Vielzahl von Anfangsbedingungen entsprechende Plots erstellen, um zu entscheiden, in welchen Bereichen des Phasenraumes
die Bewegung regulär und in welchen die Dynamik chaotisch ist. Wir können
jedoch diese etwas mühselige Vorgehensweise vereinfachen, indem wir mehrere solcher Plots in einer Darstellung vereinigen. Wir müssen dabei jedoch
sicherstellen, dass durch die Projektion auf eine zweidimensionale Darstellung
keine sich überlappenden Strukturen erzeugt werden – in diesem Fall könnte
man dem entstehenden Diagramm keine Informationen mehr entlocken. Am
Einfachsten erfüllen wir diese Forderung, indem wir Startwerte mit gleicher
Gesamtenergie wählen, dann nämlich liegen alle Trajektorien auf einer gemeinsamen Hyperfläche. Wenn wir dies berücksichtigen, kommen wir durch
Modifikation des Hauptprogramms von dpendel2.cpp zu dem Programm
dpendel3.cpp:
38
1 Mechanik der Massenpunkte
...
118
119
120
121
122
123
124
125
126
127
//-- Einlesen der Parameter -out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von dpendel3\n";
in_stream.open(argv[1]);
inout(tend,"tend");
inout(dphi1,"dphi1");
inout(dphi2,"dphi2");
inout(E_0,"E_0");
inout(alpha,"alpha");
inout(beta,"beta");
inout(nmax,"nmax");
inout(nout,"nout");
in_stream.close();
128
129
130
131
//-- Berechnung einiger benoetigter Parameter -dt = tend / nout;
h = 1.e-6;
132
133
F.function = &funktion;
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
for (n1=-nmax; n1<=nmax; n1++)
for (n2=-nmax; n2<=nmax; n2++)
{
t = 0;
x[0] = n1*dphi1; x[1] = 0;
x[2] = n2*dphi2; // x[3] = 0;
a = E_0 + cos(x[0]) + alpha*(cos(x[0])+beta*cos(x[2]));
x[3] = sqrt(2*a / (alpha*sqr(beta)));
cout << n1 << " " << n2 << "\n";
for (n=1; n<=nout; n++)
{
t1 = (n-1) * dt;
t2 = n * dt;
phi1_alt
= x[0];
v_phi1_alt = x[1];
phi2_alt
= x[2];
v_phi2_alt = x[3];
while (t<t2)
{
status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t2, &h, x);
if (status != GSL_SUCCESS) break;
}
if (v_phi1_alt*x[1] < 0)
{
tstart = t1; tend = t2;
T1 = gsl_root_fsolver_brent;
s1 = gsl_root_fsolver_alloc (T1);
gsl_root_fsolver_set (s1, &F, t1, t2);
iter = 0;
do
{
iter++;
status = gsl_root_fsolver_iterate(s1);
r = gsl_root_fsolver_root(s1);
t1 = gsl_root_fsolver_x_lower(s1);
t2 = gsl_root_fsolver_x_upper(s1);
status = gsl_root_test_interval(t1, t2, 0, 1.e-6);
}
while (status == GSL_CONTINUE && iter < max_iter);
out_stream << t1
<< " " << akt_f0 << " " << akt_f1 << " "
<< akt_f2 << " " << akt_f3 << "\n";
gsl_root_fsolver_free(s1);
}
1.4 Integrable und nicht integrable Dynamiken
}
176
177
39
}
178
179
180
181
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
182
183
184
185
out_stream.close();
return 0;
}
Das Einlesen der Eingabeparameter in Zeile 142–149 wurde dahingehend
geändert, dass v phi 1 nicht mehr eingegeben werden muss, sondern fest
gleich null ist. Auch v phi 2 muss nicht mehr eingegeben werden, da dieses durch die Energie E festgelegt wird. Variiert werden phi 1 und phi 2,
so dass statt diesen Variablen deren Schrittweiten dphi1 und dphi2 eingelesen werden. Neu hinzugekommen ist die Festlegung der Anfangswerte aus
den Schleifenzählern n1 und n2 sowie der Anfangsenergie E 0 in den Zeilen
163–168. Zu beachten ist dabei, dass es Wertekombinationen von φ1 und φ2
gibt, bei denen die vorgegebene Energie E0 nicht mehr möglich ist, weil die
zugehörige potentielle Energie bereits größer ist. In diesem Fall hat die quadratische Gleichung für vφ1 keine reelle Lösung. Um solche Situationen nicht
weiter zu betrachten, fragen wir in Zeile 177 das Vorzeichen des Terms des
Radikanden a ab.
Wir haben die Eingabeparameter geringfügig geändert, um das entstehende Bild etwas zu variieren und die regulären Inseln etwas zu vergrößern:
> cat dpendel3.par
2000
0.2 0.2
-1.5403
1 1.2
5
10000
Das Ergebnis (Abb. 1.9) zeigt sowohl Bereiche, wie wir Sie bei einer integrablen Dynamik erwarten, als auch Bereiche, die dem nicht integrablen Fall
Abb. 1.9. Poincaré-Plot für eine Dynamik mit chaotischen und regulären Bereichen
40
1 Mechanik der Massenpunkte
entsprechen. Für eine Charakterisierung dieser beiden parallel vorliegenden
Situationen ist die Unterscheidung reguläre vs. chaotische Bewegung geeignet, auf die wir im folgenden Abschnitt eingehen.
1.5 Reguläre und chaotische Dynamiken
Die Unterscheidung integrabel zu nicht integrabel ist eng verwandt mit einer
anderen Klassifizierung in der klassischen Mechanik. Diese nimmt als Klassifizierungskriterium die Sensitivität der Trajektorie auf geringfügige Änderungen der Startwerte. Wächst der Abstand zweier infinitesimal benachbarter Punkte im Phasenraum mit der Zeit exponentiell an, so spricht man von
chaotischem Verhalten, ist dieses Anwachsen langsamer als exponentiell (also
z.B. nach einem Potenzgesetz) nennt man die Dynamik regulär.
In dem letzten Absatz sind einige Begriffe enthalten, die einer näheren
Erläuterung bedürfen. Zunächst ist vom Abstand zwischen Trajektorien die
Rede, was allerdings etwas unpräzise ist. Gemeint ist der Abstand zwischen
zwei Punkten im Phasenraum und da sich diese beiden jeweils auf ihrer Trajektorie bewegen, ist auch der Abstand eine Funktion der Zeit. Wir benötigen
also eine Abstandsfunktion ∆ im Phasenraum, die jeweils zwei Punkten einen
Abstand zuordnet – eine solche Abstandsfunktion wird auch Metrik genannt.
Dann müssen wir noch klären, was unter benachbart gemeint ist. Damit meinen wir, dass der Anfangsabstand der beiden Punkte infinitesimal sein soll,
d.h. wir betrachten den Grenzwert ∆(t0 = 0) → 0. Solche Abstände verhalten
sich in einem bestimmten Sinne linear: skaliert man nämlich den infinitesimalen Anfangsabstand mit einem Faktor α, so bleibt diese Skalierung erhalten:
Gegeben sind drei Trajektorien x0 (t), x1 (t) und x2 (t), die sich zum Zeitpunkt t = 0 um
(1.48)
∆01 (t = 0) = |x0 (0) − x1 (0)|
bzw.
∆02 (t = 0) = x2 (0) − x2 (0) = α∆01 (0)
(1.49)
unterscheiden. Diese Abstände ändern sich nun aufgrund der Dynamik des
Systems, im Limes |∆x01 (t = 0)| → 0 gilt jedoch die Beziehung
∆02 (t) = α∆01 (t)
(1.50)
für alle (endlichen) Zeiten t.
Um Missverständnisse zu vermeiden, bedarf es einiger Erklärungen, mit
deren Hilfe wir das Gesagte dann auch in einer quantitativen Größe zum
Ausdruck bringen können.
Der Abstand zweier Punkte im Phasenraum wird im Allgemeinen nicht
kontinuierlich größer werden, sondern zwischenzeitlich auch mal abnehmen.
Bei der Unterscheidung zwischen regulär und chaotisch interessiert man sich
ausschließlich für das Langzeitverhalten (t → ∞), bei dem dieses intermediäre
1.5 Reguläre und chaotische Dynamiken
41
Verhalten nicht beachtet wird. Da trotz dieser langen Zeiten und des potentiell exponentiellen Anwachsens des Abstandes letzterer klein bleiben muss,
ist die Reihenfolge dieser beiden Limites entscheidend: zuerst wird der Limes
Abstand gegen null und dann der Limes Zeit gegen unendlich durchgeführt!
Unter Berücksichtigung des bisher gesagten definieren wir den LyapunovExponenten durch
1
ln (∆(T )/∆(0)) .
T →∞ ∆(t=0)→0 T
λ = lim
lim
(1.51)
Bevor wir an die numerische Berechnung des Lyapunov-Exponenten gehen, noch einige Bemerkungen:
–
Der Lyapunov-Exponent ist ein Maß für die Empfindlichkeit gegen kleine
Änderung der Anfangsbedingungen. Ist der Lyapunov-Exponent kleiner
oder gleich null, wachsen Abstände zwischen Trajektorien im Allgemeinen
polynomial an, während ein Lyapunov-Exponent größer als null bedeutet, dass diese Abstände exponentiell anwachsen. Da in der Realität ein
Zustand nie genau bekannt ist, sondern wir immer eine Unsicherheit z.B.
durch Messungenauigkeiten haben, ist der Lyapunov-Exponent auch ein
Maß dafür wie schnell diese Unsicherheit wächst und unsere Kenntnis
über das System verlorengeht.
– Die Wahl der Metrik ∆ hat – von pathologischen Ausnahmen abgesehen
– keinen Einfluss auf den Lyapunov-Exponenten.
– Auch die Wahl des Nachbarpunktes, von dem die zweite Trajektorie startet, spielt – wieder abgesehen von singulären Fällen – keine Rolle.
Nun jedoch zur numerischen Berechnung des Lyapunov-Exponenten. Die
Definition (1.51) mit den beiden Limites ∆(t = 0) → 0 und t → ∞ stellt numerisch ein Problem dar, da die endliche Genauigkeit, die uns der Computer
zur Verfügung stellt, den ersten Grenzübergang unmöglich macht. Bei den
bisherigen Anwendungen stellte dies kein Problem dar, da relative Abstände
von 10−10 und kleiner durchaus aufgelöst werden können. Erst durch den
zweiten Grenzübergang t → ∞ werden diese kleinen Abstände zu einem begrenzenden Faktor, da sie eine Einschränkung für die Zeiten geben, zu denen
der Abstand noch klein ist. Nehmen wir beispielsweise einen Anfangsabstand
von 10−10 und einen Lyapunov-Exponenten von 1 an und fragen uns, wie
lange der Abstand kleiner als 10−4 bleibt, so ergibt sich diese maximale Zeit,
bis zu der wir die Zeitentwicklung verfolgen dürfen:
tmax = ln(10−4 /10−10 ) ≈ 16 .
(1.52)
Selbst unter etwas günstigeren Annahmen (kleinerer Anfangsabstand, kleinerer Lyapunov-Exponent) sind wir weit davon entfernt, den Grenzübergang
t → ∞ vernünftig durchzuführen.
Die Beziehung (1.50) ermöglicht uns jedoch einen Trick, mit dem wir
dieses Dilemma beseitigen können. Wir beginnen mit einem moderaten Anfangsabstand, der uns nur eine kurze Zeitentwicklung ∆t erlaubt, ohne den
42
1 Mechanik der Massenpunkte
Linearitätsbereich zu verlassen. Danach skalieren wir den – inzwischen angewachsenen Abstand – auf seinen ursprünglichen Wert zurück, was wegen
(1.50) einer Verkleinerung des Anfangsabstands äquivalent ist:
∆0eff = ∆0
∆1
.
∆0
(1.53)
Nach einer erneuten Entwicklung über einen kurzen Zeitbereich skalieren wir
erneut zurück und so weiter. Da wir jedesmal effektiv den Anfangsabstand
verkleinern, erreichen wir im Prinzip beliebig kleine Werte für denselben,
ohne je wirklich mit solchen winzigen Differenzen rechnen zu müssen:
∆0eff = ∆0
∆1 ∆2
∆N
...
,
∆0 ∆0
∆0
(1.54)
wobei N die Zahl der insgesamt durchgeführten Zeitschritte ∆t ist.
Diese Vorgehensweise liefert uns eine Folge von Abständen ∆1 , ∆2 , . . . ,
die die beiden Phasenraumpunkte am Ende des ersten, zweiten, dritten
. . . Zeitintervalls haben. Den gesuchten Lyapunov-Exponenten erhalten wir
dann als
N ∆t ∆N
λ=
(1.55)
ln
∆0eff
∆1
N ∆t ∆N ∆N −1
=
...
(1.56)
ln
∆ 0 ∆0
∆0
N
1 λn ,
=
N n=1
wobei wir quasi Kurzzeit-Lyapunov-Exponenten
∆n
1
ln
λn =
∆t
∆0
(1.57)
(1.58)
eingeführt haben. Nach Gleichung (1.57) ist der Lyapunov-Exponent also
einfach der Mittelwert dieser Kurzzeit-Exponenten.
Nachdem wir diese Informationen vorangestellt haben, sollte das folgende
Programm verständlich sein:
1
2
3
4
5
6
/**************************************************************************
* Name:
ljapu.cpp
*
* Zweck:
Ljapunov-Exponent fuer ein Doppelpendel
*
* Gleichung: siehe Text
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
1.5 Reguläre und chaotische Dynamiken
12
13
14
43
#include <gsl/gsl_odeiv.h>
#include <math.h>
#include "tools.h"
15
16
using namespace std;
17
18
19
20
21
22
//---double
double
double
double
globale Variablen
alpha, beta;
phi1_alt, phi2_alt, v_phi1_alt, v_phi2_alt;
tstart, tend;
akt_f0, akt_f1, akt_f2, akt_f3;
23
24
//-------------------------------------------------------------------------
25
26
int f(double t, const double x[], double dxdt[], void *params)
27
28
29
30
31
32
33
34
35
36
37
{
dxdt[0] = x[1];
dxdt[1] = ( - alpha*beta*x[3]*x[3] * sin(x[0]-x[2])
- (1+alpha)*sin(x[0])
- alpha*x[1]*x[1]*sin(x[0]-x[2])*cos(x[0]-x[2])
+ alpha*sin(x[2])*cos(x[2]-x[0]) )
/ ( 1 + alpha*pow(sin(x[0]-x[2]),2) );
dxdt[2] = x[3];
dxdt[3] = ( -dxdt[1] * cos(x[0]-x[2])
+x[1]*x[1] * sin(x[0]-x[2]) - sin(x[2]) ) / beta;
38
39
40
return GSL_SUCCESS;
}
41
42
//-------------------------------------------------------------------------
43
44
45
46
int jac(double t, const double x[], double *dfdx, double dxdt[],
void *params)
47
48
49
50
{
return GSL_SUCCESS;
}
51
52
//-------------------------------------------------------------------------
53
54
int main( int argc, char *argv[] )
55
56
{
57
58
59
60
61
62
63
64
//-- Definition der Variablen
int
n, nout, status, i;
double dt, t1, tend1, t2, tend2, phi1_0, phi2_0, v_phi1_0, v_phi2_0;
double rtol, atol, h1, h2;
double mu = 10, x1[4], x2[4], dist0 = 1.e-6, dist1, ljapu, ljapu_1;
ifstream in_stream;
ofstream out_stream;
65
66
67
68
69
70
rtol = 1.e-10;
atol = 1.e-10;
const gsl_odeiv_step_type *T1 = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s1 = gsl_odeiv_step_alloc(T1, 4);
gsl_odeiv_control *c1 = gsl_odeiv_control_y_new(atol, rtol);
44
71
72
73
74
75
76
1 Mechanik der Massenpunkte
gsl_odeiv_evolve *e1 = gsl_odeiv_evolve_alloc(4);
const gsl_odeiv_step_type *T2 = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s2 = gsl_odeiv_step_alloc(T2, 4);
gsl_odeiv_control *c2 = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e2 = gsl_odeiv_evolve_alloc(4);
gsl_odeiv_system sys = {f, jac, 4, &mu};
77
78
79
80
81
82
83
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: ljapu infile outfile\n";
return 0;
}
84
85
86
87
88
89
90
91
92
93
//-- Einlesen der Parameter -out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von ljapu.cpp\n";
in_stream.open(argv[1]);
inout(tend,"tend");
inout(phi1_0,"phi1_0");
inout(phi2_0,"phi2_0");
inout(v_phi1_0,"v_phi1_0");
inout(v_phi2_0,"v_phi2_0");
inout(alpha,"alpha");
inout(beta,"beta");
inout(nout,"nout");
in_stream.close();
94
95
96
97
98
//-dt =
h1 =
h2 =
Berechnung einiger benoetigter Parameter -tend / nout;
1.e-6;
1.e-6;
99
100
101
102
103
x1[0]
x1[2]
x2[0]
x2[2]
=
=
=
=
phi1_0;
phi2_0;
phi1_0 + dist0;
phi2_0;
x1[1]
x1[3]
x2[1]
x2[3]
=
=
=
=
v_phi1_0;
v_phi2_0;
v_phi1_0;
v_phi2_0;
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
ljapu = 0;
for (n=1; n<=nout; n++)
{
tend1 = n * dt;
tend2 = n * dt;
while (t1<tend1)
{
status = gsl_odeiv_evolve_apply(e1,c1,s1,&sys,&t1,tend1,&h1,x1);
if (status != GSL_SUCCESS) break;
}
h2 = 1.e-6;
while (t2<tend2)
{
status = gsl_odeiv_evolve_apply(e2,c2,s2,&sys,&t2,tend2,&h2,x2);
if (status != GSL_SUCCESS) break;
}
dist1 = sqrt( sqr(x1[0]-x2[0]) + sqr(x1[1]-x2[1])
+ sqr(x1[2]-x2[2]) + sqr(x1[3]-x2[3]) );
ljapu_1 = log( dist1 / dist0 ) / dt;
ljapu = ( (n-1)*ljapu + ljapu_1 ) / n;
if (n%100==0) cout << " " << n << t1 << " " << ljapu << "\n";
out_stream << t1 << " " << ljapu << "\n";
for (i=0; i<4; i++)
x2[i] = x1[i] + (x2[i]-x1[i]) * dist0 / dist1;
gsl_odeiv_evolve_reset (e2);
1.6 Das Teilchen in der Schachtel
130
131
45
gsl_odeiv_step_reset (s2);
}
132
133
134
135
136
137
138
gsl_odeiv_evolve_free(e1);
gsl_odeiv_control_free(c1);
gsl_odeiv_step_free(s1);
gsl_odeiv_evolve_free(e2);
gsl_odeiv_control_free(c2);
gsl_odeiv_step_free(s2);
139
140
141
out_stream.close();
}
Die entscheidende Neuerung ist in den Zeilen 121–124, wo zum einen der in
Gleichung (1.58) eingehende Kurzzeit-Lyapunov-Exponent ljapu1 und zum
anderen der gleitende Mittelwert ljapu aus allen bislang ermittelten ljapu1
berechnet wird. Dieser Mittelwert ist – wenn wir über eine hinreichend lange
Zeit mitteln – gleich dem gesuchten Lyapunov-Exponenten.
In Abb. 1.10 haben wir diese Berechnung eines Lyapunov-Exponenten
zeitlich für eine reguläre und eine chaotische Dynamik verfolgt, wobei dieser
Unterschied lediglich durch verschiedene Anfangsbedingungen erreicht wurde (d.h. wir haben hier gleichzeitig ein Beispiel für einen gemischten Phasenraum). Die Parameter waren α = 1 und β = 1, die Anfangsbedingungen
φ1 (t = 0) = 0, φ2 (t = 0) = 0.4 bzw. 0.8, φ2 = 0.8 bzw. 1.6 sowie vφ2 = 0.
Abb. 1.10. Der Lyapunov-Exponent als Funktion der Zeit, über die die Zeitentwicklung verfolgt wird, für reguläre (durchgezogene Kurve) und chaotische Dynamik
(gestrichelte Kurve)
1.6 Das Teilchen in der Schachtel
In diesem Beispiel wollen wir uns mit einem Teilchen beschäftigen, das sich
wie eine Billardkugel auf einer Fläche frei bewegen kann und an den Rändern
dieser Fläche reflektiert wird. Dieses an und für sich sehr einfache Beispiel
wird zu unserem Erstaunen noch eine erstaunliche Vielfalt an Phänomenen
zeigen! Als weiterführende Literatur in Bezug auf Billards ist [19] empfehlenswert.
46
1 Mechanik der Massenpunkte
Als Umrandung wählen wir zum einen ein Rechteck, in dessen Mitte ein
kreisförmiges Hindernis ist, und zum anderen ein Rechteck ohne dieses Hindernis. Da bei letzterem die Bewegung sehr viel einfacher ist, beginnen wir
mit diesem. Wir legen den Ursprung in die linke, untere Ecke des Rechtecks
und bezeichnen dessen Kantenlängen mit a und b (Abb. 1.11). Um die Bezeichnungen so einfach wie möglich zu halten, wählen wir den Zeitpunkt t = 0
so, dass sich unser Teilchen in diesem Moment irgendwo auf der Kante 2 in
Abb. 1.11 befindet.
y
b
4
3
1
2
a
x
Abb. 1.11. Rechteck-Billard
Um die Anfangsbedingungen zu komplettieren, benötigen wir noch die xKoordinate sowie die Geschwindigkeiten in x- und y-Richtung. Damit ist das
Problem vollständig beschrieben und wir müssen uns der Frage zuwenden,
welche Größen wir überhaupt berechnen wollen. Im Normalfall wären die xund y-Koordinaten als Funktion der Zeit eine gute Wahl, und wir könnten den
Zeitverlauf dieser Koordinaten wie in dem vorigen Beispiel des Fadenpendels
durch eine Aufteilung in sehr kleine Zeitschritte gewinnen (Wenn Sie Interesse
an dieser Übung haben, wollen wir Sie an dieser Stelle sogar ausdrücklich dazu
ermuntern, dann können Sie später diesen Algorithmus mit dem vergleichen,
den wir für dieses Beispiel entwickeln werden).
Die Tatsache, dass die Bewegung zwischen zwei Wandberührungen so einfach ist, dass sie analytisch beschrieben werden kann, können wir ausnützen,
um einen Algorithmus zu entwerfen, der wesentlich schneller als eine Iteration mit vielen, kleinen Zeitschritten ist. Die Idee besteht darin, den Zeitpunkt
und den Ort des nächsten Zusammenpralls mit einer Wand zu berechnen,
dann die Geschwindigkeit entsprechend des Stoßes mit der Wand zu ändern,
und diese Prozedur zu wiederholen. Man betrachtet also nur Zeitpunkte, bei
denen das Teilchen gerade eine Wand trifft, weswegen man diese Betrachtungsweise stroboskopisch nennt – ein Stroboskop ist eine Lampe, die kurze
Lichtblitze aussendet, und den Raum nur zu diesen Zeitpunkten ausleuchtet,
während er ansonsten im Dunkeln bleibt.
Um diese Vorgehensweise zu realisieren, berechnen wir zunächst, nach
welcher Zeit ∆t unser Massenpunkt mit jeder einzelnen Wand zusammenstoßen würde, wenn die anderen Wände nicht da wären. Diese Zeiten können
1.6 Das Teilchen in der Schachtel
47
auch negativ sein, dann hat der Zusammenstoß bereits stattgefunden. Außerdem wird diese Zeit für eine Wand null sein, nämlich für die Wand, an der
sich das Teilchen gerade befindet.
Beginnen wir mit dem Zusammenstoß mit der Wand 1, die durch die
Gleichung y = 0 beschrieben wird. Wenn gar keine Wand vorhanden wäre,
wäre der Ort unseres Körpers gegeben durch:
x(t) = x(t0 + ∆t1 ) = x(t0 ) + vx (t0 )∆t1
y(t) = y(t0 + ∆t1 ) = y(t0 ) + vy (t0 )∆t1 .
(1.59)
(1.60)
Hierbei soll der Index 1 bei ∆t1 anzeigen, dass dies die Zeit bis zum Zusammenstoß mit Wand 1 ist – vorausgesetzt, es kommt nicht vorher zu einer
Berührung mit einer anderen Wand. Wenn wir die erste dieser beiden Gleichungen gleich null setzen, erhalten wir eine Bestimmungsgleichung für ∆t
und daraus
x(t0 )
.
(1.61)
∆t1 = −
vx (t0 )
Entsprechend erhalten wir für die Wand mit x = a
∆t3 =
x(t0 ) − a
,
vx (t0 )
(1.62)
y(t0 )
,
vy (t0 )
(1.63)
y(t0 ) − b
vy (t0 )
(1.64)
für die Wand mit y = 0
∆t4 =
und
∆t2 =
für die Wand mit y = b.
Der kleinste positive Wert von ∆t bestimmt nun die Wand, an der die
nächste Reflexion stattfindet. Ist dies eine Wand parallel zur x-Achse (beispielsweise die Wand mit y = b), so wird bei der Reflexion die y-Komponente
der Geschwindigkeit invertiert, andernfalls die x-Komponente.
Damit haben wir alle Bestandteile, die wir zur numerischen Bearbeitung
des Problems benötigen.
1
2
3
4
5
/**************************************************************************
* Name:
box1.cpp
*
* Zweck:
Simuliert die Dynamik eines Teilchens in einer quadr. Box
*
* Methode:
stroboskopische Abbildung
*
**************************************************************************/
6
7
8
9
10
11
12
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<math.h>
48
13
1 Mechanik der Massenpunkte
#include "tools.h";
14
15
using namespace std;
16
17
//-------------------------------------------------------------------------
18
19
int main( int argc, char *argv[] )
20
21
{
22
23
24
25
26
27
//-- Definition der Variablen
int
n1, n, n_start, nmax, n_wand;
double t, tend, dt, t1, t2, t3, t4, x, y, v_x, v_y, a, b, eps;
ifstream in_stream;
ofstream out_stream;
28
29
30
31
32
33
34
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: box1 infile outfile\n";
return 0;
}
35
36
37
38
39
40
41
42
//-- Einlesen der Parameter
in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von box1.cpp\n";
inout(tend,"tend");
inout(a,"a");
inout(b,"b");
inout(n_start,"n_start");
in_stream.close();
43
44
45
46
//-- Berechnung einiger benoetigter Parameter
nmax = 1000000;
eps = 1.e-6;
47
48
49
50
//-- Schleife ueber die Anfangsbedingungen
for (n1=1; n1<=n_start; n1++)
{
51
52
53
54
55
56
57
58
59
//-- Anfangsbedingungen
in_stream >> x;
in_stream >>
in_stream >> v_x;
in_stream >>
t = 0;
dt = 0;
n_wand = 0;
out_stream << t << " " << x << "
<< v_x <<
y;
v_y;
" << y << " "
" " << v_y << "\n";
60
61
62
63
64
65
66
67
68
69
70
71
//-- Zeitschleife
for (n=1; n<=nmax; n++)
{
dt = 1.e10;
//-- Kollision mit Wand 1
t1 = - x / v_x;
if ((t1>eps) && (t1<dt)) { dt = t1; n_wand = 1; }
//-- Kollision mit Wand 2
t2 = (b-y) / v_y;
if ((t2>eps) && (t2<dt)) { dt = t2; n_wand = 2; }
//-- Kollision mit Wand 3
1.6 Das Teilchen in der Schachtel
49
t3 = (a-x) / v_x;
if ((t3>eps) && (t3<dt)) { dt = t3; n_wand = 3; }
//-- Kollision mit Wand 4
t4 = -y / v_y;
if ((t4>eps) && (t4<dt)) { dt = t4; n_wand = 4; }
//-- die neuen Werte von x, y und t
x = x + v_x*dt;
y = y + v_y*dt;
t = t+dt;
//-- Reflexion
if ((n_wand==1) || (n_wand==3))
v_x = -v_x;
else
v_y = -v_y;
//-if (t>tend) break;
out_stream << t << " " << x << " " << y << " "
<< v_x << " " << v_y << "\n";
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
}
90
91
92
}
93
94
95
out_stream.close();
}
Einige Teile dieses Programms bedürfen einer näheren Erklärung. Zum
einen ist da diese ominöse Variable eps, was für steht. Der Hintergrund ist,
dass wir bei jedem Stoß unter den Variablen t1, t2, t3 und t4 die größte
positive finden müssen – das ist dann der Zeitpunkt des nächsten Aufpralls
auf eine Umrandung der Schachtel. Dafür müssen wir jeweils einen Vergleich
mit null durchführen, und genau dieser Vergleich stellt ein Problem dar: eine
der Zeiten muss theoretisch exakt null sein, aber durch Rundungsfehler kann
sie auch einen kleinen positiven Wert annehmen. Um nun zu verhindern, dass
dadurch diese Zeit statt der nächstgrößeren gewählt wird, fordern wir statt
ti > 0 die etwas schärfere Bedingung ti > , wobei eine sehr kleine, positive
Größe ist.
Die Variable nmax gibt an, wie viele Stöße für eine Anfangsbedingung
ausgerechnet werden sollen, falls vorher nicht die Endzeit tend erreicht wird.
Dies stellt lediglich einen Mechanismus dar, um zu verhindern, dass die Rechenzeit durch ungeeignete Parameterwahl ins Unermessliche steigt. Wenn
Sie dieses Programm laufen lassen, werden Sie feststellen, dass die Dynamik
ziemlich langweilig ist. Eine typische Bahn unseres Massenpunktes finden Sie
in Abb. 1.12.
Abb. 1.12. Bewegung eines Teilchens auf einem rechteckigen Billardtisch
50
1 Mechanik der Massenpunkte
Sie sehen, dass das Teilchen nach 2 Reflektionen immer antiparallel zur
ursprünglichen Richtung läuft – eine Tatsache, die nach einigem Nachdenken
selbstverständlich ist. Aus diesem Grund wollen wir nun eine Veränderung
vornehmen, die diese Situation drastisch ändert – drastischer, als Sie es sich
wahrscheinlich im Moment vorstellen können.
Die Änderung besteht darin, dass wir in die Mitte des Rechtecks ein
kreisförmiges Hindernis stellen, oder in anderen Worten, in der Mitte einen
Teil der vorher rechteckigen Fläche des Billardtisches herausschneiden. Dadurch erhalten wir das so genannte Sinai-Billard:
Abb. 1.13. Das so genannte Sinai-Billard, bei dem sich ein Teilchen auf einer
rechteckigen Fläche bewegt, aus dem in der Mitte ein kreisförmiger Teil herausgeschnitten wurde
Zunächst vereinfachen wir die Situation, indem wir uns auf ein Viertel
des in Abb. 1.13 dargestellten Billards beschränken. Überzeugen Sie sich,
dass wir dabei die Bahnkurve qualitativ nicht ändern, sondern diese nur in
das betreffende Viertel umklappen. Zur konkreten Berechnung der Trajektorie müssen wir zunächst bestimmen, mit welcher Wand die nächste Kollision erfolgt. Das geschieht am Einfachsten, indem wir für alle Berandungen
(vier gerade Wände und der Kreisabschnitt) berechnen, wann und wo diese erreicht werden. Von diesen fünf Paaren an Zeiten und Koordinaten des
Berührungspunktes ist dann dasjenige relevant, bei dem die berechnete Zeit
den kleinsten positiven Wert annimmt. Diese Vorgehensweise bereitet keine
sonderlichen Schwierigkeiten und Sie finden das entsprechende Programm auf
der beiliegenden CD-ROM als sinai1.cpp.
Die typischen Ergebnisse sehen völlig anders aus als beim RechteckBillard. Während dort eine Bahnkurve einen sehr übersichtlichen, geordneten
Eindruck machte (Abb. 1.12) – die einzelnen Abschnitte waren immer parallel oder senkrecht zueinander – läuft die Bahn beim Sinai-Billard (Abb. 1.14)
kreuz und quer.
Dieser visuelle Eindruck legt nahe, dass die Dynamik chaotisch sein könnte – um dies jedoch zu verifizieren, stehen uns nun zwei Möglichkeiten zur
Verfügung:
–
–
Bestimmung des Poincaré-Plots
Berechnung des Lyapunov-Exponenten.
1.6 Das Teilchen in der Schachtel
51
Abb. 1.14. Typische Bahn im Sinai-Billard, wobei diese auf das rechte obere Viertel projeziert wurde
Für die Berechnung des Lyapunov-Exponenten verweisen wir auf die Übungsaufgabe 1.5. Den Poincaré-Plot hingegen werden wir hier im Text etwas näher
unter die Lupe nehmen. Dazu wiederholen wir zunächst an diesem Beispiel
die Dimensionsbetrachtung, die wir bereits in Abschn. 1.4 angestellt haben:
Der gesamte Phasenraum ist vierdimensional (zwei Orts- und zwei Impulsvariablen). Da die Energie jedoch eine Erhaltungsgröße ist – was in diesem Fall
gleichbedeutend ist mit der Tatsache, dass der Betrag der Geschwindigkeit
konstant ist, findet die Dynamik auf einer dreidimensionalen Hyperfläche im
Phasenraum statt. Indem wir uns nun auf Durchstoßpunkte durch eine weitere solche Hyperfläche, die wir frei wählen können, beschränken, verbleiben
wir mit einem Gebilde in einem zweidimensionalen Raum. Am Einfachsten
realisieren wir das, indem wir die Punkte nehmen, an denen unser Teilchen
auf eine bestimmte der fünf Berandungen trifft – diese Auftreffpunkte berechnen wir ohnehin – und x, y, vx und vy nach jedem Stoß abspeichern
(eigentlich benötigen wir nur zwei Werte, die die Lage des Auftreffpunktes
und die Richtung der Geschwindigkeit festlegen; die anderen beiden Werte
dienen lediglich der Kontrolle). Wir könnten natürlich auch die entsprechenden vier Werte vor dem Stoß nehmen, wir dürfen aber nicht vor und nach
dem Stoß mischen. Das Ergebnis (Abb. 1.15) zeigt, dass die Dynamik nicht
integrabel ist:
Abb. 1.15. Poincaré-Plot beim Sinai-Billard
52
1 Mechanik der Massenpunkte
1.7 Hamilton-Formalismus
In diesem Abschnitt wollen wir den Hamilton-Formalismus kurz skizzieren, da
wir im nächsten Abschnitt die Hamiltonschen Bewegungsgleichungen benötigen werden. Für eine ausführliche Herleitung verweisen wir auf [20] oder [21].
Wir gehen von einem System aus, dessen Dynamik wir in Form der Lagrangefunktion L(qi , q̇i ) kennen. Das impliziert, dass wir einen Satz von generalisierten Koordinaten qi haben, deren zeitlichen Ableitungen q̇i die generalisierten
Geschwindigkeiten sind. Der Übergang zur Hamiltonfunktion erfolgt durch
eine Legendre-Transformation. Die Rolle der Lagrangefunktion L übernimmt
die Hamiltonfunktion
H(qi , pi ) =
pi q̇i − L(qi , q̇i ) .
(1.65)
i
Die darin auftretenden neuen Variablen pi sind die zu qi kanonisch konjugierten Impulse:
∂L
pi =
,
(1.66)
∂ q̇i
die die qi als unabhängige Variablen ersetzen.
Die Bewegungsgleichungen sind dann die Hamiltonschen Gleichungen:
q̇i =
∂H
∂pi
ṗi = −
∂H
.
∂qi
(1.67)
Abschließend wollen wir noch erklären, welche anschauliche Bedeutung die
Hamiltonfunktion hat. Dazu betrachten wir ein System ohne explizite Zeitabhängigkeit:
∂L
=0
(1.68)
∂t
und berechnen die zeitliche Änderung der Hamiltonfunktion H:
∂H
∂H
d
∂H
q̇i +
ṗi +
H(qi , pi ) =
dt
∂q
∂p
∂t
i
i
i
i
=0.
(1.69)
(1.70)
Die Hamiltonfunktion ist also eine Erhaltungsgröße. Man kann zeigen, dass es
sich im Fall konservativer Kräfte, wenn sich also die Kräfte durch ein Potential
darstellen lassen, um die Energie handelt [22]. In diesem Fall spricht man von
skleronomen Zwangsbedingungen.
1.8 Attraktoren in dissipativen Systemen
Bislang haben wir uns ausschließlich mit sogenannten Hamiltonschen Systemen beschäftigt – darunter versteht man Systeme, bei denen der Energieerhaltungssatz gilt. Wenn man jedoch Situationen betrachtet, bei denen von
1.8 Attraktoren in dissipativen Systemen
53
außen Energie zugeführt bzw. nach außen Energie abgeführt wird, bleibt die
Gesamtenergie im System selbst nicht konstant. Eine ähnliche Situation erhalten wir, wenn zwar keine Energie zu- bzw. abgeführt wird, aber Energie in
eine nicht betrachtete Energieform umgewandelt wird. Dies ist zum Beispiel
bei einem System mit Reibung der Fall, solange wir die thermische Energie
in unserer (rein mechanischen) Betrachtung nicht berücksichtigen.
Um diese qualitativen Aussagen auf eine exakte theoretische Basis zu
stellen, betrachten wir die Zeitentwicklung von (kleinen) Phasenraumvolumina ∆V in einem 2N -dimensionalen Phasenraum der von N Orts- und N
Impulskoordinaten aufgespannt wird:
Abb. 1.16. Entwicklung eines Phasenraumelements, das durch die Punkte 0, 1
und 2 aufgespannt wird. Zur besseren Übersichtlichkeit sind nur die Ortsvektoren
des Punktes 0 beschriftet
In Abb. 1.16 sehen wir ein solches Volumen, das von den Punkten 0, 1
und 2 aufgespannt wird. Zu einem beliebigen Zeitpunkt τ sind die zugehörigen Phasenraumvektoren x0 (τ ), x1 (τ ) und x2 (τ ). Außerdem führen wir die
Differenzvektoren
∆xn = xn − x0
(1.71)
ein. Der Einfachheit halber wollen wir annehmen, dass diese Differenzvektoren an den Koordinatenachsen ausgerichtet sind:
∆xn = (0, . . . , ∆xn , . . . , 0, 0, . . . , 0 , . . . , 0)
für n ≤ N
(1.72)
∆xn = (0, . . . , 0, . . . , 0, 0, . . . , ∆pn , . . . , 0)
für n > N ,
(1.73)
so dass sich das Phasenraumvolumen ∆V einfach durch Multiplikation ergibt:
∆V =
N
∆xn ∆pn .
(1.74)
n=1
Die Zeitableitung dieses Volumenelements erhalten wir gemäß der Produktregel
54
1 Mechanik der Massenpunkte
d∆V
= ∆V
dt
N
N
1 d∆xn 1 d∆pn
+
x
dt
p
dt
n=1 n
n=1 n
.
(1.75)
Wenn die Zeitentwicklung durch die Hamiltonschen Bewegungsgleichungen
d∆xn
d∆Hn
=
dt
dpn
d∆Hn
d∆pn
=−
dt
dxn
(1.76)
mit der Abkürzung
∆Hn = H(x + ∆xn ) − H(x)
(1.77)
gegeben ist, erhalten wir für diese Volumenänderung für kleine ∆x, ∆p den
Wert null, da
dH
dH
1 dxn
1 d
1 dpn
1 d
∆xn −
∆pn = 0
+
=
xn dt
pn dt
xn dpn dxn
pn dxn dpn
(1.78)
ist. Diese Erhaltung von Phasenraumvolumina in Hamiltonschen Systemen
wird als Liouvillescher Satz bezeichnet. Nimmt die Zeitableitung von Phasenraumvolumina also einen Wert verschieden von null an, so ist die Dynamik
mit Sicherheit nicht Hamiltonsch, was nur möglich ist, wenn dem System
Energie zugeführt bzw. abgeführt wird. Ein System, bei dem diese Zeitableitung negativ ist, wird dissipativ genannt.
Wir sehen also, dass ein Phasenraumvolumen in einem dissipativen System immer weiter schrumpft, bis es im Grenzfall t → ∞ verschwindet. Im
Einfachsten Fall heißt das, dass alle Trajektorien auf einen einzigen stabilen
Punkt laufen, in dem das System dann zur Ruhe kommt. Selbstverständlich gibt es auch die Möglichkeit, dass mehrere solcher Fixpunkte existieren
– in diesem Fall hängt es von den Anfangsbedingungen ab, in welchen der
Fixpunkte die Trajektorie hineinläuft. Bei einem System, bei dem die einzige
Orts- oder Impulsvariable nicht in die Bewegungsgleichungen eingeht und der
Phasenraum daher quasi eindimensional ist, ist dies die einzige Möglichkeit.
Eine weitere Möglichkeit kommt hinzu, wenn ein zweidimensionaler Phasenraum vorliegt: der Grenzzyklus, darunter versteht man eine Trajektorie,
der sich alle benachbarten Trajektorien immer mehr annähern. Das heißt,
dass sich das System nach einem transienten Einschwingvorgang, in dem die
Trajektorie in den Grenzzyklus läuft, periodisch verhält.
Fixpunkt und Grenzzyklus sind zwei spezielle Formen von Attraktoren,
von denen es in höherdimensionalen Phasenräumen noch weitere Ausbildungen gibt. Ein Attraktor ist eine Untermenge des Phasenraumes mit folgenden
Eigenschaften:
– Der Attraktor ist bezüglich der Dynamik des Systems abgeschlossen, d.h.
startet man auf einem beliebigen Punkt des Attraktors, verläuft die gesamte Dynamik auf dem Attraktor und verlässt diesen nicht.
1.9 Das periodisch angetriebene Pendel
–
55
Startet man an einem Punkt genügend nahe am aber nicht auf dem Attraktor so nähert sich die Trajektorie für t → ∞ dem Attraktor immer
mehr an.
Insbesondere kann die Dimension eines solchen Attraktors auch nicht ganzzahlige Werte annehmen – in diesem Fall spricht man von einem seltsamen
Attraktor.
1.9 Das periodisch angetriebene Pendel
In diesem Abschnitt wollen wir ein Beispiel besprechen, das in manchen Bereichen des Phasenraums dissipativ ist, während in anderen das Phasenraumvolumen expandiert. Als Ausgangspunkt wählen wir das gedämpfte Fadenpendel mit der Bewegungsgleichung
d2
d
(1.79)
x + β x + sin x = 0 .
dt2
dt
Das Programm getr pendel1.cpp, das diese Bewegungsgleichung löst, finden Sie auf der CD-ROM. Neu gegenüber der Bewegungsgleichung (1.8) ist
lediglich der Dämpfungsterm β dx/dt, der zur Folge hat, dass Phasenraumvolumina nicht mehr erhalten bleiben, sondern immer kleiner werden. Diesen
rein dissipativen Charakter der Bewegungsgleichung ändern wir durch das
Hinzufügen einer periodischen, treibenden Kraft:
d2
d
(1.80)
x + β x + sin x = α cos(Ωt) .
dt2
dt
Durch Hinzufügen dieses explizit zeitabhängigen Terms verlassen wir zunächst
den Rahmen der zeitunabhängigen Dynamiken, auf die wir uns bisher beschränkt haben. Wir können dieses Problem jedoch elegant lösen, indem wir
den zweidimensionalen Phasenraum des ursprünglichen Problems formal um
eine Dimension erweitern. Die zusätzliche Variable nennen wir f und ihre
Bewegungsgleichung soll durch
d
f =Ω
(1.81)
dt
gegeben sein. Die Lösung dieser Differentialgleichung zur Anfangsbedingung
f (t = 0) = 0 ist trivial:
f = Ωt
(1.82)
und erlaubt die obigen Differentialgleichung (1.80) in das folgende zeitunabhängige Differentialgleichungssystem umzuwandeln:
d
x=v
dt
d
v = −βv − sin x + α cos f
dt
d
f = Ω.
dt
(1.83)
(1.84)
(1.85)
56
1 Mechanik der Massenpunkte
Beachten Sie bitte, dass sowohl x als auch f den Charakter eines Winkels
haben und daher modulo 2π zu betrachten sind. Eine direkte Konsequenz
ist, dass die zusätzlich eingeführte Variable f periodisch ist und deren Periodendauer durch T0 = 2π/Ω gegeben ist. Wenn wir später nach der Periodendauer des Gesamtsystems fragen, kommen hierfür nur Vielfache dieses
Wertes in Frage.
Da wir nun sehen, dass die Dimension des Phasenraums größer als zwei
ist und das System außerdem dissipativ ist, wissen wir aus den Überlegungen
des vorigen Abschnitts, dass mindestens ein Attraktor existieren muss, wissen
aber nicht, ob dieser vom Typ eines Fixpunkts, eines Grenzzyklus oder eines
seltsamen Attraktors ist. Die erste Möglichkeit können wir jedoch verhältnismäßig einfach ausschließen: Im Grenzpunkt müsste die Zeitableitung aller
drei Koordinaten des Phasenraums null sein – was jedoch (1.85) widerspricht.
Es bleiben die Möglichkeiten eines Grenzzyklusses und eines seltsamen Attraktors. Um dies zu entscheiden, konstruieren wir das Programm zur Lösung
der Bewegungsgleichung so, dass es eine eventuelle Periodizität erkennt und
die zugehörige Periodendauer ausgibt:
1
2
3
4
5
6
7
8
/**************************************************************************
* Name:
getr_pendel2.cpp
*
* Zweck:
Berechnet die Periodendauer eines getriebenen, gedaempften
*
*
Fadenpendels
*
* Gleichung: (dˆ2/d tˆ2) phi + beta * (d/dt) phi + sin(phi) =
*
*
alpha * cos(Omega*t)
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
9
10
11
12
13
14
15
16
17
#include
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<gsl/gsl_roots.h>
<math.h>
"tools.h"
18
19
using namespace std;
20
21
22
23
//---- globale Variablen
double alpha, beta, Omega;
double rtoleranz, atoleranz, h;
24
25
//-------------------------------------------------------------------------
26
27
int f(double t, const double x[], double dxdt[], void *params)
28
29
30
31
{
dxdt[0] = x[1];
dxdt[1] = -beta*x[1]-sin(x[0])+alpha*cos(Omega*t);
32
33
34
return GSL_SUCCESS;
}
35
36
//-------------------------------------------------------------------------
1.9 Das periodisch angetriebene Pendel
57
37
38
39
int jac(double t, const double x[], double *dfdx, double dfdt[],
void *params)
40
41
42
43
{
return GSL_SUCCESS;
}
44
45
//-------------------------------------------------------------------------
46
47
int check_period(double x0[],double x1[], double eps, int *n)
48
49
50
{
int n1, n2, n_min, found;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
found = 0;
n_min = 1000;
for (n1=0; n1<=*n; n1++)
{
for (n2=1; n2<=*n-n1; n2++)
{
if ((fabs(x0[n1]-x0[n1+n2])<eps) && (fabs(x1[n1]-x1[n1+n2])<eps)
&& (n2<n_min))
{n_min = n2; found = 1; break;}
}
if (found == 1) break;
}
*n = n_min;
65
66
67
return found;
}
68
69
//-------------------------------------------------------------------------
70
71
int main( int argc, char *argv[] )
72
73
{
74
75
76
77
78
79
80
81
82
//-- Definition der Variablen
int
n, n1, n_alpha, n_alpha_max, status, nmax, found, n_assumed;
double t, tend, dt, t_start, T_0, T_period;
double x[2], x_start[2], delta[2], x0[10000], x1[10000];
double alpha_min, alpha_max, dalpha, eps;
double mu = 10;
ifstream in_stream;
ofstream out_stream;
83
84
85
86
87
88
89
//-- Fehlermeldung, wenn Input- oder Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: getr_pendel2 infile outfile\n";
exit(0);
}
90
91
92
93
94
95
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von getr_pendel2.cpp\n";
inout(dt,"dt");
inout(nmax,"nmax");
58
96
97
98
99
100
101
1 Mechanik der Massenpunkte
inout(alpha_min,"alpha_min");
inout(n_alpha_max,"n_alpha_max");
inout(Omega,"Omega");
inout(rtoleranz,"rtoleranz");
inout(x_start[1],"x_start[1]");
in_stream.close();
inout(alpha_max,"alpha_max");
inout(beta,"beta");
inout(atoleranz,"atoleranz");
inout(x_start[0],"x_start[0]");
102
103
104
105
106
107
//-- Berechnung einiger benoetigter Parameter -eps = 1.e-7;
h = 1.e-6;
dalpha = (alpha_max-alpha_min) / n_alpha_max;
T_0 = 2 * pi / Omega;
108
109
110
111
112
113
114
out_stream.precision(7);
//-- Schleife ueber alpha
for (n_alpha=0; n_alpha<=n_alpha_max; n_alpha++)
{
alpha = alpha_min + n_alpha * dalpha;
found = 0;
115
116
117
118
119
//-- Anfangsbedingungen
t = 0;
x[0] = x_start[0];
x[1] = x_start[1];
120
121
122
123
124
125
126
//-- Initialisierung der GSL-Routine
const gsl_odeiv_step_type *T = gsl_odeiv_step_rk8pd;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, 2);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atoleranz, rtoleranz);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(2);
gsl_odeiv_system sys = {f, jac, 2, &mu};
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
for (n=1; n<=nmax; n++)
{
//-- Zeitentwicklung bis zum Ende der n-ten Periode
tend = n*T_0;
while (t<tend)
{
status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, tend, &h, x);
if (status != GSL_SUCCESS) break;
}
n1 = int(fabs(x[0] / 2 / pi) + 0.5);
x[0] = x[0] - sign(x[0]) * 2*n1*pi;
x0[n] = x[0];
x1[n] = x[1];
for (n1=n-1; n1>0; n1--)
{
delta[0] = x[0] - x0[n1];
delta[1] = x[1] - x1[n1];
if (fabs(delta[0]) > pi-eps) delta[0] = fabs(delta[0]) - pi;
if ((fabs(delta[0])<eps) && (fabs(delta[1])<eps))
{found=1; break;}
}
if (found==1) break;
}
150
151
152
153
154
n_assumed = n-n1;
if ((found==1) && (n_assumed<=64))
{
x0[0] = x[0];
x1[0] = x[1];
1.9 Das periodisch angetriebene Pendel
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
59
t_start = t;
for (n=1; n<=5*n_assumed; n++)
{
tend = t_start + n*T_0;
while (t<tend)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t,
tend, &h, x);
if (status != GSL_SUCCESS) break;
}
x0[n] = x[0];
x1[n] = x[1];
}
n--;
if ( check_period(x0, x1, 5*eps, &n) )
{
T_period = n*T_0;
out_stream << alpha << " " << T_period << " " << 1/T_period << "\n";
}
}
174
175
176
177
178
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
}
179
180
181
out_stream.close();
}
In den Zeilen 25 bis 35 finden wir die Funktion f, die zur Lösung der
Bewegungsgleichung benötigt wird, wobei wir auf die einfachere Form eines zweidimensionalen Phasenraumes zurückgreifen und dafür die explizite
Zeitabhängigkeit der Differentialgleichung in Kauf nehmen. Nach dem Einlesen der Parameter und der Berechnung einiger benötigter Hilfsvariablen
finden wir in den Zeilen 111 bis 178 eine Schleife, mit der wir einen der
Parameter (hier α) variieren können.
Für jeden Wert von α bestimmen wir die Lösung der Bewegungsgleichung
zu den Zeitpunkten, die Vielfache von T0 sind, und speichern die Werte von
x und v in x0[n] und x1[n]. Nach jedem solchem Schritt vergleichen wir
in den Zeilen 140 bis 147 diese neuen Werte mit denen zu den vorangegangen Zeitpunkten und entscheiden so, ob wahrscheinlich Periodizität vorliegt
und – wenn dies erfüllt ist – welche Periodendauer wir vermuten. Die so ermittelte Periodendauer kann jedoch noch falsch sein – insbesondere könnte
die tatsächliche Periodendauer ein Bruchteil des ermittelten Wertes sein, wie
wir uns an dem folgenden Beispiel klarmachen können: nehmen wir an, die
gesuchte Periodizität ist T0 . Wenn wir nun die Dynamik an einem beliebigen Punkt starten, nähert sich die Trajektorie dem gesuchten Grenzzyklus
und nähert sich ihm irgendwann soweit, dass die Prüfung in Zeile 145 zu
dem Schluss kommt, dass Periodizität vorliegt. Dies kann bedeuten, dass die
Werte von x und v ihren jeweiligen Vorgängern so nahe kommen, dass der
Abstand unter den Schwellwert fällt – es kann aber auch sein, dass dieser
Abstand geringfügig größer ist, aber der Abstand zum Zustand vor 2T0 klein
genug war. In diesem Fall scheint die Periode – fälschlicherweise – 2T0 sein.
60
1 Mechanik der Massenpunkte
Dieser erste Schritt garantiert also nur, dass wir uns dem Grenzzyklus hinreichend genähert haben und wahrscheinlich keine Periodendauer größer als
der gefundene Wert vorliegt.
Wir müssen also an diesen ersten Schritt noch eine Prüfung anschließen,
welche Periodendauer die wahrscheinlich richtige ist. Zu diesem Zweck verfolgen wir die Trajektorie weiter – in unserem Fall bis zum fünffachen der
vermuteten Periodendauer und ermitteln aus den gefundenen Werten von x
und v die Periodendauer T . Diesen letzten Schritt übernimmt das Unterprogramm check period in den Zeilen 47 bis 67. Schließlich schreiben wir,
wenn wir für einen Wert von α eine Periodendauer gefunden haben, diese
sowie ihren Kehrwert in Zeile 171 in die Ausgabedatei.
Nach so vielen Details zum Programm getr pendel2.cpp können wir uns
nun den so gewonnenen Ergebnissen widmen:
Abb. 1.17. Periodenverdopplung beim getriebenen gedämpften Pendel. Die Stärke
α der treibenden Kraft wird variiert, während die Parameter Ω = 2/3 und β = 0.5
konstant gehalten werden
Abbildung 1.17 zeigt die gefundene Periodendauer sowie ihren Kehrwert
als Funktion von α, wobei die anderen beiden Parameter Ω und β festgehalten
wurden. Bis zu α ≈ 1.0625 liegt die für Ω = 2/3 kürzestmögliche Periodendauer 2π/ω ≈ 9.425 vor. Dann springt die Periodendauer plötzlich auf den
doppelten Wert, auf dem sie bis etwa α ≈ 1.078 bleibt. Bei diesem Wert
verdoppelt sich die Periodendauer ein weiteres Mal, um sich dann bereits
bei α ≈ 1.082 wieder zu verdoppeln. Diese Periodenverdopplungen treten
dann immer wieder auf und folgen zunehmend dichter aufeinander. Schließlich wird die Periodendauer unendlich – das heißt mit anderen Worten, dass
die Bewegung nicht mehr periodisch ist.
Wie Sie sehen, ist eine einigermaßen zuverlässige Bestimmung der Periodendauer gar nicht so einfach, wie man dies vielleicht zunächst vermuten
sollte. Es stellt sich also die Frage, ob man auf einfacherem Weg die selbe Information über das physikalische Verhalten hätte bekommen können.
Diese Frage führt uns zum sogenannten Attraktordiagramm. Für dieses sammeln wir für jeden Wert des Kontrollparameters α die Werte einer beliebigen
dynamischen Variablen zu den Zeiten nT0 , (n + 1)T0 , . . . (n + N )T0 . Dabei
wählen wir die Startzeit nT0 groß genug um sicherzugehen, dass sich unsere
1.10 Der schiefe Wurf mit Luftwiderstand
61
Trajektorie nahe genug an den Attraktor angenähert hat. Wenn wir nun all
diese Werte der dynamischen Variable in Abhängigkeit des Kontrollparameters (hier α) auftragen, erhalten wir das Attraktordiagramm in Abb. 1.18
(das Programm sollte keinerlei Probleme darstellen und Sie finden es auf der
CD als getr pendel3.cpp).
Abb. 1.18. Attraktordiagramm des getriebenen gedämpften Pendels. Auch in diesem Diagramm sehen wir die Kaskade der Periodenverdopplungen bis hin zum Chaos. Der Parameter α wird variiert, während die Parameter Ω = 2/3 und β = 0.5
konstant gehalten werden
Vergleichen Sie dieses Diagramm mit Abb. 1.17 so sehen Sie, dass sich die
erste Periodenverdopplung im Attraktordiagramm durch eine Gabelung, eine
Bifurkation manifestiert. Bei der zweiten Periodenverdopplung gabeln sich
beide so entstandenen Linien noch einmal und so weiter bis im chaotischen
Bereich keine einzelnen Linien mehr ausgemacht werden können, sondern ein
Band entsteht.
Mit dem Wissen aus den Abbildungen 1.17 und 1.18 können wir nun einige
interessante Werte von α auswählen und die Trajektorien des Grenzzyklus für
diese Werte berechnen. An dieser Stelle nun kommen wir auf das Programm
getr pendel1.cpp zurück. Die Werte von Ω = 2/3 und β = 0.5 übernehmen
wir und wählen für α die Werte 1.06 (vor der ersten Periodenverdopplung),
1.075 (nach der ersten Periodenverdopplung), 1.082 (nach der zweiten Periodenverdopplung) und 1.085 (chaotischer Bereich). Die Ergebnisse sehen Sie
in Abb. 1.19.
1.10 Der schiefe Wurf mit Luftwiderstand
Beim schiefen Wurf handelt es sich um die Bewegung eines Körpers im –
homogen genäherten – Schwerefeld der Erde. Unter Vernachlässigung der
Luftreibung sind die Bewegungsgleichungen schnell aufgestellt: in x-Richtung
haben wir eine freie Bewegung
d2
x(t) = 0
dt2
(1.86)
62
1 Mechanik der Massenpunkte
Abb. 1.19. Trajektorien des getriebenen gedämpften Pendels für einige ausgewählte Werte von α für feste Parameter Ω = 2/3 und β = 0.5. Die Werte von α sind
1.06 (a), 1.075 (b), 1.082 (c) und 1.085 (d). Die Periodenverdopplung von (a) nach
(b) manifestiert sich in dieser Darstellung in einer Aufspaltung der Trajektorie
von einer Einfachschleife in eine Doppelschleife. Entsprechend erhalten wir nach
einer weiteren Periodenverdopplung (c) eine Vierfachschleife. Im chaotischen Fall
(d) schließlich ist die Trajektorie flächenfüllend
und in z-Richtung den freien Fall
d2
z(t) = −g .
dt2
(1.87)
Hierbei haben wir unser Koordinatensystem so gewählt, dass in y-Richtung
keine Bewegung stattfindet, so dass wir diese Koordinate nicht zu berücksichtigen brauchen.
Diese Bewegungsgleichungen sind einfach zu lösen:
x(t) = x0 + vx0 t
(1.88)
1
z(t) = z0 + vz0 t − gt2 .
2
(1.89)
Nun müssen wir in die Bewegungsgleichungen (1.86) und (1.87) die
Luftreibung einbauen, von der wir annehmen wollen, dass sie proportional
zum Quadrat der Geschwindigkeit des Körpers ist (eine Annahme, die für
die nicht übermäßig schnelle Bewegung in Gasen sehr gut erfüllt ist). Diese
Reibungskraft ist natürlich der Geschwindigkeit entgegengerichtet, so dass
wir als modifizierte Bewegungsgleichungen
d2
x(t) = −γ vx2 (t) + vz2 (t)vx
2
dt
d2
z(t) = −g − γ vx2 (t) + vz2 (t)vz
2
dt
(1.90)
(1.91)
1.10 Der schiefe Wurf mit Luftwiderstand
63
erhalten. Wie sie sehen, koppelt der Luftwiderstand die bisher unabhängigen
Bewegungsgleichungen von x(t) und z(t).
Am Einfachsten bekommen wir das benötigte Programm, indem wir unser
bestehendes Programm faden1.cpp nach wurf.cpp kopieren und dann die
nötigen Änderungen vornehmen. So bekommen wir
1
2
3
4
5
6
7
/**************************************************************************
* Name:
wurf.cpp
*
* Zweck:
Simuliert den schiefen Wurf mit Luftwiderstand
*
* Gleichung: (dˆ2/d tˆ2) x = - eta sqrt(v_xˆ2+v_zˆ2) v_x
*
*
(dˆ2/d tˆ2) z = -g - eta sqrt(v_xˆ2+v_zˆ2) v_x
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
8
9
10
11
12
13
14
15
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<math.h>
"tools.h"
16
17
using namespace std;
18
19
20
//---- globale Variablen
double g, eta;
21
22
//-------------------------------------------------------------------------
23
24
int f(double t, const double x[], double dxdt[], void *params)
25
26
27
28
29
30
{
dxdt[0]
dxdt[1]
dxdt[2]
dxdt[3]
= x[2];
= x[3];
=
- eta * sqrt(x[2]*x[2]+x[3]*x[3]) * x[2];
= - g - eta * sqrt(x[2]*x[2]+x[3]*x[3]) * x[3];
31
32
33
return GSL_SUCCESS;
}
34
35
//-------------------------------------------------------------------------
36
37
38
int jac(double t, const double x[], double *dfdx, double dxdt[],
void *params)
39
40
41
42
{
return GSL_SUCCESS;
}
43
44
//-------------------------------------------------------------------------
45
46
main( int argc, char *argv[] )
47
48
{
49
50
51
52
//-- Definition der Variablen
int
n, n1, n_start, n_out, max_fct, iaux, error, normal;
double t, tend, dt, t1, t2, rtol, atol, h, x[4];
64
53
54
55
1 Mechanik der Massenpunkte
double mu = 10;
ifstream in_stream;
ofstream out_stream;
56
57
58
59
60
61
62
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: wurf infile outfile\n";
exit(1);
}
63
64
65
66
67
68
69
70
71
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von wurf1.cpp\n";
inout(tend,"tend");
inout(n_out,"n_out");
inout(g,"g");
inout(eta,"eta");
inout(rtol,"rtol");
inout(atol,"atol");
inout(n_start,"n_start");
72
73
74
75
76
77
//-- Berechnung einiger benoetigter Parameter -dt = tend / n_out;
rtol = 1.e-8;
atol = 1.e-8;
h = 1.e-6;
78
79
80
81
//-- Schleife ueber die Anfangsbedingungen
for (n1=1; n1<=n_start; n1++)
{
82
83
84
85
86
//-- Anfangsbedingungen
in_stream >> x[0];
in_stream >> x[1];
in_stream >> x[2];
in_stream >> x[3];
t = 0;
87
88
89
out_stream << t << " " << x[0] << " " << x[1]
<< " " << x[2] << " " << x[3] << "\n";
90
91
92
93
94
95
96
//-- Initialisierung der GSL-Routine
const gsl_odeiv_step_type *T = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, 4);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(4);
gsl_odeiv_system sys = {f, jac, 4, &mu};
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//-- Zeitschleife -t = 0;
for (n=1; n<=n_out; n++)
{
t1 = n * dt;
while (t<t1)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t1, &h, x);
if (status != GSL_SUCCESS) break;
}
out_stream << t << " " << x[0] << " " << x[1]
<< " " << x[2] << " " << x[3] << "\n";
}
gsl_odeiv_evolve_free(e);
1.10 Der schiefe Wurf mit Luftwiderstand
112
113
114
65
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
}
115
116
117
118
out_stream.close();
in_stream.close();
}
Zum Überprüfen können wir zwei Grenzfälle betrachten: zum einen den
schiefen Wurf ohne Luftreibung, zum anderen die vertikale Bewegung des
Körpers (vx (t) = 0) mit Luftreibung, aber ohne Schwerefeld. Auf diese Weise
testen wir sowohl die richtige Implementation des Schwerefeldes als auch der
Luftreibung. Für den freien Fall haben wir die Lösung bereits angegeben, es
fehlt uns also nur noch der Fall der eindimensionalen Bewegung mit vx (t) = 0.
Die Differentialgleichung für die Geschwindigkeit vz (t) ist
d
vz (t) = −γ|vz (t)|vz (t) .
dt
(1.92)
Ohne Beschränkung der Allgemeinheit können wir vz (0) positiv wählen. Unsere Anschauung sagt uns, dass in diesem Fall die Geschwindigkeit allein
durch Reibung nie negative Werte annimmt (eine Tatsache, die wir anschließend am Ergebnis verifizieren können), so dass wir die einfachere Differentialgleichung
d
vz (t) = −γvz2 (t) .
(1.93)
dt
zu lösen haben. Diese Differentialgleichung ist separierbar, d.h. wir können
sie in die folgende Form bringen:
−
1
dvz = dt .
γvz2
Integration auf beiden Seiten führt auf die Gleichung
1
1
1
−
=t.
3γ vz (t)3
vz (0)3
(1.94)
(1.95)
Diese Gleichung können wir ohne Problem nach vz (t) auflösen und erhalten
1/3
vz (t) = vz (0)3 − 3γt
.
(1.96)
Der letzte Punkt schließlich ist die Visualisierung der Ergebnisse, wobei es dabei darauf ankommt, unter welchem Aspekt man den schiefen Wurf
betrachten möchte. Sehr lehrreich sind die Trajektorien bei gleichen Anfangsbedingungen aber verschiedenen Werten des Luftwiderstandskoeffizienten γ,
wie in Abb. 1.20 dargestellt.
In allen drei dargestellten Fällen beginnt das Teilchen seine Bahn im
Ursprung. Seine Geschwindigkeit ist sehr hoch (etwa 69 m/s oder knapp 250
66
1 Mechanik der Massenpunkte
Abb. 1.20. Bahnkurve beim schiefen Wurf für verschiedene Luftwiderstandswerte,
wobei alle übrigen Parameter – Abwurfwinkel und -geschwindigkeit festgehalten
wurden
km/h und bildet einen Winkel von 45 Grad zum Horizont. Die Werte für den
Luftwiderstand γ waren null (durchgezogene Kurve), 10−4 1/m (gestrichelte
Linie), 10−3 1/m (gepunktet-gestrichelte Linie) sowie 10−2 1/m (gepunktete
Linie). Vergleichen Sie die verschiedenen Flugbahnen qualitativ mit Ihren
Erfahrungen: wie fliegt ein Wattebausch, wie fliegt ein Stein?
Übungen
1.1 Numerische Lösung von Differentialgleichungen
Lösen Sie direkt durch Anwendung der Entwicklung (1.14), (1.15), d.h. ohne
Verwendung einer Numerik-Bibliothek, das Kepler-Problem mit N Teilchen,
wobei Sie sowohl die Ordnung m der Entwicklung als auch die Zahl N der
beteiligten Körper variieren. Was stellen Sie insbesondere für große N und
große m fest?
1.2 Dreieckspotential
Lösen Sie die Bewegungsgleichung eines Teilchens im Potential
(x − [x])
für [x] gerade
V (x) = V0 ×
.
(1 − x + [x]) für [x] ungerade
(1.97)
Dabei ist die Gaußklammer [x] definiert als die größte ganze Zahl, die kleiner
gleich x ist, also z.B.
[2] = 2
[1.2] = 1
(1.98)
(1.99)
[−1.2] = −2 .
(1.100)
Übungen
67
Dieses Potential hat die folgende Form:
Abb. 1.21. Dreieckspotential
Versuchen Sie für die Lösung der Bewegungsgleichung verschiedene Bibliotheksroutinen, insbesondere solche, die auf Verfahren niedriger bzw. höherer
Ordnung beruhen.
1.3 Stadionbillard
Wir betrachten ein Teilchen in einer stadionförmigen Umrandung (Abb. 1.22):
Abb. 1.22. Stadionbillard
Schreiben Sie ein Programm, das – analog zum Programm box1.cpp –
die Bewegung eines Massenpunkts in diesem sogenannten Stadionbillard simuliert. Ausgehend von diesem Programm können Sie nun einen PoincarèSchnitt oder ein Attraktordiagramm anfertigen und dann für ausgewählte
Parameter den Lyapunov-Exponenten berechnen.
1.4 Dreikörperproblem
Die Entdeckung des klassischen Chaos durch Poincaré geschah im Rahmen
eines Wettbewerbs zur Berechenbarkeit von Planetenbewegungen. Im Rahmen seiner Abhandlung konnte Poincaré zeigen, dass bereits bei drei Köpern
unter dem Einfluss ihrer gegenseitigen Anziehung eine Voraussage über lange
Zeiten nicht möglich ist. Um eine solche Situation zu untersuchen, wählen
68
1 Mechanik der Massenpunkte
Sie bitte drei gleiche Massen m an den Ecken eines gleichschenkligen Dreiecks und spielen verschiedene Anfangsgeschwindigkeiten durch – von Interesse
sind hierbei allerdings nur Situationen, bei denen alle drei Massen gebunden bleiben. Da der Phasenraum 18-dimensional ist (für jeden Massenpunkt
drei Orts- und drei Geschwindigkeitskoordinaten) und nach Festlegung des
Schwerpunkts und seines Impulses davon immer noch 12 Dimensionen übrig
bleiben, ist ein Poincarè-Plot nicht ohne weiteres möglich. Zur Entscheidung,
ob chaotisches Verhalten vorliegt oder nicht, ziehen Sie deshalb bitte den
Lyapunov-Exponenten heran.
1.5 Lyapunov-Exponent beim Sinai-Billard
Berechnen Sie den Lyapunov-Exponent des in Abschn. 1.6 vorgestellten SinaiBillards. Betrachten Sie dazu zwei Bahnen, die an der selben Wand entweder parallel und nahe beeinander oder am selben Punkt mit etwas unterschiedlicher Richtung beginnen. Versuchen Sie zunächst eine Bestimmung des
Lyapunov-Exponenten ohne der im Text beschriebenen sukzessiven Rückskalierung. Welche tend sind bei dieser Vorgehensweise noch sinnvoll möglich?
Und wie ändert sich das, wenn Sie in bestimmten Zeitabständen den Abstand
auf einen festen Wert skalieren?
1.6 Flug einer Diskusscheibe
Das Programm wurf1.cpp aus Abschn. 1.10 kann verhältnismässig einfach
so modifiziert werden, dass die wesentlichen Punkte beim Flug einer Diskusscheibe in einem einfachen Modell erfasst werden. Gegenüber dem schiefen
Wurf eines ’normalen’ Körpers kommt bei der Diskusscheibe eine Auftriebskraft proportional zur Relativgeschwindigkeit Diskus–Luft hinzu:
F Auftr = α(vx,Diskus − vx,Luft )ez .
(1.101)
Hierbei sind vx,Diskus bzw. vx,Luft die x-Komponente der Geschwindigkeit des
Diskus bzw. der Luft und ez ist der Einheitsvektor in z-Richtung. Der Einfachheit halber haben wir angenommen, daß der Diskus während des Fluges
eben liegt – in Wirklichkeit steht der Diskus üblicherweise leicht schräg in
der Luft, wobei diese Neigung aufgrund der schnellen Rotation während des
Fluges annähernd konstant ist. Der Wert der Konstanten α hängt von der
Form des Diskus ab; ein guter Diskus sollte ein möglichst großes α aufweisen,
da dies viel Auftrieb und damit eine große Flugweite bedeutet.
Beachten Sie, dass die Einbeziehung von Luftbewegung auch eine Modifikation beim Reibungsterm erfordert.
Im ersten Schritt müssen Sie den Wert von α abschätzen – überlegen Sie
sich dazu einen realistischen Wert der Anfangsgeschwindigkeit und ermitteln
Sie für verschiedene Werte von α die maximalen Wurfweiten bei Windstille
(vx,Luft = 0). Im Folgenden verwenden Sie das α, bei dem die so ermittelte
Übungen
69
maximale Wurfweite mit dem Weltklasseergebnis von 70 m übereinstimmt.
Nun können Sie durch Variation von vx,Luft studieren, inwieweit ein günstiger
Wind noch eine Verbesserung erlaubt. Ist eher Rückenwind oder Gegenwind
günstig?
2 Elektrodynamik
2.1 Die Maxwellschen Gleichungen
Nach dem Studium des letzten Kapitels beherrschen wir mechanische Systeme, d.h. Systeme, die durch die Angabe von Orten und Impulsen vollständig
beschrieben werden. Noch Anfang des letzten Jahrhunderts nahm man an,
dass dies – zumindest im Prinzip – bei allen physikalischen Fragestellungen
der Fall sei. Dieses mechanistische Weltbild, das zur Erklärung elektrischer
und magnetischer Phänomene den Äther benötigte, wurde durch die Aufstellung der speziellen Relativitätstheorie [23] zerstört. In Einsteins Weltbild
ist eine mechanische Interpretation des elektrischen und magnetischen Feldes nicht mehr möglich, und die Bewegungsgleichungen dieser Felder können
nicht als eine Folge der Newtonschen Axiome betrachtet werden. Vielmehr
nehmen diese Bewegungsgleichungen, die Maxwellschen Gleichungen, denselben Status ein, wie die Newtonschen Axiome selbst.
Im Rahmen dieses Buches ist die Elektrodynamik die erste Feldtheorie,
die wir kennen lernen, d.h. die dynamischen Variablen sind nicht mehr skalare oder vektorielle Größen, sondern sind Funktionen von Ort und Zeit. Das
hat zur Folge, dass die Bewegungsgleichungen keine gewöhnlichen Differentialgleichungen, sondern partielle Differentialgleichungen sind:
div B = 0
div D = ∂D
=j
∂t
∂B
rotE +
= 0,
∂t
rotH −
(2.1)
(2.2)
wobei ρ die Ladungsdichte und j die Stromdichte sind.
Es wird Sie vielleicht verwundern, dass statt zwei Feldern (dem elektrischen und dem magnetischen) in den Maxwellschen Gleichungen vier Felder
eingehen. Der Grund ist praktischer Natur: auf diese Weise drückt man sich
quasi – zumindest für’s erste – um die Frage, wie stark das elektrische und
magnetische Feld innerhalb von Materie ist, wenn man die Felder außerhalb
kennt. Bis auf historisch durch das Einheitensystem bedingte Vorfaktoren ist
nämlich D nichts anderes als das elektrische Feld E innerhalb von Materie
und entsprechend verhält es sich mit B und H. Letztendlich kann man sich
dieser Problematik aber nicht entziehen, so dass die Maxwell-Gleichungen
72
2 Elektrodynamik
alleine noch unvollständig sind und erst durch – Im allgemeinen empirische
– Beziehungen zwischen D und E sowie zwischen B und H ergänzt werden
müssen.
Diese Beziehungen können außerordentlich kompliziert sein und sogar von
der Vorgeschichte abhängen (Hysterese), in vielen Fällen wird die Wirklichkeit aber durch einen linearen Ansatz hinreichend genau beschrieben:
D = ε0 ε r E
B = µ0 µr H .
(2.3)
(2.4)
Dabei sind ε0 und µ0 die oben erwähnten historischen Faktoren (sie heißen
Dielektrizitäts- und Permeabilitätskonstante des Vakuums), während εr und
µr die Dielektrizitäts- und Permeabilitätskonstante des jeweiligen Mediums
sind. Im Vakuum nehmen die letzteren beiden Konstanten den Wert eins an.
Transparenter wird die Unterscheidung von E und D einerseits und B
und H andererseits durch die Aufspaltung
D = ε0 E + P
(2.5)
B = µ0 (H + M ) .
(2.6)
wobei P die Polarisation und M die Magnetisierung des Mediums ist.
2.2 Felder von Punktladungen
Die Maxwellschen Gleichungen sind in Ihrer vollen Allgemeinheit außerordentlich kompliziert – was Sie unter anderem daran ermessen können, welche
Vielzahl von Phänomenen grundsätzlich auf dieses System partieller Differentialgleichungen zurückgeführt werden kann. Man beschränkt sich daher in
den Vorlesungen darauf, mehr oder weniger eingeschränkte Spezialfälle des
allgemeinen Problems zu betrachten. Der erste Spezialfall, den wir nun eingehender untersuchen wollen, ist der eines Systems von sich nicht bewegenden
Punktladungen qn an den Orten xn im Vakuum:
qn δ(x − xn )
(2.7)
(x, t) =
n
j(x, t) = 0 .
(2.8)
In diesem Fall vereinfachen sich die Maxwellgleichungen zu
div B =
div E =
0
1
ε0
n qn δ(x
1
µ0 rotB
− xn )
=0
rotE = 0 ,
(2.9)
(2.10)
wobei wir bereits alle Terme mit einer Zeitableitung weggelassen haben, da
die Lösung dieses statischen Problems keine zeitabhängigen Anteile enthalten
kann.
2.3 Multipole
73
Die Lösung dieser Differentialgleichungen lässt sich direkt angeben:
E(x, t) =
qn
1 (x − xn )
4πε0 n |x − xn |3
B(x, t) = 0 .
(2.11)
(2.12)
Die Lösung für das elektrische Feld sollte uns sofort an das Newtonsche
Gravitationsgesetz (1.2) erinnern, das genau die selbe Form und die selbe
Abstandsabhängigkeit besitzt.
2.3 Multipole
Auch wenn das Newtonsche Gravitationsgesetz, das die Anziehung zwischen
Massen beschreibt, und das Grundgesetz der Elektrostatik, das die Anziehung
und Abstoßung von Ladungen wiedergibt, mathematisch völlig äquivalent
sind, gibt es doch einen entscheidenden physikalischen Unterschied: während
Massen immer positiv sind, sich diese also immer anziehen, können Ladungen
sowohl positiv als auch negativ sein. Eine direkte Konsequenz ist, dass das
Feld einer positiven Punktladung durch eine negative Ladung in unmittelbarer Nähe nahezu aufgehoben werden kann — eine Möglichkeit, die beim
Gravitationsgesetz natürlich nicht gegeben ist. Auf diese Weise erhalten wir
einen so genannten Dipol, dessen Fernfeld nur vom Quotienten q/∆ abhängt
(q bzw. −q sind die beiden Punktladungen und ∆ ist deren Abstand) und
kubisch mit dem Abstand zum Dipol abnimmt (anstatt quadratisch wie bei
einer Einzelladung, dem Monopol). Durch Hinzufügen eines zweiten Dipols,
dessen Ladungen gegenüber dem ersten wieder vertauscht sind, erhalten wir
einen Quadrupol, dessen Feld nun quartisch mit zunehmenden Abstand abnimmt usw.
In diesem Abschnitt wollen wir die Felder dieser Multipole berechnen und
die bereits erwähnte Abhängigkeit der Feldstärke vom Abstand zum Multipol bestätigen. Das Programm multipol.cpp konstruiert im ersten Schritt
(Zeile 60–71) einen Multipol vorgegebener Ordnung. Dabei wird ausgenutzt,
dass man aus zwei gegeneinander versetzten Ladungsanordnungen, die jeweils
einen Multipol n-ter Ordnung darstellen und bei denen beim einen Multipol
die Ladungen gegenüber dem anderen alle invertiert sind, zusammen einen
Multipol (n+1)-ter Ordnung erhält. Anschließend (Zeile 73–86) wird die elektrische Feldstärke entlang einer Geraden, deren Richtung durch die Variable
alpha festgelegt wird, berechnet und ausgegeben.
1
2
3
4
5
/**************************************************************************
* Name:
multipol.cpp
*
* Zweck:
Berechnet eletrostatische Multipol-Felder
*
* Gleichung: E(x) = sum_n q_n (x-x_n) / (x-x_n)ˆ3
*
**************************************************************************/
6
7
#include <iostream>
74
8
9
10
2 Elektrodynamik
#include <fstream>
#include <math.h>
#include "tools.h"
11
12
using namespace std;
13
14
main( int argc, char *argv[] )
15
16
17
18
19
20
21
22
23
24
{
//-- Definition der Variablen
const int ord_max = 10;
const int xmax = 1024;
int
n, n_left, ord, nr, nr_max, npos, npos1, n_ord;
double
x_multipol[xmax], q_multipol[xmax], alpha, x, y, r, dr, rmax;
double
E, E_x, E_y, delta, distance;
ifstream in_stream;
ofstream out_stream;
25
26
27
28
//-- Initialisierung
for (n=0; n<xmax; n++) x_multipol[n] = 0;
for (n=0; n<xmax; n++) q_multipol[n] = 0;
29
30
31
32
33
34
35
//-- Fehlermeldung, wenn Input- oder Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: multipol infile outfile\n";
return 0;
}
36
37
38
39
40
41
42
43
44
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ausgabe-File generiert von multipol.cpp \n";
inout(ord,"ord");
inout(alpha,"alpha");
inout(rmax,"rmax");
inout(nr_max,"nr_max");
inout(delta,"delta");
in_stream.close();
45
46
47
//-- Berechnung einiger benoetigter Parameter -dr = rmax / nr_max;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//-- Generation des Multipols aus Einzelladungen
if (ord > ord_max)
{
cout << " Maximal zulaessige Ordnung ueberschritten!\n";
return 0;
}
if (ord < 1)
{
cout << " Nur positive Ordnungen sind physikalisch sinnvoll!\n";
return 0;
}
n_left = -pow(2,ord-1);
x_multipol[0] = n_left * delta;
q_multipol[0] = 1;
npos = 0;
for (n_ord=1; n_ord<=ord; n_ord++)
for (n=1; n<=pow(2,n_ord-1); n++)
{
2.3 Multipole
67
68
69
70
71
75
npos++;
npos1 = npos - pow(2,n_ord-1);
x_multipol[npos] = (npos+n_left) * delta;
q_multipol[npos] = -q_multipol[npos1];
}
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//-- Berechnung der elektrischen Feldstaerke entlang einer Linie
for (nr=1; nr<=nr_max; nr++)
{
r = nr*dr;
x = r * cos(alpha);
y = r * sin(alpha);
E_x = 0; E_y = 0;
for (n=0; n<=npos; n++)
{
distance = sqr(x-x_multipol[n]) + sqr(y);
E_x = E_x + q_multipol[n] / pow(distance,1.5) * ( x-x_multipol[n]);
E_y = E_y + q_multipol[n] / pow(distance,1.5) * y;
}
E = sqrt( sqr(E_x)+sqr(E_y) );
out_stream << r << " " << log(r) / log(10) << " "
<< E << " " << log(fabs(E)) / log(10) << "\n";
}
out_stream.close();
}
Das Ergebnis dieses Programms für Ordnungen bis n ord = 10 zeigt das
folgende Diagramm:
Abb. 2.1. Abhängigkeit der elektrischen Feldstärke vom Abstand bei Multipolen
1. (oberste Linie) bis 10. Ordnung (unterste Linie)
Da die Multipolfelder als kleine Größen durch Differenzbildung der wesentlich größeren Monopolfelder gebildet werden, bekommen wir nur numerisches Rauschen, wenn das Größenverhältnis zwischen den beiden zu groß
wird, was hier etwa bei Multipolfeldern der Größenordnung 10−25 der Fall
ist. Bei kleinen Abständen r überschneiden sich die Kurven teilweise, weil wir
noch nicht im Fernfeld sind – durch unser Konstruktionsverfahren sind insbesondere Multipole höherer Ordnung N ziemlich ausgedehnt: der Abstand
zwischen den beiden außen liegenden Ladungen ist (2N − 1)∆, wobei ∆ der
Abstand zweier benachbarter Einzelladungen ist. Dieses unschöne Detail ließe
sich entschärfen, indem man die hier gewählte lineare Anordnung der Punkt-
76
2 Elektrodynamik
ladungen durch eine räumliche Anordnung ersetzt. Abgesehen von diesen
beiden Artefakten erhalten wir jedoch die bei einer doppelt-logarithmischen
Auftragung erwarteten Geraden verschiedener Steigung.
2.4 Berechnung von Feldlinien
Nachdem wir im vorangegangenen Abschnitt elektrische Felder berechnet haben, stellt sich nun die Frage, wie wir diese am besten graphisch darstellen.
Sie kennen sowohl aus Lehrbüchern als auch aus dem Physikunterricht an
der Schule die Darstellung mittels Feldlinien: Linien, die immer in Richtung
des elektrischen Feldes zeigen. Der Berechnung solcher Feldlinien bei vorgegebenem elektrischen Feld, z.B. dem Feld eines Multipols, wollen wir uns in
diesem Abschnitt widmen.
Was wir suchen, ist also eine Kurve x(s), wobei s ein Parameter ist,
der einen jeweils festzulegenden Bereich durchläuft. Die Tatsache, dass die
Feldlinie immer in Richtung des elektrischen Feldes zeigt, heißt nun, dass
d
x(s) = α(s)E(x) .
dt
(2.13)
Den Proportionalitätsfaktor α(s) können wir hierbei frei wählen, da er lediglich angibt, wie schnell die Kurve x(s) mit s durchlaufen wird. Gleichung
(2.13) stellt bei gegebenem elektrischen Feld E(x) eine Differentialgleichung
für die Parameterdarstellung der elektrischen Feldlinien dar. Wenn man noch
berücksichtigt, dass Feldlinien immer bei positiven Ladungen beginnen und
bei negativen Ladungen enden, steht einem Computerprogramm zur Berechnung von Feldlinien nichts mehr im Wege:
1
2
3
4
5
6
/**************************************************************************
* Name:
linien1.cpp
*
* Zweck:
Berechnet Feldlinien elektrostatischer Multipol-Felder
*
* Gleichung: (d/dt)x(t) = E(x) / |E(x)|
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
14
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
"tools.h"
15
16
using namespace std;
17
18
19
20
21
22
//-- Definition der globalen Variablen
const int ord_max = 10;
const int nq_max = 1024;
int
npos;
double
x_multipol[nq_max], q_multipol[nq_max];
2.4 Berechnung von Feldlinien
77
23
24
//-------------------------------------------------------------------------
25
26
int f(double s, const double x[], double dxds[], void *params)
27
28
29
30
{
int
n;
double E_x, E_y, E, distance;
31
32
33
34
35
36
37
38
E_x = 0; E_y = 0;
for (n=0; n<=npos; n++)
{
distance = sqr(x[0]-x_multipol[n]) + sqr(x[1]);
E_x = E_x + q_multipol[n] / pow(distance,1.5) * ( x[0]-x_multipol[n]);
E_y = E_y + q_multipol[n] / pow(distance,1.5) * x[1];
}
39
40
41
42
E = sqrt( sqr(E_x) + sqr(E_y) );
dxds[0] = E_x / E;
dxds[1] = E_y / E;
43
44
45
return GSL_SUCCESS;
}
46
47
//-------------------------------------------------------------------------
48
49
50
int jac(double s, const double x[], double *dfdx, double dxds[],
void *params)
51
52
53
54
{
return GSL_SUCCESS;
}
55
56
//-------------------------------------------------------------------------
57
58
main( int argc, char *argv[] )
59
60
61
62
63
64
65
66
{
//-- Definition der Variablen
int
n, n1, n_left, ord, npos1, n_ord, nline, nline_max, status;
double
x[2], s, send, delta, eps, rtol, atol;
double
dist, dist1, deltax, deltay, h, mu = 10;
ifstream in_stream;
ofstream out_stream;
67
68
69
70
71
72
73
//-- Fehlermeldung, wenn Input- oder Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: linien1 infile outfile\n";
return 0;
}
74
75
76
77
//-- Initialisierung
for (n=0; n<nq_max; n++) x_multipol[n] = 0;
for (n=0; n<nq_max; n++) q_multipol[n] = 0;
78
79
80
81
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
78
82
83
84
85
86
2 Elektrodynamik
out_stream << "! Ausgabe-File generiert von linien1.cpp\n";
inout(ord,"ord");
inout(delta,"delta");
inout(nline_max,"nline_max");
inout(atol,"atol");
inout(rtol,"rtol");
inout(eps,"eps");
in_stream.close();
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//-- Generation des Multipols aus Einzelladungen
if (ord > ord_max)
{
cout << " Maximal zulaessige Ordnung ueberschritten!\n";
return 0;
}
if (ord < 1)
{
cout << " Nur echt positive Ordnungen sind physikalisch sinnvoll!\n";
return 0;
}
n_left = -pow(2,ord-1);
x_multipol[0] = n_left * delta;
q_multipol[0] = 1;
npos = 0;
for (n_ord=1; n_ord<=ord; n_ord++)
for (n=1; n<=pow(2,n_ord-1); n++)
{
npos++;
npos1 = npos - pow(2,n_ord-1);
x_multipol[npos] = x_multipol[npos-1] + delta;
q_multipol[npos] = -q_multipol[npos1];
}
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//-- Berechnung der elektrischen Feldlinien
for (n=0; n<=npos; n++)
{
if (q_multipol[n] > 0)
for (nline=1; nline<=nline_max; nline++)
{
deltax = eps * cos(2*3.141*(nline-0.5)/nline_max);
deltay = eps * sin(2*3.141*(nline-0.5)/nline_max);
x[0] = x_multipol[n] + deltax;
x[1] = deltay;
dist = eps * 2;
const gsl_odeiv_step_type *T1 = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s1 = gsl_odeiv_step_alloc(T1, 2);
gsl_odeiv_control *c1 = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e1 = gsl_odeiv_evolve_alloc(2);
gsl_odeiv_system sys = {f, jac, 2, &mu};
h = 1.e-6;
while (dist >= eps)
{
s = 0; send = eps;
while (s < send)
{
status = gsl_odeiv_evolve_apply(e1,c1,s1,&sys,&s,send,&h,x);
if (status != GSL_SUCCESS) break;
}
out_stream << x[0] << " " << x[1] << "\n";
dist = 1.e6;
for (n1=0; n1<=npos; n1++)
{
2.4 Berechnung von Feldlinien
141
142
143
144
145
146
147
148
149
150
151
152
79
dist1 = sqrt(sqr(x[0]-x_multipol[n1])+sqr(x[1]));
if (dist1<dist) dist = dist1;
}
if (dist<eps) break;
}
gsl_odeiv_evolve_free(e1);
gsl_odeiv_control_free(c1);
gsl_odeiv_step_free(s1);
}
}
out_stream.close();
}
In diesem Programm haben wir die Generierung eines Multipols aus dem
Programm multipol.cpp des vorigen Abschnitts übernommen (Zeile 99–110).
Rings um jede positive Ladung im Abstand eps werden die Startpunkte für
die Feldlinien gewählt (Zeile 118–121), die Feldlinien werden dann als Lösung
des Differentialgleichungssystems (2.13) berechnet (Zeile 123–136). In Zeile
144 finden Sie die Abbruchbedingung: Eine Feldlinie gilt als beendet, wenn
ihr Abstand zu einer der Punktladungen kleiner als eps geworden ist.
Das Ergebnis dieses Programms für Multipole erster bis vierter Ordnung
zeigt Abb. 2.2. Was wir an diesen Diagrammen sehr schön sehen, ist die
Symmetrie der Multipolfelder. Während das hier nicht gezeigte Feld eines
Monopols kugelsymmetrisch ist, wird diese Symmetrie beim Dipol gebrochen
und es ist symmetrisch bzgl. Spiegelung an der Verbindungslinie zwischen
den beiden Einzelladungen sowie bzgl. Drehungen um π. Das Quadrupolfeld
Abb. 2.2. Feldlinien für Multipole 1. Ordnung (Dipol, oben links), 2. Ordnung
(Quadrupol, oben rechts), 3. Ordnung (Oktopol, unten links) sowie 4. Ordnung
(unten rechts)
80
2 Elektrodynamik
sollte invariant gegenüber Drehungen um beliebige Vielfache von π/2 sein,
aufgrund des endlichen Abstands der Einzelladungen ist diese Symmetrie jedoch bereits leicht gebrochen. Noch deutlicher wird diese Symmetriebrechung
beim Oktopolfeld, das eigentlich invariant gegenüber Drehungen um Vielfache von π/3 sein sollte, und beim Multipol 4. Ordnung, dessen Feld invariant
gegenüber Drehungen um Vielfache von π/4 sein sollte. Der Grund für diese zunehmende Verzerrung des elektrischen Feldes ist die Tatsache, dass die
Ausdehnung unseres linear aufgebauten Multipols mit höherer Ordnung rasch
zunimmt und deshalb nicht mehr vernachlässigbar ist. Beachten Sie hierzu
Übungsaufgabe 2.1, in der die Multipole in einer räumlichen Anordnung realisiert werden.
2.5 Magnetfelder stationärer Ströme
Einen weiteren interessanten – und auch technisch sehr bedeutsamen Spezialfall – stellt das elektromagnetische Feld dar, das von stationären Strömen
erzeugt wird:
(x, s) = 0
j(x, s) = I
(2.14)
ds
dl(s)
δ(x − x(s)) .
ds
(2.15)
Der zweite Ausdruck beschreibt einen auf einen Draht beschränkten Strom.
Dabei ist I die Stromstärke, und s ist ein Parameter, der den Leiter parametrisiert, d.h. jedem Wert von s entspricht einem Punkt l(s) auf der Leiterbahn. In diesem Fall reduzieren sich die Maxwell-Gleichungen auf:
dl(s)
divB = 0
rotH = I ds
δ(x − x(s))
(2.16)
ds
divD = 0
rotE = 0 .
(2.17)
Diese Gleichungen lassen sich direkt lösen:
E=0
j(x ) × (x − x )
.
B = d3 x
|x − x |3
(2.18)
(2.19)
Da Ströme häufig in dünnen Leiterbahnen fließen, ist diese Situation von
besonderer Bedeutung. In diesem Fall reduziert sich das Dreifachintegral aus
(2.19) zu einem Einfachintegral entlang der Leiterbahn(en):
I(x ) × (x − x )
B = dx
.
(2.20)
|x − x |3
Das ist das Biot-Savart-Gesetz, mit dem das Magnetfeld um beliebige Leiteranordnungen berechnet werden kann, z.B. das Magnetfeld einer Spule, für
das Sie aus der Schule die Näherungsformel
2.5 Magnetfelder stationärer Ströme
B = NI
A
l
81
(2.21)
kennen (N ist die Zahl der Windungen, l und A ist die Länge bzw. die Querschnittsfläche der Spule). Berechnungen von Feldern zu verschiedenen Stromverteilungen finden Sie z.B. in [24]. Doch zurück zum Biot-Savart-Gesetz in
seiner allgemeinen Formulierung (2.19), die wir auf eine beliebig geformte
Leiterbahn anwenden können, z.B. einen Ringstrom:
Abb. 2.3. Kreisstrom und das von ihm erzeugte Magnetfeld
Die dargestellten Feldlinien wurden mittels dieses Programms berechnet:
1
2
3
4
5
6
/**************************************************************************
* Name:
kreisstrom.cpp
*
* Zweck:
Berechnet das Magnetfeld eines stromdurchflossenen Rings
*
* Gleichung: Biot-Savart-Gesetz
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
14
15
#include
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<gsl/gsl_integration.h>
"tools.h"
16
17
using namespace std;
18
19
20
21
//-- Definition der globalen Variablen
const double epsabs = 1.e-12;
const double epsrel = 1.e-12;
22
23
24
int
double
npos;
R, x[3];
25
26
//-------------------------------------------------------------------------
27
28
29
int dI(double s, double x0[3], double dx0_ds[3],
double *r, double *dl)
30
31
32
{
x0[0] = R * cos(s);
dx0_ds[0] = - R * sin(s);
82
33
34
2 Elektrodynamik
x0[1] = R * sin(s);
x0[2] = 0;
dx0_ds[1] =
dx0_ds[2] =
R * cos(s);
0;
35
36
37
*dl = sqrt(sqr(dx0_ds[0])+sqr(dx0_ds[1])+sqr(dx0_ds[2]));
*r = sqrt(sqr(x[0]-x0[0])+sqr(x[1]-x0[1])+sqr(x[2]-x0[2]));
38
39
40
return 1;
}
41
42
//-------------------------------------------------------------------------
43
44
double dB_x(double s, void *params)
45
46
47
48
{
int
result;
double x0[3], dx0_ds[3], r, dl;
49
50
result = dI(s, x0, dx0_ds, &r, &dl);
51
52
53
return (dx0_ds[1] * (x[2]-x0[2]) - dx0_ds[2] * (x[1]-x0[1]))/pow(r,3);
}
54
55
//-------------------------------------------------------------------------
56
57
double dB_y(double s, void *params)
58
59
60
61
{
int
result;
double x0[3], dx0_ds[3], r, dl;
62
63
result = dI(s, x0, dx0_ds, &r, &dl);
64
65
66
return (dx0_ds[2] * (x[0]-x0[0]) - dx0_ds[0] * (x[2]-x0[2]))/pow(r,3);
}
67
68
//-------------------------------------------------------------------------
69
70
double dB_z(double s, void *params)
71
72
73
74
{
int
result;
double x0[3], dx0_ds[3], r, dl;
75
76
result = dI(s, x0, dx0_ds, &r, &dl);
77
78
79
return (dx0_ds[0] * (x[1]-x0[1]) - dx0_ds[1] * (x[0]-x0[0]))/pow(r,3);
}
80
81
//-------------------------------------------------------------------------
82
83
double B_x(double x, double y, double z)
84
85
86
87
88
{
const size_t limit = 10000;
const int key = GSL_INTEG_GAUSS21;
double result, abserr;
89
90
91
gsl_integration_workspace * w
= gsl_integration_workspace_alloc(limit);
2.5 Magnetfelder stationärer Ströme
83
92
93
94
gsl_function F;
F.function = &dB_x;
95
96
97
gsl_integration_qag(&F, -pi, pi, epsabs, epsrel, limit, key, w,
&result, &abserr);
98
99
gsl_integration_workspace_free(w);
100
101
102
return result;
}
103
104
//-------------------------------------------------------------------------
105
106
double B_y(double x, double y, double z)
107
108
109
110
111
{
const int limit = 1000;
const int key = GSL_INTEG_GAUSS61;
double result, abserr;
112
113
114
gsl_integration_workspace * w
= gsl_integration_workspace_alloc(limit);
115
116
117
gsl_function F;
F.function = &dB_y;
118
119
120
gsl_integration_qag(&F, -pi, pi, epsabs, epsrel, limit, key, w,
&result, &abserr);
121
122
gsl_integration_workspace_free(w);
123
124
125
return result;
}
126
127
//-------------------------------------------------------------------------
128
129
double B_z(double x, double y, double z)
130
131
132
133
134
{
const int limit = 1000;
const int key = GSL_INTEG_GAUSS61;
double result, abserr;
135
136
137
gsl_integration_workspace * w
= gsl_integration_workspace_alloc(limit);
138
139
140
gsl_function F;
F.function = &dB_z;
141
142
143
gsl_integration_qag(&F, 0, 2*pi, epsabs, epsrel, limit, key, w,
&result, &abserr);
144
145
gsl_integration_workspace_free(w);
146
147
148
return result;
}
149
150
//-------------------------------------------------------------------------
84
2 Elektrodynamik
151
152
int f(double s, const double x[], double dxds[], void *params)
153
154
155
{
double B;
156
157
158
159
160
161
B = sqrt(
+
dxds[0] =
dxds[1] =
dxds[2] =
sqr(B_x(x[0],x[1],x[2])) + sqr(B_y(x[0],x[1],x[2]))
sqr(B_z(x[0],x[1],x[2])) );
B_x(x[0],x[1],x[2]) / B;
B_y(x[0],x[1],x[2]) / B;
B_z(x[0],x[1],x[2]) / B;
162
163
164
return GSL_SUCCESS;
}
165
166
//-------------------------------------------------------------------------
167
168
169
int jac(double s, const double x[], double *dfdx, double dxds[],
void *params)
170
171
172
173
{
return GSL_SUCCESS;
}
174
175
//-------------------------------------------------------------------------
176
177
178
main( int argc, char *argv[] )
179
180
181
182
183
184
185
186
187
188
{
//-- Definition der Variablen
double
dxds[3], x_start[3], s, send, ds;
double
delta, eps, rtol, atol, dist, dist1;
double
mu = 10;
double
h, x2old;
int
n, n1, N, N_res, nline, nline_max;
ifstream in_stream;
ofstream out_stream;
189
190
191
192
193
194
195
//-- Fehlermeldung, wenn Input- oder Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: kreisstrom infile outfile\n";
return 0;
}
196
197
198
199
200
201
202
203
204
205
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ausgabe-File generiert von kreisstrom.cpp \n";
inout(R,"R");
inout(x_start[0],"x_start[0]");
inout(x_start[1],"x_start[1]"); inout(x_start[2],"x_start[2]");
inout(ds,"ds");
inout(atol,"atol");
inout(rtol,"rtol");
inout(eps,"eps");
in_stream.close();
206
207
208
209
//-- Berechnung der elektrischen Feldlinien
x[0] = x_start[0];
x[1] = x_start[1];
2.5 Magnetfelder stationärer Ströme
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
85
x[2] = x_start[2];
const gsl_odeiv_step_type *T1 = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s1 = gsl_odeiv_step_alloc(T1, 3);
gsl_odeiv_control *c1 = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e1 = gsl_odeiv_evolve_alloc(3);
gsl_odeiv_system sys = {f, jac, 3, &mu};
h = 1.e-6;
s = 0;
send = ds;
dist = 1.e6;
x2old = 1.e6;
while ((x2old >=0) || (x[2]<=0))
{
x2old = x[2];
while (s < send)
{
int status = gsl_odeiv_evolve_apply(e1,c1,s1,&sys,&s,send,&h,x);
if (status != GSL_SUCCESS) break;
}
out_stream << x[0] << " " << x[1] << " " << x[2] << "\n";
cout << s << " " << x[0] << " " << x[1] << " " << x[2] << "\n";
dist = sqrt(sqr(x[0]-x_start[0])+sqr(x[1]-x_start [1])
+sqr(x[2]-x_start[2]));
send = send + ds;
}
gsl_odeiv_evolve_free(e1);
gsl_odeiv_control_free(c1);
gsl_odeiv_step_free(s1);
238
239
240
out_stream.close();
}
Die Generierung der Feldlinien bei gegebenem Feld können wir aus dem
Programm linien1.cpp übernehmen, was bleibt ist die Berechnung des magnetischen Feldes gemäß (2.19), die zur besseren Übertragung auf andere
Leitergeometrien in drei Teile aufgeteilt ist:
–
Das Unterprogramm dI liefert für ein s die zugehörigen Koordinaten des
Punktes x0 (s) auf dem Leiter, die Richtung der Leiterbahn sowie der
Einfachheit halber den Abstand vom gerade betrachteten Punkt x und
das Längenelement dl = |dx0 (s)/ds|.
– Die Funktionen dB x, dB y und dB z stellen die drei Komponenten des
Integranden in (2.19) zur Verfügung.
– Die Unterprogramme B x, B y und B z schließlich berechnen die jeweiligen
Komponenten des magnetischen Feldes durch Integration der Funktionen
dB x, dB y und dB z.
Den Vorteil dieser Strukturierung sehen Sie, wenn Sie das Programm
spule1.cpp weiter unten studieren. Abgesehen von einer Anpassung der einzulesenden Parameter, musste lediglich das Unterprogramm dI modifiziert
werden.
Bei diesem Programm haben wir von einer nützlichen Symmetrie nicht
Gebrauch gemacht: das Problem ist nämlich symmetrisch bzgl. beliebigen
Drehungen um die z-Achse. Aus diesem Grund müssen Feldlinien immer in
86
2 Elektrodynamik
der Ebene liegen, die durch den Startpunkt und die z-Achse festgelegt wird.
Der Grund, warum wir uns den Luxus leisten, von dieser Einschränkung auf
zwei Dimensionen nicht Gebrauch zu machen, liegt wieder in der Wiederverwertbarkeit für andere Probleme, bei denen diese Symmetrie nicht mehr
vorliegt. Womit wir nun endgültig zum Programm spule1.cpp kommen, das
ebenfalls die Feldlinien des Magnetfelds berechnet – diesmal jedoch für eine
Spule:
Abb. 2.4. Stromführung durch eine Spule inklusive Zuleitungen
1
2
3
4
5
6
/*********************************************************************
* Name:
spule1.cpp
*
* Zweck:
Berechnet das Magnetfeld einer Spule
*
* Gleichung: Biot-Savart-Gesetz
*
* verwendete Bibiliothek: GSL
*
*********************************************************************/
7
8
9
10
11
12
13
14
15
#include
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_matrix.h>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<gsl/gsl_integration.h>
"tools.h"
16
17
using namespace std;
18
19
//-- Definition der globalen Variablen
20
21
22
23
24
25
26
const
const
const
const
const
const
double
double
double
double
double
double
epsabs = 1.e-12;
epsrel = 1.e-12;
s0 = 0;
const double s1 = 1;
s2 = 2;
const double s3 = 3;
s4 = 4;
const double s5 = 5;
s6 = 6;
27
28
int
npos, dir;
2.5 Magnetfelder stationärer Ströme
29
double
R, l, L1, L2, phi_min, phi_max, dphids, x[3];
30
31
//--------------------------------------------------------------------
32
33
34
int dI(double s, double x0[3], double dx0_ds[3],
double *r, double *dl)
35
36
37
{
double phi = phi_min + dphids*s;
38
39
40
41
42
43
44
if (s<1)
{
x0[0] = R * cos(phi);
x0[1] = R * sin(phi);
x0[2] = l * s;
}
dx0_ds[0] = - R * sin(phi) * dphids;
dx0_ds[1] =
R * cos(phi) * dphids;
dx0_ds[2] = l;
45
46
47
48
49
50
51
if ((s>=s1) && (s<s2))
{
x0[0] = R;
dx0_ds[0] = 0;
x0[1] = 0;
dx0_ds[1] = 0;
x0[2] = l + (L2-l) * (s-1); dx0_ds[2] = (L2-l);
}
52
53
54
55
56
57
58
if ((s>=s2) && (s<s3))
{
x0[0] = R + (L1-R)*(s-2);
x0[1] = 0;
x0[2] = L2;
}
dx0_ds[0] = L1-R;
dx0_ds[1] = 0;
dx0_ds[2] = 0;
59
60
61
62
63
64
65
if ((s>=s3) && (s<s4))
{
x0[0] = L1;
x0[1] = 0;
x0[2] = L2 - 2*L2*(s-3);
}
dx0_ds[0] = 0;
dx0_ds[1] = 0;
dx0_ds[2] = -2*L2;
66
67
68
69
70
71
72
if ((s>=s4) && (s<=s5))
{
x0[0] = L1 + (R-L1)*(s-4);
x0[1] = 0;
x0[2] = -L2;
}
dx0_ds[0] = R-L1;
dx0_ds[1] = 0;
dx0_ds[2] = 0;
73
74
75
76
77
78
79
if ((s>=s5) && (s<=s6))
{
x0[0] = R;
x0[1] = 0;
x0[2] = -L2+L2*(s-5);
}
dx0_ds[0] = 0;
dx0_ds[1] = 0;
dx0_ds[2] = L2;
80
81
82
*dl = sqrt(sqr(dx0_ds[0])+sqr(dx0_ds[1])+sqr(dx0_ds[2])) / 100.;
*r = sqrt(sqr(x[0]-x0[0])+sqr(x[1]-x0[1])+sqr(x[2]-x0[2]));
83
84
85
return 1;
}
86
87
//--------------------------------------------------------------------
87
88
2 Elektrodynamik
88
89
double dB_x(double s, void *params)
90
91
92
93
{
int
result;
double x0[3], dx0_ds[3], r, dl;
94
95
result = dI(s, x0, dx0_ds, &r, &dl);
96
97
98
99
return (dx0_ds[1] * (x[2]-x0[2]) - dx0_ds[2] * (x[1]-x0[1]))
/ pow(r,3) * dl;
}
100
...
126
127
//--------------------------------------------------------------------
128
129
double B_x(double x, double y, double z)
130
131
132
133
134
{
const size_t limit = 10000;
const int key = GSL_INTEG_GAUSS21;
double result1, result2, result3, result4, result5, result6, abserr;
135
136
137
138
gsl_integration_workspace * w
= gsl_integration_workspace_alloc(limit);
139
140
141
gsl_function F;
F.function = &dB_x;
142
143
144
145
146
147
148
149
150
151
152
153
154
gsl_integration_qag(&F, s0, s1, epsabs,
&result1, &abserr);
gsl_integration_qag(&F, s1, s2, epsabs,
&result2, &abserr);
gsl_integration_qag(&F, s2, s3, epsabs,
&result3, &abserr);
gsl_integration_qag(&F, s3, s4, epsabs,
&result4, &abserr);
gsl_integration_qag(&F, s4, s5, epsabs,
&result5, &abserr);
gsl_integration_qag(&F, s5, s6, epsabs,
&result6, &abserr);
epsrel, limit, key, w,
epsrel, limit, key, w,
epsrel, limit, key, w,
epsrel, limit, key, w,
epsrel, limit, key, w,
epsrel, limit, key, w,
155
156
gsl_integration_workspace_free(w);
157
158
159
return result1+result2+result3+result4+result5+result6;
}
160
...
226
227
//-------------------------------------------------------------------------
228
229
int f(double s, const double x[], double dxds[], void *params)
230
231
{
2.5 Magnetfelder stationärer Ströme
232
89
double B;
233
234
235
236
237
238
B = sqrt(
+
dxds[0] =
dxds[1] =
dxds[2] =
sqr(B_x(x[0],x[1],x[2])) + sqr(B_y(x[0],x[1],x[2]))
sqr(B_z(x[0],x[1],x[2])) );
dir * B_x(x[0],x[1],x[2]) / B;
dir * B_y(x[0],x[1],x[2]) / B;
dir * B_z(x[0],x[1],x[2]) / B;
239
240
241
return GSL_SUCCESS;
}
242
243
//-------------------------------------------------------------------------
244
245
246
int jac(double s, const double x[], double *dfdx,
double dxds[], void *params)
247
248
249
250
{
return GSL_SUCCESS;
}
251
252
//-------------------------------------------------------------------------
253
254
255
main( int argc, char *argv[] )
256
257
258
259
260
261
262
263
264
265
{
//-- Definition der Variablen
int
n, n1, N, nline, nline_max;
double
dxdt[3], x_start[3], s, s_max, send, ds, delta, rtol, atol;
double
dist, dist1, Bx, By, Bz, kruemmung, h;
double
tx, ty, tz, txold, tyold, tzold, txp, typ, tzp, kx, ky, kz;
double
mu = 10;
ifstream in_stream;
ofstream out_stream;
266
267
268
269
270
271
272
273
//-- Fehlermeldung, wenn Input- und Outputfilename nicht
//
uebergeben wurden
if (argc<3)
{
cout << " Aufruf: spule1 infile outfile\n";
return 0;
}
274
275
276
277
278
279
280
281
282
283
284
285
286
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ausgabe-File generiert von spule1.cpp \n";
inout(N,"N");
inout(l,"l");
inout(L1,"L1");
inout(L2,"L2");
inout(R,"R");
inout(s_max,"s_max");
inout(ds,"ds");
inout(x_start[0],"x_start[0]");
inout(x_start[1],"x_start[1]"); inout(x_start[2],"x_start[2]");
inout(atol,"atol");
inout(rtol,"rtol");
inout(dir,"dir");
in_stream.close();
287
288
289
290
//-- Berechnung einiger benoetigter Parameter -phi_min = 0;
phi_max = 2 * pi * N;
90
291
2 Elektrodynamik
dphids = phi_max - phi_min;
292
293
294
295
296
297
298
299
300
301
302
303
304
305
//-- Berechnung der elektrischen Feldlinien
x[0] = x_start[0];
x[1] = x_start[1];
x[2] = x_start[2];
const gsl_odeiv_step_type *T1 = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s1 = gsl_odeiv_step_alloc(T1, 3);
gsl_odeiv_control *c1 = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e1 = gsl_odeiv_evolve_alloc(3);
gsl_odeiv_system sys = {f, jac, 3, &mu};
h = 1.e-6;
s = 0;
send = ds;
dist = 1.e6;
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
while (s < s_max)
{
while (s < send)
{
int status = gsl_odeiv_evolve_apply(e1,c1,s1,&sys,&s,send,&h,x);
if (status != GSL_SUCCESS) break;
}
out_stream << s << " " << x[0] << " " << x[1] << " " << x[2] << " ";
Bx = B_x(x[0],x[1],x[2]);
By = B_y(x[0],x[1],x[2]);
Bz = B_z(x[0],x[1],x[2]);
tx = Bx / sqrt( sqr(Bx)+sqr(By)+sqr(Bz) );
ty = By / sqrt( sqr(Bx)+sqr(By)+sqr(Bz) );
tz = Bz / sqrt( sqr(Bx)+sqr(By)+sqr(Bz) );
txp = ( tx - txold ) / ds; txold = tx;
typ = ( ty - tyold ) / ds; tyold = ty;
tzp = ( tz - tzold ) / ds; tzold = tz;
kx = ty * tzp - tz * typ;
ky = tz * txp - tx * tzp;
kz = tx * typ - ty * txp;
kruemmung = sqrt( sqr(kx)+sqr(ky)+sqr(kz) );
out_stream << Bx << " " << By << " " << Bz << " "
<< sqrt(sqr(Bx)+sqr(By)+sqr(Bz)) << " " << kruemmung << "\n";
cout << x[0] << " " << x[1] << " " << x[2] << "\n";
dist = sqrt(sqr(x[0]-x_start[0])+sqr(x[1]-x_start [1])
+sqr(x[2]-x_start[2]));
send = send + ds;
}
gsl_odeiv_evolve_free(e1);
gsl_odeiv_control_free(c1);
gsl_odeiv_step_free(s1);
338
339
340
out_stream.close();
}
Etwas länglich ist die Funktion dI, die bei gegebenem Parameter t die
dazugehörige Position x0, das Linienelement dx0 ds, sowie dessen Betrag dl
und den Abstand r vom Punkt x berechnet. Das liegt an der notwendigen
Fallunterscheidung für die verschiedenen Abschnitte der Leiterbahn:
2.5 Magnetfelder stationärer Ströme
91
für t ∈ [0, 1] befindet man sich auf der Spulenwicklung,
für t ∈ [1, 2] befindet man sich auf dem daran anschließenden Abschnitt
in z-Richtung,
– für t ∈ [2, 3] befindet man sich auf dem nächsten Abschnitt, der in xRichtung verläuft,
– usw.
–
–
Von den Funktionen dB x, dB y und dB z haben wir hier nur die erste wiedergegeben – die beiden anderen sind völlig analog. Diese Funktionen stellen
die Implementierung des Integranden im Biot-Savart-Gesetz (2.20) dar und
sind gegenüber dem Programm kreisstrom.cpp unverändert. Das selbe gilt
für die Funktionen B x, B y und B z – Sie finden im Buchtext wieder nur B x.
Diese berechnen aus dB x, dB y und dB z durch Integration das magnetische
Feld. Die Routine f schließlich baut aus diesen Funktionen das Differentialgleichungssystem, dessen Lösung – im Hauptprogramm implementiert – die
Feldlinien sind.
Abb. 2.5. Magnetisches Feld einer (gestrichelt eingezeichneten) Spule – man beachte, dass die Feldlinien am Rand der Spule wegen der einzelnen Wicklungen
onduliert sind und dann aus dem Innenbereich der Spule ausbrechen
Das Bild der Feldlinien in Abb. 2.5 entspricht unseren Erwartungen: innerhalb der schematisch eingezeichneten Spule ist das Feld einigermaßen homogen – je näher man aber den Spulendrähten kommt, desto mehr weicht
das Feld von dieser Vorstellung ab, weil es die Diskretheit der Drahtwicklungen sieht. Interessanterweise verlassen die äußeren beiden Feldlinien die
Spule nicht an den Enden sondern nutzen den Zwischenraum zwischen zwei
Drahtwicklungen. Wenn Sie die Feldlinien weiter in den Außenbereich verfolgen – was Sie zur Übung unbedingt tun sollten! – werden Sie feststellen,
dass sich auch dort die Feldlinien ungefähr so verhalten, wie man dies erwartet: sie machen einen weiten Bogen um die Spule, treten wieder in diese ein,
und die Feldlinie schließt sich – naja, jedenfalls fast – wieder. Soweit so gut,
nun wollen wir an den Parametern ein wenig drehen, und schauen, ob wir
vielleicht etwas Interessanteres entdecken können.
Die Spule, die Abb. 2.6 zugrunde liegt, ist gleich lang wie die aus Abb. 2.5,
jedoch hat sie nur den halben Radius und doppelt so viele Wicklungen (20
statt 10). Gezeigt ist nur eine einzelne Feldlinie, die in etwa in der Mitte
92
2 Elektrodynamik
10
5
0
-10
-5
0
5
Abb. 2.6. Eine Feldlinie des magnetischen Felds einer Spule – man beachte, dass
sich die Feldlinie nicht schließt
der Spule beginnt, wobei wir die ganze Anordnung gedreht haben, um eine
maßstabsgerechte Darstellung zu ermöglichen.
Im Innenbereich der Spule sehen wir wieder einen weitgehend geradlinigen Verlauf (entsprechend dem hier nahezu homogenen Magnetfeld). Wenn
wir diesen Bereich getrennt darstellen und den Maßstab der z-Achse stark
vergrößern, stellen wir wieder eine leichte Welligkeit der Feldlinien fest, die
von den einzelnen Spulenwicklungen herrührt. Interessant wird es aber vor
allem, wenn wir die Feldlinie im Außenbereich verfolgen: die Feldlinie ist hier
bei weitem nicht mehr eben und der Weg an das andere Ende der Spule
verläuft in unerwarteten Schleifen. Die Tatsache, dass die Feldlinien nicht
in einer Ebene verlaufen, sollte uns allerdings nicht sonderlich überraschen,
da die Wicklungen der Spule keine entsprechende Symmetrie aufweisen. Dieser Verlauf der Feldlinie im Außenbereich der Spule führt dazu, dass sich
die Feldlinie nicht schließt, sondern beim zweiten Durchlauf durch die Spule
parallel zum ersten verläuft.
2.6 Hysterese
Wir haben bereits in Abschn. 2.1 angemerkt, dass der lineare Charakter der
Maxwellschen Gleichungen durch nichtlineare Materialgleichungen zu einem
komplexen nichtlinearen Gleichungssystem führen kann. Dort hatten wir auch
festgehalten, dass in manchen Fällen überhaupt kein funktionaler Zusammenhang zwischen Polarisation P und elektrischem Feld E bzw. zwischen Magnetisierung M und magnetischem Feld H existiert, sondern Gedächtniseffekte
eine Rolle spielen. Dieses Phänomen wird Hysterese genannt und wir wollen
in diesem Abschnitt anhand eines einfachen Modells für einen Ferromagneten
(siehe [25]) aufzeigen, wie es zu diesem Effekt kommt.
Wir gehen von einem System von N Spin- 12 -Teilchen (siehe Abschn. 5.13)
aus, die sich in einem äußeren Magnetfeld Hext jeweils entweder parallel oder
2.6 Hysterese
93
antiparallel zu Hext ausrichten können. Wir bezeichnen die Zahl der parallel orientierten Spins mit N1 und die der antiparallel orientierten mit N2 .
Wenn wir diese beiden Zahlen im thermischen Gleichgewicht wissen möchten,
müssen wir einen Vorgriff auf Kap. 4 machen:
N1
E1 − E 2
= exp −
.
(2.22)
N2
kT
Dabei sind k die Boltzmann-Konstante und E1 und E2 die jeweiligen Energien
der beiden möglichen Spinzustände:
E1 = −µH
E2 = +µH .
(2.23)
(2.24)
Das magnetische Feld H setzt sich nun aus dem externen Feld Hext und
dem durch die Spins selbst erzeugten Feld zusammen. Letzteres ist für jeden
Spin verschieden, wenn wir dieses lokale Feld jedoch durch dessen Mittelwert
ersetzen (Mean-Field-Näherung) erhalten wir:
H = Hext + α(N1 − N2 ) .
(2.25)
Der zweite Term auf der rechten Seite ist die Magnetisierung M . Der entscheidende Schritt zur Komplettierung unseres Modells ist nun die Aufstellung von Bewegungsgleichungen für N1 und N2 , die bei statischem äußerem
Feld für t → ∞ in das durch (2.22) beschriebene Gleichgewicht läuft. Darüber
hinaus haben wir zu beachten, dass die Bewegungsgleichungen bzgl. Invertierung des äußeren Feldes Hext bei gleichzeitigem Vertauschen von N1 und
N2 symmetrisch sein müssen. Überzeugen Sie sich bitte, dass die geforderten
Bedingungen von
d
N1 = −ξ [(N1 exp(−βH) − N2 exp(βH)]
dt
d
N2 = +ξ [(N1 exp(−βH) − N2 exp(βH)]
dt
(2.26)
(2.27)
erfüllt werden. Dabei haben wir zum einen den Parameter ξ eingeführt, der
angibt, wie schnell die Spins auf eine Änderung des magnetischen Feldes
reagieren. Zum anderen haben wir die Abkürzung
β=
αµ
kT
(2.28)
verwendet. β setzt die Wechselwirkungsenergie eines einzelnen Spins mit der
Magnetisierung ins Verhältnis zur thermischen Energie pro Freiheitsgrad.
Durch Subtraktion von (2.26) und (2.27) und Multiplikation mit µ erhält man
eine einzige Bewegungsgleichung für die Magnetisierung M = α(N1 − N2 ):
d
M = −2µξ [N1 exp(βH) − N2 exp(βH)] .
dt
(2.29)
94
2 Elektrodynamik
Die darin auftretenden Größen N1 , N2 und H müssen dabei durch M ausgedrückt werden:
M
1
N1,2 =
1±
(2.30)
2
α
H = Hext + M
(2.31)
Die Implementierung dieser Bewegungsgleichung stellt uns vor keine besonderen Probleme:
1
2
3
4
5
6
/**************************************************************************
* Name:
hysterese.cpp
*
* Zweck:
Berechnet eine Hysterese-Kurve
*
* Gleichung: siehe Buch
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_odeiv.h>
<gsl/gsl_errno.h>
<math.h>
"tools.h"
14
15
using namespace std;
16
17
18
//-- globale Variablen
double mu, kT, xi, H_0, omega, alpha, beta;
19
20
//-------------------------------------------------------------------------
21
22
int f(double t, const double x[], double dxdt[], void *params)
23
24
25
{
double H_ext, H, N1, N2;
26
27
28
29
30
H_ext = H_0 * sin(omega*t);
H = H_ext + x[0];
N1 = 0.5 * (1. + x[0] / alpha);
N2 = 0.5 * (1. - x[0] / alpha);
31
32
dxdt[0] = -2*alpha*xi*(N1*exp(-beta*H)-N2*exp(beta*H));
33
34
35
return GSL_SUCCESS;
}
36
37
//------------------------------------------------------------------------
38
39
40
int jac(double t, const double x[], double *dfdx, double dxdt[],
void *params)
41
42
43
44
{
return GSL_SUCCESS;
}
45
46
47
//-------------------------------------------------------------------------
2.6 Hysterese
48
95
main( int argc, char *argv[] )
49
50
{
51
52
53
54
55
//-- Definition der Variablen
int
n, nout;
double x[1], t, t1, dt, atol, rtol, h, tend;
double mu1 = 10;
56
57
58
ifstream in_stream;
ofstream out_stream;
59
60
61
62
63
64
65
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: hysterese infile outfile\n";
exit(1);
}
66
67
68
69
70
71
72
73
74
75
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von hysterese.cpp\n";
inout(mu,"mu");
inout(kT,"kT");
inout(xi,"xi");
inout(alpha,"alpha");
inout(H_0,"H_0");
inout(omega,"omega");
inout(tend,"tend");
inout(nout,"nout");
in_stream.close();
76
77
78
79
//-- Berechnung einiger benoetigter Parameter -dt = tend / nout;
beta = exp(alpha*mu/(kT));
80
81
82
83
//-- Anfangsbedingungen
t
= 0; x[0] = 0;
atol = 1.e-6; rtol = 1.e-6;
84
85
86
87
88
89
90
//-- Initialisierung der GSL-Routine
const gsl_odeiv_step_type *T = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, 1);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(1);
gsl_odeiv_system sys = {f, jac, 1, &mu1};
91
92
93
94
95
96
97
98
99
100
101
102
for (n=1; n<=nout; n++)
{
t1 = n * dt;
while (t<t1)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t1, &h, x);
if (status != GSL_SUCCESS) break;
}
cout
<< t << " " << H_0*sin(omega*t) << " " << x[0] << "\n";
out_stream << t << " " << H_0*sin(omega*t) << " " << x[0] << "\n";
}
103
104
105
out_stream.close();
}
96
2 Elektrodynamik
In dem Programm ist die Zeitabhängigkeit des äußeren Feldes auf
Hext = H0 sin(ωt)
(2.32)
festgelegt (Zeile 27). Für kleine Frequenzen ω stellt sich zu jedem Zeitpunkt das durch (2.22) beschriebene Gleichgewicht ein. Wenn jedoch ω in
der Größenordnung von ξ oder darüber liegt, hinkt die Magnetisierung ihrem
Gleichgewichtswert immer hinterher und wir beobachten Hysterese:
Abb. 2.7. Magnetisierung M in Abhängigkeit vom äußeren Feld Hext , das einen
sinusoidalen Zeitverlauf hat. Nach dem transienten Verhalten in der ersten Halbperiode sieht man deutlich die typische Hysterese-Kurve
Dieser Abbildung lagen die Parameter µ = kT = ξ = 1, α = 0.6, H0 = 2 sowie
ω = 2 zu Grunde. Wir überlassen es dem Leser, zu untersuchen, inwieweit sich
die in Abb. 2.7 dargestellte Kurve ändert, wenn man die einzelnen Parameter
verändert.
Übungen
2.1 Räumlich angeordnete Multipole
Sie können die Ausdehnung der Multipole aus Abschn. 2.3 reduzieren, indem
Sie statt einer linearen Anordnung der Einzelladungen eine räumliche Anordnung wählen. Berechnen Sie die Abhängigkeit der Feldstärke vom Abstand
r und vergleichen Sie die Ergebnisse mit Abb. 2.1. Wie fällt dieser Vergleich
für kleine Abstände und für große Abstände aus? Interessant ist es auch, für
diese Anordnung die Feldlinien zu berechnen und mit Abb. 2.2 zu vergleichen.
2.2 Feld von Helmholtz-Spulen
In Abschn. 2.5 haben wir das Magnetfeld eines Kreisstroms betrachtet. Sie
können nun zwei solcher Ringströme koaxial anordnen und erhalten eine
Helmholtz-Spule (Abb. 2.8).
Typischerweise wird der Abstand der beiden Spulen gleich deren Radius
gewählt, wodurch das Magnetfeld in der Mitte der Anordnung sehr homogen
Übungen
97
Abb. 2.8. Helmholtz-Spule, bestehend aus zwei Ringströmen. Gestrichelt eingezeichnet wurde die Symmetrieachse der Anordnung
wird. Berechnen Sie dieses Feld in der Nähe der Mitte für diesen Abstand,
wobei Sie auch Variationen senkrecht zur Symmetrieachse betrachten sollen.
Wie sieht das magnetische Feld aus, wenn der Stromfluß in den beiden
Ringen nicht wie dargestellt gleichsinnig sondern gegensinnig erfolgt?
2.3 Diamagnetismus
Das in Abschn. 2.6 entworfene Modell beschreibt für positive µ – je nach
Größe von µ und α ein paramagnetisches oder ferromagnetisches Material,
bei dem sich die Spins parallel zum magnetischen Feld ausrichten möchten.
Für negative µ ist die energetisch günstige Orientierung hingegen antiparallel zum äußeren Feld, was der Situation bei einem Diamagneten entspricht.
Untersuchen Sie zunächst analytisch das Verhalten bei statischem äußeren
Feld und anschließend numerisch das bei einem Magnetfeld mit sinusoidalem
Zeitverlauf (2.32).
2.4 Magnetisierung und Entmagnetisierung
Durchläuft man die Hystereseschleife in Abb. 2.7 und hört bei Hext = 0
auf, verbleibt das Material mit einer endlichen Magnetisierung, die man Remaneszenz nennt. Möchte man dies vermeiden, muss man das äußere Feld
langsam abklingen lassen (z.B. in Form einer gedämpften Sinusschwingung).
Schreiben Sie – aufbauend auf hysterese.cpp – ein Programm, das diese
Entmagnetisierung modelliert.
3 Optik
In diesem Kapitel wollen wir einige ausgewählte Probleme der Optik mit Verfahren der numerischen Physik bearbeiten. Da Licht nichts anderes als eine
elektromagnetische Welle ist, handelt es sich hierbei streng genommen um ein
Unterkapitel zu 2 – aufgrund der Bedeutung der Optik ist es jedoch gerechtfertigt, hierfür ein eigenes Kapitel vorzusehen. Während die ersten Abschnitte
sich mit der Strahlenoptik beschäftigen, werden wir uns ab Abschn. 3.7 der
Wellenoptik widmen.
3.1 Historischer Überblick
Beginnen wir unseren Streifzug durch die Optik mit einem kleinen historischen Rückblick auf deren Geschichte: Nachdem lange Zeit über das Wesen von Licht nur wilde Spekulationen existierten, kristallisierten sich im
17. Jahrhundert zwei unvereinbare Theorien heraus. Die erstere wurde von
dem größten Physiker dieser Epoche vertreten, von Isaac Newton. Er war
der Ansicht, dass Licht ein Strom von Teilchen ist, deren Flugbahn bei Lichtstrahlen besonders deutlich wird. Aus dieser Vorstellung entwickelte sich die
Strahlenoptik. Der Hauptvertreter der zweiten Theorie war Huygens – nach
dieser Theorie sollte es sich bei Licht um Schwingungen eines Mediums handeln, das jeden Stoff durchdringt und das er Äther nannte (der nichts mit
dem Äther aus der Chemie zu tun hat). Aus dieser Theorie entwickelte sich
später die Wellenoptik. Zu Lebzeiten dieser beiden Physiker wurde allgemein
die Newtonsche Theorie bevorzugt, zum einen, weil es keinerlei sonstige Hinweise auf den Äther gab, den Huygens’ Theorie benötigte, zum anderen aber
auch wegen der Berühmtheit Newtons. Später jedoch häuften sich Hinweise,
dass Newton in diesem Punkt doch irrte und die Huygenssche Theorie korrekt
sei. Als Mitte des 19. Jahrhunderts die Maxwellschen Gleichungen aufgestellt
wurden, die als spezielle Lösungen die elektromagnetischen Wellen und damit
Licht beinhalten, schien die Entscheidung über die wahre Natur des Lichts
gefallen. Aber das Blatt wendete sich noch einmal – gegen Ende des vorletzten Jahrhunderts gab es die ersten Experimente, die nicht ins Wellenbild des
Lichts passten und schließlich zur Entwicklung der Quantentheorie führten:
nach unserer heutigen Auffassung sind die beiden oben dargestellten Theorien über das Licht zwei Facetten, von denen je nach Experiment die eine oder
100
3 Optik
die andere zum Vorschein kommen. Die Theorie, die beide Aspekte in sich
vereinigt und das Licht korrekt beschreibt, ist folgerichtig die Quantenoptik.
Es stellt sich natürlich die Frage, warum sich so viele – sich gegenseitig
ausschließende – Auffassungen über die Natur des Lichts entwickeln konnten. Die Antwort ist, dass jede dieser Betrachtungsweisen ihre Berechtigung
hat, und die Frage, ob die geometrische Optik, die Wellenoptik oder gar
nur die Quantenoptik die geeignete Beschreibung für ein konkretes Problem
ist, von der Art des Problems abhängt. Wenn alle relevanten Längen groß
gegen die Wellenlänge des Lichts (ca 400–800 nm) sind, spielen die Welleneigenschaften keine Rolle und die geometrische Optik kommt zu korrekten
Resultaten. Ist dies nicht der Fall, z.B. weil Wegdifferenzen in dieser Größenordnung vorhanden sind, tritt die Wellennatur des Lichts zutage und eine
korrekte Beschreibung erfordert deren Berücksichtigung. Die Quantennatur
des Lichts schließlich wird relevant, wenn die Energie eines einzelnen Photons
(h̄ω, wobei h̄ das Plancksche Wirkungsquantum und ω = 2πf die Frequenz
des Lichts ist) nicht mehr klein gegen die alle anderen betrachteten Energien
ist.
An dieser Stelle ist auch ein Blick auf die Weise interessant, in der Optik gelehrt und gelernt wird. Diese ist nämlich äquivalent zur historischen
Entwicklung und beginnt in der gymnasialen Mittelstufe mit der Strahlenoptik und geht dann erst zur Wellenoptik über. Quantenoptik schließlich bleibt,
wenn überhaupt, den höheren Semestern im Physikstudium vorbehalten. Und
an diese Reihenfolge wollen wir uns auch in diesem Kapitel halten und beginnen daher mit Problemen aus der Strahlenoptik.
3.2 Grundbegriffe der Strahlenoptik
Der entscheidende Begriff der Strahlenoptik ist – naheliegenderweise – der des
Lichtstrahls. Dieser wird zum einen durch seine Bahn und zum anderen in
jedem Punkt seiner Bahnkurve durch seine Intensität, seine Wellenlänge und
gegebenenfalls durch seine Polarisation charaktierisiert. Mit der letzten Eigenschaft verlassen wir allerdings streng genommen schon die Strahlenoptik,
da die Polarisation die Schwingungsebene des elektrischen und magnetischen
Feldes, also eine typische Welleneigenschaft, angibt – näheres hierzu werden
wir in Abschn. 3.7 diskutieren.
Beginnen wir jedoch mit der Bahnkurve: diese ist in homogenen Ausbreitungsmedien geradlinig, so dass der geometrische Verlauf des Lichtstrahls hier
durch eine Geradengleichung (z.B. einen Punkt und einen Richtungsvektor)
beschrieben wird. Hierbei bezieht sich die Forderung der Homogenität auf die
Brechzahl n und damit die Ausbreitungsgeschwindigkeit des Lichts in diesem
Medium. Interessant wird es, wenn wir mehrere Medien mit verschiedenen
Brechzahlen haben: in diesem Fall erfolgt an der Grenzfläche zwischen diesen
beiden Materialien Brechung und Reflektion (siehe Abschn. 3.3). In solchen
Situationen ist es also unsere Aufgabe, eine Abfolge von Geradengleichungen
3.3 Brechung und Reflektion von Licht
101
für den Verlauf des Lichtstrahls aufzustellen und gegebenenfalls die Intensität
und Polarisation entlang dieses Strahls zu berechnen.
3.3 Brechung und Reflektion von Licht
In der Strahlenoptik geht es um die Berechnung von Lichtstrahlen. Diese
verlaufen im Allgemeinen geradlinig, jedoch mit zwei Ausnahmen, die auftreten, wenn ein Lichtstrahl auf die Grenzfläche zwischen zwei Medien mit
unterschiedlichen Brechzahlen trifft:
x’
n2
x
n1
n
x’’
Abb. 3.1. Brechung und Reflektion an einer Grenzfläche
–
Ein Teil des Lichts dringt in das andere Medium ein, wobei sich dessen
Richtung ändert. Dieses Phänomen bezeichnet man als Brechung.
– Der verbleibende Teil des Lichts wird an der Grenzfläche reflektiert. Diesen Vorgang bezeichnet man als Reflektion.
Dies sind also die beiden Elementarprozesse, die wir zunächst beherrschen
müssen, bevor wir uns schwierigeren, darauf aufbauenden Problemen zuwenden. Wie in Abb. 3.1 wollen wir den Normalenvektor der Grenzfläche so
wählen, dass sein Skalarprodukt mit dem Richtungsvektor des einfallenden
Strahls positiv ist.
Beginnen wir mit der Darstellung des einfallenden Lichtstrahls:
x = x0 + µxt ,
(3.1)
wobei x0 ein beliebig gewählter Punkt auf der Einfallslinie des Lichtstrahls
ist. Es ist sinnvoll, den Richtungsvektor xt in eine Komponente in Richtung
der Flächennormalen n
(3.2)
xt = (xt n)n
und die dazu senkrechte Komponente
xt⊥ = xt − (xt n)n
(3.3)
102
3 Optik
aufzuspalten. Dann erhalten wir den Richtungsvektor des reflektierten Strahls
als
xt = xt⊥ − xt
= x − 2xt .
(3.4)
(3.5)
Etwas komplizierter ist die Berechnung der Richtung des gebrochenen Strahls.
Zunächst müssen wir uns an das Snelliussche Brechungsgesetz erinnern:
n1 sin α1 = n2 sin α2 .
(3.6)
Dabei sind n1 und n2 die Brechungsindizes im Bereich des einfallenden bzw.
gebrochenen Lichtrahls. Aus Symmetriegründen muss der gebrochene Lichtstrahl in der Ebene verlaufen, die durch den eingehenden Lichtstrahl und den
Normalenvektor der Ebene aufgespannt wird:
x = λ1 x + λ2 n .
(3.7)
Da die in (3.6) auftretenden Terme sin α1 und sin α2 schwer zu bestimmen
sind, quadrieren wir die gesamte Gleichung, drücken die sin2 -Terme durch
den Kosinus aus und schreiben
cos α0 = xt n
cos α1 = xt n .
(3.8)
(3.9)
Wir müssen nun die zwei Faktoren λ1 und λ2 in (3.7) so berechnen, dass
das Snelliussche Brechungsgesetz (3.6) erfüllt ist:
sin2 α1
1 − cos2 α1
=
1 − cos2 α2
sin2 α2
=
1 − (xn)2
=
1 − (x n)2
(3.10)
n2
n1
2
.
(3.11)
Dabei haben wir vorausgesetzt, dass alle Richtungsvektoren normiert sind,
was zu der zusätzlichen Bedingung
λ21 + λ22 + 2λ1 λ2 xt n = 1
(3.12)
führt.
Setzen wir in (3.11) die Zerlegung (3.7), so können wir die entstehenden
Terme mit λ1 unter Verwendung der Normierungsbedingung (3.12) eliminieren und erhalten
n1
.
(3.13)
λ1 =
n2
Die negative Lösung λ1 = −n1 /n2 können wir ausschließen, da der gesuchte
Richtungsvektor in Richtung des Normalenvektors zeigen muss. Setzen wir
3.3 Brechung und Reflektion von Licht
103
diesen Ausdruck für λ1 in (3.12) ein und berücksichtigen, dass auch λ2 positiv
sein muss (siehe Abb. 3.1), so erhält man
(3.14)
λ2 = 1 − λ1 (1 − ξ 2 ) − ξλ1 .
Dabei haben wir die Abkürzung
ξ = nxt
(3.15)
eingeführt. Wenn wir den Ausdruck für λ2 betrachten, stellen wir fest, dass
dieser nicht notwendigerweise reell ist – und nur reellwertige Koeffizienten
λ1 und λ2 geben einen physikalisch sinnvollen Richtungsvektor. Wenn eine
solches reelles λ2 nicht existiert, wenn also
1 − λ1 (1 − ξ 2 ) < 0
n2
sin α12 >
n1
(3.16)
(3.17)
ist, tritt Totalreflektion ein, d.h. das gesamte Licht wird an der Grenzfläche reflektiert und kein Licht wird in das andere Medium gebrochen. Bei genügend
streifendem Einfall wird also jede Grenzfläche zwischen zwei Medien aus der
Richtung des optisch dichteren Mediums (das Material mit dem größerem
Brechungsindex) totalreflektierend – eine Tatsache, die man in Glasfasern
ausnutzt, um den Lichtstrahl in denselben zu halten.
Abschließend wollen wir uns noch der Frage zuwenden, welche Intensitäten
der reflektierte und der gebrochene Strahl haben – eine Frage, die durch die
Fresnelschen Formeln (von denen wir hier nur einen Teil benötigen) beantwortet wird:
sin2 (α1 − α2 )
sin2 (α1 + α2 )
tan2 (α1 − α2 )
R⊥ =
.
tan2 (α1 + α2 )
R =
(3.18)
(3.19)
Der hier angegebene Reflektionskoeffizient R = I2 /I0 hängt von der Polarisationsebene des Lichts ab: Bei Polarisation in der durch Einfallsrichtung und
Normalenvektor definierten Ebene gilt (3.18), in der dazu senkrechten Ebene
gilt (3.19).
Zum Abschluss dieses Abschnitts implementieren wir dessen Inhalt in
einem Unterprogramm, das ein wichtiger Bestandteil aller Programme zum
Bereich Strahlenoptik sein wird – es ist daher das Einfachste, diesen Teil
separat zu kompilieren und bei den folgenden Programmen hinzuzulinken:
1
2
3
4
5
/**************************************************************************
* Name:
refract.cpp
*
* Zweck:
Unterprogramm zur Berechnung des gebrochenen und
*
*
reflektierten Strahls
*
* Gleichung: Snelliussches Brechungsgesetz
*
104
6
3 Optik
**************************************************************************/
7
8
9
10
11
12
int refract(double x_t, double y_t, double z_t,
double n_x, double n_y, double n_z,
double* xs_t, double* ys_t, double* zs_t,
double* xss_t, double* yss_t, double* zss_t,
double I, double* Is, double* Iss, double n_0, double n_1)
13
14
15
{
double norm, xi, xi2, lambda_1, lambda_2, alpha_1, alpha_2, R_1, R_2;
16
17
// Berechnung der Richtung des reflektierten Strahls
18
19
20
21
22
23
24
25
26
xi = x_t*n_x + y_t*n_y + z_t*n_z;
*xs_t = x_t - 2.*xi* n_x;
*ys_t = y_t - 2.*xi* n_y;
*zs_t = z_t - 2.*xi* n_z;
norm = 1. / sqrt( sqr(*xs_t)+sqr(*ys_t)+sqr(*zs_t) );
*xs_t = *xs_t * norm;
*ys_t = *ys_t * norm;
*zs_t = *zs_t * norm;
27
28
29
// Berechnung der Richtung des gebrochenen Strahls
xi2 = sqr(xi);
30
31
32
lambda_1 = n_0 / n_1;
lambda_2 = sqrt( 1. - lambda_1*(1.-xi2) ) - xi*lambda_1;
33
34
35
36
37
38
39
40
*xss_t
*yss_t
*zss_t
norm =
*xss_t
*yss_t
*zss_t
= lambda_1
= lambda_1
= lambda_1
1. / sqrt(
= *xss_t *
= *yss_t *
= *zss_t *
* x_t + lambda_2 * n_x;
* y_t + lambda_2 * n_y;
* z_t + lambda_2 * n_z;
sqr(*xss_t)+sqr(*yss_t)+sqr(*zss_t) );
norm;
norm;
norm;
41
42
43
44
45
46
// Berechnung der Intensitaeten
alpha_1 = acos(xi);
alpha_2 = acos(*xss_t*n_x + *yss_t*n_y + *zss_t*n_z);
R_1 = sqr(sin(alpha_1-alpha_2)/sin(alpha_1+alpha_2));
R_2 = sqr(tan(alpha_1-alpha_2)/tan(alpha_1+alpha_2));
47
48
49
*Is = 0.5 * I * (R_1+R_2);
*Iss = I - *Is;
50
51
52
return 1;
}
Zu beachten ist, dass dieses Programm normierte Richtungsvektoren als
Eingabeparameter voraussetzt und auch normierte Richtungsvektoren für den
refelektierten und den gebrochenen Strahl liefert (siehe Zeile 20–26 und 37–
47). Auch dass der Richtungsvektor des einfallenden Strahls und der Normalenvektor der Grenzfläche nicht in entgegengesetzte Richtung zeigen, muss
man beim Aufruf von refract sicherstellen, da eine entsprechende Korrektur in der Routine selbst nicht vorgenommen wird. Der Rückgabewert von
refract ist im Fall von Totalreflektion null und ansonsten eins.
3.4 Brechung an einer Linsenfläche
105
Der Einfachheit halber haben wir angenommen, dass das einfallende Licht
unpolarisiert ist, so dass wir für den Reflektionskoeffizienten den Mittelwert
der beiden Werte aus den Fresnelschen Formeln nehmen konnten. Beachten
Sie, dass selbst unter dieser Annahme der reflektierte und gebrochene Strahl
teilweise polarisiert sind, so dass die sukzessive Anwendung von refract eigentlich nicht korrekt ist – ein Detail, das wir im Folgenden unberücksichtigt
lassen werden. Die Korrektur dieser Ungenauigkeit wäre nicht schwierig –
sie erfordert lediglich eine Auftrennung in die beiden Polarisationsebenen –
führt jedoch zu längeren und unübersichtlicheren Programmen, so dass wir
in diesem Rahmen darauf verzichtet haben. Der Leser, der jedoch ein vertieftes Interesse an der Optik hat, sollte diese Lücke durch eine entsprechende
Erweiterung des Programms durchführen.
3.4 Brechung an einer Linsenfläche
Nachdem wir im vorangegangenen Abschnitt die Grundlagen von Reflektion und Brechung besprochen haben, können wir diese jetzt benutzen, um die
Brechung von Licht an einer Linsenoberfläche zu berechnen. Wir beschränken
uns dabei auf Grenzflächen, die Teile von Kugeln sind – diese Bedingung ist
bei den meisten Linsenoberflächen erfüllt, da diese sphärischen Linsen einfacher und damit billiger herzustellen sind als die sogenannten asphärischen
Linsen.
Sie können die im Folgenden verwendeten Bezeichnungen Abb. 3.2 entnehmen, wobei wir – im Vorgriff auf die folgenden Abschnitte – uns dabei nicht
auf eine einzelne Linsenoberfläche beschränkt haben, sondern eine komplette
Linse mit einem Objektpunkt und dem dazugehörigen Bildpunkt betrachten.
Die Linse hat zwei Linsenflächen – alle Größen, die zur linken Linsenoberfläche gehören tragen den Index 1, während die entsprechenden Größen zur
rechten Linsenfläche am Index 2 erkennbar sind. Die Position der Linsenfläche auf der optischen Achse (die in z-Richtung orientiert ist) kennzeichnen
wir durch deren Scheitelpunkt z1 bzw. z2 . Die Krümmung der Linsenflächen
wird durch den Radien r1 und r2 beschrieben. Den Linsendurchmesser – der
für beide Linsenflächen im Normalfall identisch sein muss – schließlich bezeichnen wir mit 2R. Die Position des Objekt- sowie des Bildpunkts (die wir
Abb. 3.2. Schematischer Aufbau einer Linse inklusive einem Objektpunkt (links
im Bild) und dem dazugehörigen Bildpunkt (rechts)
106
3 Optik
allerdings erst in den späteren Abschnitten benötigen) charakterisieren wir
durch ihre Lage auf der optischen Achse z0 bzw. z0 sowie ihre Objekthöhe l
bzw. (d.h. den Abstand von der optischen Achse).
Bevor wir in die Rechnung einsteigen, wollen wir Ihnen noch einen kurzen Überblick über die Vorgehensweise zur Konstruktion des Strahlengangs
geben:
–
Wir beginnen mit einem Strahl, der durch einen Punkt und durch einen
Richtungsvektor gegeben ist,
– dann berechnen wir den ersten Schnittpunkt mit der Linsenoberfläche
und den Normalenvektor in diesem Punkt,
– anschließend verwenden wir die Routine des vorigen Abschnitts um die
Richtung des gebrochenen und/oder des reflektierten Strahls zu berechnen.
Beginnen wir also mit der Berechnung des Schnittpunktes von Linsenoberfäche und einfallendem Lichstrahl. Die Linsenoberfläche wird durch
x2 + y 2 + (z − zc )2 = r2
(3.20)
beschrieben, wobei zc die z-Koordinate des Kugelmittelpunktes und r der
Kugelradius ist. Der Lichtstrahl hingegen durchläuft alle Punkte entlang der
Bahn
(3.21)
x = x0 + µxt .
Setzen wir dies in (3.20) ein, erhalten wir eine quadratische Bestimmungsgleichung für µ und damit Kandidaten für potentielle Punkte, bei denen der
Lichtstrahl in die Linse eindringt:
(x0 + µxt )2 + (y0 + µyt )2 + (z0 + µzt − zc )2 = r2 .
(3.22)
Wir multiplizieren aus, fassen die Terme gleicher Ordnung in µ zusammen
und erhalten so eine quadratische Gleichung in der Standardform
µ2 + 2aµ + b = 0
(3.23)
mit
a = x0 xt + y0 yt + (z0 − zc )zt
b=
x20
+
y02
+
z02
+
zc2
(3.24)
− 2z0 zc − r .
2
(3.25)
Hat diese quadratische Gleichung keine Lösung, verfehlt der Lichtstrahl
die Linse, aber auch wenn es eine reelle Lösung gibt, ist nicht gesagt, dass
der Lichtstrahl tatsächlich die Linse trifft – dazu müssen noch zwei weitere
Bedingungen erfüllt sein:
–
Das gefundene µ muss positiv sein, ansonsten hat der Lichtstrahl nämlich
die Linse verlassen, bevor er unseren Objektpunkt durchlaufen hat.
3.4 Brechung an einer Linsenfläche
–
107
Der Auftreffpunkt muss zwischen dem linken (zl ) und dem rechten Rand
der Linse (zr ) liegen.
Die Lösung der quadratischen Gleichung (3.23) lautet:
µ1,2 = −a ± a2 − b .
(3.26)
Durch Betrachten der Determinante a2 − b können wir entscheiden, ob überhaupt reelle Lösungen existieren, und wenn ja, entscheiden wir anhand der
beiden gewonnenen Lösungen, welche davon die relavante ist. Den so gewonnenen Eintrittspunkt bezeichnen wir im Weiteren mit x1 = (x1 , y1 , z1 ).
Um dies zu überprüfen, bauen wir das daraus resultierende Unterprogramm gleich in ein kleines Testprogramm ein:
1
2
3
4
5
6
/**************************************************************************
* Name:
strahl1.cpp
*
* Zweck:
Brechung an einer Linsenoberflaeche
*
* Gleichung: Snelliussches Brechungsgesetz
*
* Methode:
Konstruktion von Lichtstrahlen
*
**************************************************************************/
7
8
9
10
11
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
"tools.h"
12
13
using namespace std;
14
15
#include "refract.cpp"
16
17
//-------------------------------------------------------------------------
18
19
20
21
22
23
int hit(double x_0, double y_0, double z_0,
double x_t, double y_t, double z_t,
double* x_1, double* y_1, double* z_1,
double* n_x, double* n_y, double* n_z,
double r, double z_l, double z_r, double z_c)
24
25
26
27
28
{
double t_1, t_2, a, b, d, diskri;
int
ret_val;
29
30
ret_val = 0;
31
32
33
a = x_0*x_t + y_0*y_t + (z_0-z_c)*z_t;
b = sqr(x_0) + sqr(y_0) + sqr(z_0) + sqr(z_c) - 2*z_0*z_c - sqr(r);
34
35
36
diskri = sqr(a) - b;
if (diskri < 0) return 0;
37
38
39
t_1 = -a + sqrt(diskri);
t_2 = -a - sqrt(diskri);
40
41
42
43
if (t_2 > 0)
{
*z_1 = z_0 + t_2 * z_t;
108
3 Optik
if ((*z_1 >= z_l) && (*z_1 <= z_r))
{
ret_val = 1;
*x_1 = x_0 + t_2 * x_t;
*y_1 = y_0 + t_2 * y_t;
}
else return 0;
}
else if (t_1 > 0)
{
*z_1 = z_0 + t_1 * z_t;
if ((*z_1 > z_l) && (*z_1 < z_r))
{
ret_val = 1;
*x_1 = x_0 + t_1 * x_t;
*y_1 = y_0 + t_1 * y_t;
}
else return 0;
}
else return 0;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
d = sqrt(sqr(*x_1) + sqr(*y_1) + sqr(z_c-*z_1));
65
66
*n_x = -*x_1 / d;
*n_y = -*y_1 / d;
*n_z = (z_c - *z_1) / d;
67
68
69
70
if (r < 0)
{
*n_x = -*n_x;
*n_y = -*n_y;
*n_z = -*n_z;
}
71
72
73
74
75
76
77
return ret_val;
78
79
}
80
81
//-------------------------------------------------------------------------
82
83
int main( int argc, char *argv[] )
84
85
86
87
88
89
90
91
{
int
n, hit_result;
double x0, y0, z0, x1, y1, z1, xs_t, ys_t, zs_t, xss_t, yss_t, zss_t;
double Is, Iss, z_l, z_r, z_c, z_1, R, n_x, n_y, n_z, r1, length;
double n0, n1, phi_start, phi_end;
ifstream in_stream;
ofstream out_stream;
92
93
94
95
96
97
98
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: strahl1 infile outfile\n";
return 0;
}
99
100
101
102
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
3.4 Brechung an einer Linsenfläche
103
104
105
106
107
109
out_stream << "! GLE-Datei generiert von strahl1.cpp\n";
inout(z_1,"z_1");
inout(R,"R");
inout(r1,"r1");
inout(n0,"n0");
inout(n1,"n1");
in_stream.close();
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
if (r1 > 0)
{
z_l = z_1;
z_c = z_l +
z_r = z_c phi_start =
phi_end
=
}
else
{
z_r = z_1;
z_c = z_r +
z_l = z_c phi_start =
phi_end
=
}
r1;
r1 * cos(asin(R/r1));
180 * acos((z_r-z_c)/r1)/pi;
180 * (2*pi-acos((z_r-z_c)/r1))/pi;
r1;
r1 * cos(asin(R/r1));
-180 * acos(-(z_l-z_c)/r1)/pi;
+180 * acos(-(z_l-z_c)/r1)/pi;
125
126
y0 = 0;
z0 = 0;
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
out_stream
out_stream
out_stream
out_stream
out_stream
<< "size 22*0.2 10*0.5\n";
<< "set lwidth 0.1\n";
<< "begin scale 0.2 0.2\n";
<< "amove " << 1+z_c << " " << 12 << "\n";
<< "arc " << fabs(r1) << " " << phi_start
<< " " << phi_end << "\n";
for (n = -10; n <= 10; n++)
{
x0 = n;
out_stream << "amove 1 " << x0+12 << "\n";
hit_result = hit(x0,y0,z0,0,0,1,&x1,&y1,&z1,&n_x,&n_y,
&n_z,r1,z_l,z_r,z_c);
if (hit_result == 1)
{
refract(0,0,1,n_x,n_y,n_z,&xs_t,&ys_t,&zs_t,&xss_t,
&yss_t,&zss_t,1,&Is,&Iss,n0,n1);
length = min( (20-z1) / zss_t, fabs((fabs(x1)+10)/xss_t) );
out_stream << "aline " << z1+1 << " " << x1+12 << "\n";
out_stream << "rline " << length*zss_t << " " << length*xss_t << "\n";
}
else
out_stream << "rline 20 0\n";
}
out_stream << "end scale\n";
out_stream.close();
}
Zunächst eine Bemerkung zur Funktion hit, die den Auftreffpunkt auf
die Linsenoberfläche und den zugehörigen Normalenvektor berechnet. Die
Funktion ist eins, wenn die Oberfläche getroffen wird und ansonsten null.
Wenn sie den Wert eins hat liefert sie in den Variablen x 1, y 1 und z 1
die Koordinaten des Auftreffpunktes und in den Variablen n x bis n z die
Komponenten des Normalenvektors, der bereits in Richtung des einfallenden
110
3 Optik
Lichtstrahls normiert ist (Zeile 67–76). Da wir uns an dieser Stelle lediglich
für den geometrischen Aspekt der Strahlverläufe interessieren, sind die Intensitäten irrelevant. Das Testprogramm berechnet die Strahlverläufe für einen
Satz parallel einlaufender Strahlen und schreibt die Ergebnisse in ein GLEFile strahl1.gle, so dass wir uns diese Strahlverläufe direkt anschauen und
entscheiden können, ob wir mit dem Ergebnis zufrieden sind. Typische Bilder
für n0 < n1 bzw n0 > n1 sehen etwa so aus:
Abb. 3.3. Strahlverlauf beim Übergang vom optisch dünneren ins optisch dichtere
Medium (a) und umgekehrt (b)
Nachdem wir uns davon überzeugt haben, dass die Unterprogramme
refract und hit funktionieren und vernünftige Ergebnisse liefern, ist der
nächste Schritt verhältnismäßig einfach: wir müssen nach dem Eintritt in die
Linse noch den Austritt aus der Linse berechnen, wozu wir – abgesehen von
Deklaration und Einlesen einiger neuer Parameter – nur den letzten Teil des
Programms ändern müssen:
...
153
154
155
156
157
158
159
160
161
162
out_stream
out_stream
out_stream
out_stream
out_stream
<<
<<
<<
<<
<<
<<
out_stream <<
out_stream <<
<<
"size 27*0.2 10*0.5\n";
"set lwidth 0.1\n";
"begin scale 0.2 0.2\n";
"amove " << 1+z1_c << " "
"arc " << fabs(r1) << " "
" " << phi1_end << "\n";
"amove " << 1+z2_c << " "
"arc " << fabs(r2) << " "
" " << phi2_end << "\n";
<< 12 << "\n";
<< phi1_start
<< 12 << "\n";
<< phi2_start
163
164
165
166
167
168
169
170
171
172
173
174
175
176
for (n = -10; n <= 10; n++)
{
x0 = n+0.01;
out_stream << "amove 1 " << x0+12 << "\n";
hit_result = hit(x0,y0,z0,0,0,1,&xa,&ya,&za,&n_x,&n_y,
&n_z,r1,z1_l,z1_r,z1_c);
if (hit_result == 1)
{
refr_result = refract(x_t,y_t,z_t,n_x,n_y,n_z,
&dummy_1,&dummy_2,&dummy_3,
&xs_t,&ys_t,&zs_t,1.,&dummy_4,&Is,n0,n1);
hit_result = hit(xa,ya,za,xs_t,ys_t,zs_t,&xb,&yb,&zb,&n_x,&n_y,&n_z,
r2,z2_l,z2_r,z2_c);
3.5 Bild durch eine Linse – Linsenfehler
111
refr_result = refract(xs_t,ys_t,zs_t,n_x,n_y,n_z,
&dummy_1,&dummy_2,&dummy_3,
&xss_t,&yss_t,&zss_t,Is,&dummy_4,&Iss,n1,n0);
out_stream << "aline " << za+1 << " " << xa+12 << "\n";
out_stream << "aline " << zb+1 << " " << xb+12 << "\n";
length = min( (25-zb) / zss_t, fabs((fabs(xb)+10)/xss_t) );
out_stream << "rline " << length*zss_t << " " << length*xss_t
<< "\n";
}
else
out_stream << "rline 25 0\n";
177
178
179
180
181
182
183
184
185
186
187
}
out_stream << "end scale\n";
out_stream.close();
188
189
190
191
192
}
Das geänderte Programm finden Sie als strahl2.cpp bzw. strahl2.f auf
der CD-ROM. Mit dessen Hilfe erhalten wir nun den typischen Strahlengang
durch eine Linse in Abb. 3.4:
Abb. 3.4. Strahlverlauf beim Durchgang durch eine sphärische Linse
Bei genauem Hinsehen stellen Sie fest, dass sich die Strahlen nicht alle in
einem Punkt – dem Brennpunkt – kreuzen, wie dies für eine perfekte Linse der
Fall sein sollte. Vielmehr schneiden die achsfernen Strahlen die optische Achse
näher an der Linse als dies bei den achsnahen Strahlen der Fall ist. Dieser
Linsenfehler rührt daher, dass die Linsenoberflächen Sphären – also Teile von
Kugeln – sind, was nicht der idealen Oberflächenform entspricht. Aus diesem
Grund bezeichnet man diesen Linsenfehler als sphärische Aberration.
3.5 Bild durch eine Linse – Linsenfehler
Nach diesem Testprogramm können wir davon ausgehen, dass das Unterprogramm refract korrekte Ergebnisse liefert. Dieses werden wir für unsere
nächste Aufgabe – der Konstruktion des Bildes eines Objektpunkts in endlicher Entfernung von der Linse – weiterverwenden. Bevor wir uns dem Umbau
unseres bisherigen Programms zuwenden können, müssen wir uns zunächst
überlegen, wie die Lichtintensität an einem Punkt in der Bildebene zustande
kommt und wie sie demzufolge zu berechnen ist
112
3 Optik
In der Strahlenoptik sendet die Lichtquelle Licht in den Raum hinaus, wobei bei einer isoptropen Lichtquelle die Intensität des ausgestrahlten Lichts
unabhängig von der Richtung ist. In diesem Falle ist der Energiefluss in einen
Raumwinkel dΩ = sin(θ)dθdφ proportional zum Raumwinkel selbst. Im freien Raum ist der Strahlverlauf aller Strahlen in einem solchen Raumwinkel
sehr einfach und man erkennt sofort, dass die Fläche, die dieses Raumwinkelelement aus einer Kugelfläche im Abstand r ausschneidet, proportional
zu r2 ist. Auf diese Fläche verteilt sich nun die Energie, die von der Lichtquelle ausgestrahlt wurde, was bedeutet, dass die Intensität einer Lichtquelle proportional zum Quadrat des Abstands von ihr abnimmt. Eine optische
Vorrichtung wie z.B. eine Linse verändert nun den weiteren Strahlverlauf und
führt dazu, dass die Fläche auf der Bildebene, auf der sich das Winkelelement
verteilt, eine völlig andere ist.
Was müssen wir also tun, um die Intensität an einem bestimmten Punkt
(x1 ,y1 ) in der Bildebene zu bestimmen? Eine einfache Möglichkeit ist es, eine Reihe von Lichtstrahlen, die vom Punkt in der Objektebene ausgehen,
zu betrachten und ihren Strahlverlauf zu berechnen. Jeder dieser Lichtstrahlen trägt zur Intensitätsverteilung in der Bildebene bei, und entsprechend
summieren wir diese Einzelintensitäten auf:
1
2
3
4
5
6
/**************************************************************************
* Name:
strahl3.cpp
*
* Zweck:
Brechung an zwei Linsenoberflaechen
*
* Gleichung: Snelliussches Brechungsgesetz
*
* Methode:
Konstruktion von Lichtstrahlen
*
**************************************************************************/
7
8
9
10
11
12
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_matrix.h>
"tools.h"
13
14
using namespace std;
15
16
//-- globale Variablen
17
18
double r1, r2, n0, n1, z1_l, z1_r, z1_c, z2_l, z2_r, z2_c, Iss;
19
20
#include "refract.cpp"
21
22
//-------------------------------------------------------------------------
23
24
25
26
27
28
int hit(double x_0, double y_0, double z_0,
double r_x, double r_y, double r_z,
double* x_1, double* y_1, double* z_1,
double* n_x, double* n_y, double* n_z,
double r, double z_l, double z_r, double z_c)
29
30
31
32
33
{
int
hit_result;
double t_1, t_2, a, b, diskri, xi, d;
3.5 Bild durch eine Linse – Linsenfehler
34
113
hit_result = 0;
35
36
37
a = x_0*r_x + y_0*r_y + (z_0-z_c)*r_z;
b = sqr(x_0) + sqr(y_0) + sqr(z_0) + sqr(z_c) - 2*z_0*z_c - sqr(r);
38
39
40
diskri = sqr(a) - b;
if (diskri < 0) return 0;
41
42
43
t_1 = -a + sqrt(diskri);
t_2 = -a - sqrt(diskri);
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
if (t_2 > 0)
{
*z_1 = z_0 + t_2 * r_z;
if ((*z_1 >= z_l) && (*z_1 <= z_r))
{
hit_result = 1;
*x_1 = x_0 + t_2 * r_x;
*y_1 = y_0 + t_2 * r_y;
}
else return 0;
}
else if (t_1 > 0)
{
*z_1 = z_0 + t_1 * r_z;
if ((*z_1 >= z_l) && (*z_1 <= z_r))
{
hit_result = 1;
*x_1 = x_0 + t_1 * r_x;
*y_1 = y_0 + t_1 * r_y;
}
else return 0;
}
else return 0;
69
d = sqrt(sqr(*x_1) + sqr(*y_1) + sqr(z_c-*z_1));
70
71
*n_x = -*x_1 / d;
*n_y = -*y_1 / d;
*n_z = (z_c - *z_1) / d;
72
73
74
75
xi = (*n_x)*r_x + (*n_y)*r_y + (*n_z)*r_z;
if (xi < 0)
{
*n_x = -(*n_x);
*n_y = -(*n_y);
*n_z = -(*n_z);
}
76
77
78
79
80
81
82
83
return hit_result;
84
85
}
86
87
//-------------------------------------------------------------------------
88
89
int main( int argc, char *argv[] )
90
91
92
{
double x0, y0, z0, z1, x_t, y_t, z_t, z1_1, z2_1, R, Is, Iss, I_max, I;
114
93
94
95
96
97
98
99
100
3 Optik
double xs_t, ys_t, zs_t, x1s, y1s, xss_t, yss_t, zss_t;
double dummy_1, dummy_2, dummy_3, dummy_4, n_x, n_y, n_z;
double theta, theta_max, dtheta, phi, phi_max, dphi;
double xmin, xmax, dx, ymin, ymax, dy, xa, ya, za, xb, yb, zb, t;
ifstream in_stream;
ofstream out_stream;
int hit_result, refr_result, nx, nx_max, ny, ny_max;
int n_phi, n_phi_max, n_theta, n_theta_max;
101
102
103
104
105
106
107
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: strahl3 infile outfile\n";
exit(1);
}
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von strahl3.cpp\n";
inout(z1_1,"z1_1");
inout(r1,"r1");
inout(z2_1,"z2_1");
inout(r2,"r2");
inout(R,"R");
inout(n0,"n0");
inout(n1,"n1");
inout(xmin,"xmin");
inout(xmax,"xmax");
inout(ymin,"ymin");
inout(ymax,"ymax");
inout(nx_max,"nx_max");
inout(ny_max,"ny_max");
inout(phi_max,"phi_max");
inout(theta_max,"theta_max");
inout(n_phi_max,"n_phi_max");
inout(n_theta_max,"n_theta_max"); inout(x0,"x0");
inout(y0,"y0");
inout(z0,"z0");
inout(z1,"z1");
out_stream << "! Spalte 1: x
Spalte 2: y
Spalte 3: I(x,y)\n";
in_stream.close();
125
126
gsl_matrix *I_ges = gsl_matrix_alloc(nx_max,ny_max);
127
128
129
dx = (xmax-xmin) / nx_max;
dy = (ymax-ymin) / ny_max;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//-- Berechnung einiger benoetigter Parameter
dphi
= phi_max / n_phi_max;
dtheta = (theta_max-pi/2.) / n_theta_max;
if (r1 > 0)
{
z1_l = z1_1;
z1_c = z1_l + r1;
z1_r = z1_c - r1 * cos(asin(R/r1));
}
else
{
z1_r = z1_1;
z1_c = z1_r + r1;
z1_l = z1_c - r1 * cos(asin(R/r1));
}
146
147
148
149
150
151
if (r2
{
z2_l
z2_c
z2_r
> 0)
= z2_1;
= z2_l + r2;
= z2_c - r2 * cos(asin(R/r2));
3.5 Bild durch eine Linse – Linsenfehler
152
153
154
155
156
157
158
115
}
else
{
z2_r = z2_1;
z2_c = z2_r + r2;
z2_l = z2_c - r2 * cos(asin(R/r2));
}
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//-- Intensitaeten in der Bildebene bestimmen
for (n_phi = -n_phi_max; n_phi<=n_phi_max; n_phi++)
{
if ((n_phi % 100) == 0) cout << n_phi << " von " << n_phi_max << "\n";
for (n_theta = -n_theta_max; n_theta<=n_theta_max; n_theta++)
{
phi = n_phi * dphi;
theta = n_theta * dtheta + pi/2.;
I = cos(theta);
x_t = cos(phi) * cos(theta);
y_t = sin(phi) * cos(theta);
z_t = sin(theta);
hit_result = hit(x0,y0,z0,x_t,y_t,z_t,&xa,&ya,&za,
&n_x,&n_y,&n_z,r1,z1_l,z1_r,z1_c);
if (hit_result == 1)
{
refr_result = refract(x_t,y_t,z_t,n_x,n_y,n_z,&dummy_1,&dummy_2,
&dummy_3,&xs_t,&ys_t,&zs_t,1.,&dummy_4,&Is,n0,n1);
hit_result = hit(xa,ya,za,xs_t,ys_t,zs_t,&xb,&yb,&zb,
&n_x,&n_y,&n_z,r2,z2_l,z2_r,z2_c);
refr_result = refract(xs_t,ys_t,zs_t,n_x,n_y,n_z,&dummy_1,&dummy_2,
&dummy_3,&xss_t,&yss_t,&zss_t,Is,&dummy_4,&Iss,n1,n0);
t = (z1-zb) / zss_t;
x1s = xb + t * xss_t;
y1s = yb + t * yss_t;
nx = int((x1s-xmin)/dx);
ny = int((y1s-ymin)/dy);
if ((nx>=0) && (nx<nx_max) && (ny>=0) && (ny<ny_max))
gsl_matrix_set(I_ges,nx,ny,gsl_matrix_get(I_ges,nx,ny)+Iss);
}
}
}
192
193
194
195
196
197
198
199
200
201
//-- Intensitaeten in der Bildebene abspeichern und Maximum bestimmen
I_max = 0;
for (nx = 0; nx<nx_max; nx++)
for (ny = 0; ny<ny_max; ny++)
{
I = gsl_matrix_get(I_ges,nx,ny);
out_stream << nx << " " << ny << " " << I << "\n";
if (I > I_max) I_max = I;
}
202
203
204
205
out_stream << "! Max. Intensity = " << I_max <<"\n";
out_stream.close();
}
Die beiden Unterprogramme refract und hit konnten wir ungeändert
übernehmen, so dass wir diese nicht erneut besprechen müssen. Der Rest des
Programms ist verhältnismäßig einfach: eine große Zahl von Strahlen wird
116
3 Optik
ausgehend vom Objektpunkt verfolgt und trägt zur Intensitätsverteilung in
der Bildebene bei.
Um Abb. 3.5 zu erzeugen haben wir Parameter gewählt, die für eine gute
Abbildungsleistung ziemlich ungünstig sind:
– einen kleinen Abstand des Objektes von der Linse,
– eine große Linse,
– einen Objektpunkt weit weg von der optischen Achse.
Abb. 3.5. Intensitätsverteilung in der Bildebene durch den Linsenfehler Koma.
Die Intensität des korrekten Lichtpunktes (rechts im Bild) wurde reduziert, um
den Linsenfehler deutlicher hervortreten zu lassen
Sie sehen, dass ein Lichtpunkt – wie erwartet – durch die Linsenfehler
nicht auf einen Punkt in der Bildebene abgebildet wird. Hingegen ist vielleicht die Tatsache überraschend, dass wir nicht einfach einen diffusen Fleck
erhalten, sondern ein Gebilde, das durchaus eine gewisse Ästhetik ausstrahlt.
Der Hauptteil des Lichtes konzentriert sich in dem Lichtpunkt auf der rechten
Seite, das Streulicht verteilt sich in dem ’Schweif’ nach links, wobei hier insbesondere der begrenzende Rand ganz links und der Punkt genau in der Mitte
ausgeleuchtet wird. Dieser Linsenfehler kommt vor allem daher, dass unser
Objektpunkt sehr weit von der optischen Achse entfernt gewählt wurde und
dass wir eine große Linse betrachten. Aufgrund seiner optischen Erscheinung
wird dieser Linsenfehler Koma genannt. Andere Linsenfehler lassen sich mit
dem Programm durch Variation der Parameter (siehe Übungsaufgabe 3.1)
erzeugen.
3.6 Entstehung eines Regenbogens
Die vielleicht eindrucksvollste Demonstration der spektralen Zerlegung von
weißem Licht in der Natur ist der Regenbogen. Man sieht ihn, wenn hinter
3.6 Entstehung eines Regenbogens
117
einem die Sonne scheint und es gleichzeitig vor einem regnet. Unter besonders günstigen Bedingungen sieht man sogar zwei Regenbögen, wobei der
äußere schwächer ist und die Reihenfolge der Spektralfarben gegenüber dem
äußeren invertiert ist. Hier wollen wir zunächst qualitativ erklären, wie es
zum Regenbogen kommt, und dann das Problem numerisch angehen, wobei
die entstehende Theorie in der Lage sein sollte, sowohl die Existenz mehrerer Regenbögen zu erklären als auch Größe und Reihenfolge der Farben
vorherzusagen.
3.6.1 Qualitative Erklärung des Regenbogens
Das Licht, das von der Sonne die Erde erreicht, trifft auf einen Regentropfen,
den wir zunächst einmal kugelförmig annehmen. Am Auftrittspunkt A (siehe
Abb 3.6) an der Grenzfläche Luft–Wasser wird ein Teil dieses Lichts reflektiert, während der Rest in den Wassertropfen eintritt. Dieser Rest trifft nach
Durchqueren des Wassertropfens bei B auf die Grenzfläche Wasser–Luft, wo
wieder ein Teil austritt, während der Rest reflektiert wird und also im Wassertropfen verbleibt und ihn abermals durchquert. Danach tritt bei C wiederum
ein Teil aus, während der übrige Teil reflektiert wird – und immer so weiter.
Abb. 3.6. Strahlverlauf beim Durchgang durch einen Wassertropfen. Dargestellt
ist der Strahlverlauf, der für den Hauptregenbogen verantwortlich ist
Es gibt also eine Vielzahl möglicher Wege, die zum einen durch die Zahl
der Reflektionen an der Grenzschicht Wasser–Luft gekennzeichnet sind, zum
anderen werden diese Wege aber auch von dem Winkel α zwischen der
usprünglichen Richtung des Sonnenstrahls und der Verbindungslinie des Wassermittelpunkts und dem ersten Auftrittspunkt A bestimmt. Interessant ist
nun, in welche Richtungen besonders viel Licht vom Regentropfen gestreut
wird.
Um dies zu verstehen, betrachten wir Abb. 3.7, bei der wir statt einem
Lichtstrahl eine ganze Reihe parallel einfallender Lichtstrahlen betrachten,
allerdings haben wir zwecks Übersichtlichkeit auf den auslaufenden Teil jedes
Lichtstrahls nach einer Reflektion beschränkt und alle anderen Teile weggelassen. Wir sehen, dass das ganze Licht mehr oder weniger zurückgestreut
wird, nichts wird in Vorwärtsrichtung gestreut. Wir bezeichnen den Winkel
118
3 Optik
Abb. 3.7. Richtung des rückgestreuten Lichts nach Passieren eines Regentropfens
(bei einfacher Reflektion im Regentropfen). Der einfallende Lichtstrahl kommt wie
in Abb. 3.6 von links
zwischen einfallendem Licht und rückgestreutem Licht mit α. Dieser variiert zwischen einem minimalen Ablenkungswinkel −αmax und einem Maximum αmax . Wir werden sehen, dass diese Extrema die Richtungen sind, in
die besonders viel Licht gestreut wird. Nicht berücksichtigen werden wir die
Interferenz von Strahlen, die in die gleiche Richtung gestreut werden – ein
Effekt, der in der Wirklichkeit zu einer, wenn auch geringen Abhängigkeit der
genauen Struktur des Regenbogens von der Größe der Regentropfen führt.
3.6.2 Quantititave Vorüberlegungen
Wie wir den gerade beschriebenen Strahlverläufen entnehmen, treten nur
zwei Elementarprozesse auf: Brechung (jeweils beim Ein- und Austritt) und
Reflektion. Wenn wir ein Unterprogramm haben, das für einen beliebigen
Lichtstrahl den nächsten Auftreffpunkt auf die Grenzfläche Wasser–Luft berechnet und sowohl die Intensität als auch die Richtung des reflektierten
und des gebrochenen Strahls ausgibt, können wir den gesamten Strahlverlauf
durch wiederholte Aufrufe dieses Unterprogramms erhalten.
An dieser Stelle wollen wir die ursprüngliche Annahme eines kugelförmigen Wassertropfens durch die vielleicht wirklichkeitsgetreuere Beschreibung
durch ein Rotationsellipsoid ersetzen. Wenn wir den Wassertropfen in den
Ursprung des Koordinatensystems setzen, wird seine Oberfläche durch die
Gleichung
x2
y2
z2
+ 2 + 2 =1
(3.27)
2
a
a
b
beschrieben. Hierbei sind a bzw. b die Radien des Wassertropfens in x- und
y- bzw. in z-Richtung. Alternativ können wir auch eine Parameterdarstellung
durch zwei Winkel ϑ und φ wählen:
⎛ ⎞ ⎛
⎞
x
a cos φ sin ϑ
⎝ y ⎠ = ⎝ a sin φ sin ϑ ⎠ .
(3.28)
z
b cos ϑ
3.6 Entstehung eines Regenbogens
Wenn wir den Lichtstrahl in der Parameterdarstellung
⎛ ⎞ ⎛ ⎞
⎛ ⎞
x
x0
xt
⎝ y ⎠ = ⎝ y0 ⎠ + α ⎝ yt ⎠ .
z
z0
zt
119
(3.29)
gegeben haben, erhalten wir eventuelle Schnittpunkte mit der Oberfläche des
Wassertropfens durch Lösen der quadratischen Gleichung
(x0 + αxt )2 + (y0 + αyt )2 + β 2 (z0 + αzt )2 = a2 .
(3.30)
Hierbei haben wir den Parameter β = a/b eingeführt, der für einen kugelförmigen Wassertropfen den Wert eins annimmt. Diese quadratische Gleichung hat entweder
–
–
–
keine Lösung, wenn der Lichtstrahl den Wassertropfen verfehlt,
genau eine Lösung, wenn der Lichtstrahl den Wassertropfen berührt,
oder zwei Lösungen, wenn der Lichtstrahl durch den Wassertropfen durchgeht.
Die Situation, die uns im Zusammenhang mit dem Regenbogen natürlich am
meisten interessiert, ist die letztgenannte, bei der der Lichtstrahl eine Chance
hat, in den Wassertropfen einzudringen.
Wir führen die Abkürzungen
A = x20 + y02 + β 2 z02 − a2
2
B = x0 xt + y0 yt + β z0 zt
C = x2t + yt2 + β 2 zt2
(3.31)
(3.32)
(3.33)
ein, wonach wir die Lösung des quadratischen Gleichungssystems verhältnismäßig leicht hinschreiben können:
√
B ± B 2 − AC
.
(3.34)
α1,2 =
A
Nachdem wir den Schnittpunkt des Lichtstrahls mit der Oberfläche des
Wassertropfens ermittelt haben, benötigen wir nun den Normalenvektor zu
dieser Oberfläche in diesem Punkt. Dazu gehen wir in die Parameterdarstellung (3.28), wo wir durch Differentiation nach φ bzw. ϑ die (nicht normierten)
Tangentialvektoren t1 und t2 an die Wasseroberfläche erhalten:
⎛
⎞ ⎛
⎞
a cos φ sin ϑ
a cos φ cos ϑ
∂ ⎝
a sin φ sin ϑ ⎠ = ⎝ a sin φ cos ϑ ⎠
(3.35)
t1 =
∂φ
b cos ϑ
−b sin ϑ
⎛
⎞ ⎛
⎞
a cos φ sin ϑ
−a sin φ sin ϑ
∂ ⎝
a sin φ sin ϑ ⎠ = ⎝ a cos φ sin ϑ ⎠ .
t2 =
(3.36)
∂ϑ
b cos ϑ
0
120
3 Optik
Durch Bildung des Vektorprodukts erhalten wir daraus den gesuchten (immer
noch nicht normierten) Normalenvektor:
⎞
⎛
ab cos φ sin2 ϑ
(3.37)
n = t1 × t2 = ⎝ ab sin φ sin2 ϑ ⎠ .
a2 sin ϑ cos ϑ
Der Faktor sin θ ist allen drei Komponenten gemein, so dass wir diesen ohne
Änderung der Richtung des Vektors n streichen können, ebenso extrahieren
wir den Faktor ab, wonach
⎛ ⎞
x
n=⎝ y ⎠
(3.38)
βz
verbleibt.
3.6.3 Programm zur Berechnung eines Regenbogens
Das Programm, das nun die Quintessenz der vorangegangen Überlegungen
darstellt, ist gar nicht so verschieden von den Programmen zur Berechnung
des Strahlengangs durch eine Linse und zur Konstruktion eines Punktes in
der Objektebene, wie wir sie im vorhergehenden Abschnitt kennengelernt
haben.
1
2
3
4
5
6
/**************************************************************************
* Name:
regen1.cpp
*
* Zweck:
Beschreibung eines Regenbogens
*
* Gleichung: Snelliussches Brechungsgesetz, Fresnelsche Formeln
*
* Methode:
Konstruktion von Lichtstrahlen
*
**************************************************************************/
7
8
9
10
11
12
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_matrix.h>
<math.h>
"tools.h"
13
14
using namespace std;
15
16
17
const int nmax = 10;
double r1, r2;
18
19
#include "refract.cpp"
20
21
//-------------------------------------------------------------------------
22
23
24
25
26
int hit(double x_0, double y_0, double z_0,
double x_t, double y_t, double z_t,
double* x_1, double* y_1, double* z_1,
double* n_x, double* n_y, double* n_z)
27
28
29
{
int
hit_result;
3.6 Entstehung eines Regenbogens
30
121
double t_1, t_2, diskri, eps, A, B, C, beta, xi;
31
32
33
hit_result = 0;
eps = 1.e-8;
34
35
36
37
38
39
// Berechnung des Eintrittspunktes
beta = r2/r1;
A = sqr(x_t) + sqr(y_t) + sqr(beta*z_t);
B = x_0*x_t + y_0*y_t + sqr(beta)*(z_0*z_t);
C = sqr(x_0) + sqr(y_0) + sqr(beta*z_0) - sqr(r1);
40
41
42
diskri = sqr(B) - A*C;
if (diskri < 0) return 0;
43
44
45
t_1 = (-B + sqrt(diskri)) / A;
t_2 = (-B - sqrt(diskri)) / A;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
if (t_2 > eps)
{
hit_result =
*x_1 = x_0 +
*y_1 = y_0 +
*z_1 = z_0 +
}
else if (t_1
{
hit_result
*x_1 = x_0
*y_1 = y_0
*z_1 = z_0
}
else return
1;
t_2 * x_t;
t_2 * y_t;
t_2 * z_t;
> eps)
=
+
+
+
1;
t_1 * x_t;
t_1 * y_t;
t_1 * z_t;
0;
62
63
64
65
66
67
68
69
70
71
72
73
// Berechnung des Normalenvektors
*n_x = *x_1;
*n_y = *y_1;
*n_z = beta * (*z_1);
xi = x_t*(*n_x) + y_t*(*n_y) + z_t*(*n_z);
if (xi<0)
{
*n_x = - (*n_x);
*n_y = - (*n_y);
*n_z = - (*n_z);
}
74
75
76
return hit_result;
}
77
78
//------------------------------------------------------------------------
79
80
int main( int argc, char *argv[] )
81
82
83
84
85
86
87
88
{
int
n_1, n_2, ny, nz, hit_result, n_refl, n_refl_max;
int
m_1, m_2, my, mz, m1start, m1stop, m2start, m2stop;
double Intensity[nmax], Intensity_max, angle_max;
double x[nmax], y[nmax], z[nmax], rx[nmax], ry[nmax], rz[nmax];
double y_min, y_max, z_min, z_max, n0, n1, n_x, n_y, n_z;
ifstream in_stream;
122
89
3 Optik
ofstream out_stream;
90
91
92
93
94
95
96
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: regen1 infile outfile\n";
return 0;
}
97
98
99
100
101
102
103
104
105
106
107
108
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
in_stream >> x[0];
in_stream
in_stream >> nz;
in_stream
in_stream >> y_max;
in_stream
in_stream >> z_max;
in_stream
in_stream >> mz;
in_stream
in_stream >> r1;
in_stream
in_stream >> n0;
in_stream
in_stream.close();
>>
>>
>>
>>
>>
>>
>>
ny;
y_min;
z_min;
my;
n_refl_max;
r2;
n1;
109
110
111
112
gsl_matrix *Intensity_result = gsl_matrix_alloc(my, mz);
Intensity_max = 0;
angle_max = 0;
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
for (n_1 = 0; n_1<=ny; n_1++)
{
if (n_1 % 100 == 0) cout << n_1 << " von " << ny << "\n";
for (n_2 = 0; n_2<=nz; n_2++)
{
y[0] = y_min + (y_max-y_min) * (n_1) / (ny-1);
z[0] = z_min + (z_max-z_min) * (n_2) / (nz-1);
Intensity[0] = 1;
hit_result = hit(x[0],y[0],z[0],1.,0.,0.,
&(x[1]),&(y[1]),&(z[1]),&n_x,&n_y,&n_z);
if (hit_result == 1)
{
hit_result = refract(1.,0.,0.,n_x,n_y,n_z,
&(rx[1]),&(ry[1]),&(rz[1]), &(rx[2]),&(ry[2]),&(rz[2]),
Intensity[0],&(Intensity[1]),&(Intensity[2]),n0,n1);
for (n_refl=1; n_refl<=n_refl_max+1; n_refl++)
{
hit_result = hit(x[n_refl],y[n_refl],z[n_refl],
rx[n_refl*2],ry[n_refl*2],rz[n_refl*2],&(x[n_refl+1]),
&(y[n_refl+1]),&(z[n_refl+1]),&n_x,&n_y,&n_z);
hit_result = refract(rx[n_refl*2],ry[n_refl*2],rz[n_refl*2],
n_x,n_y,n_z,&(rx[n_refl*2+2]),&(ry[n_refl*2+2]),
&(rz[n_refl*2+2]),&(rx[n_refl*2+1]),&(ry[n_refl*2+1]),
&(rz[n_refl*2+1]),Intensity[n_refl*2],
&(Intensity[n_refl*2+2]),&(Intensity[n_refl*2+1]),n1,n0);
if ((rx[n_refl*2+1]<0) && (n_refl==n_refl_max+1)) //Rueckstreuung
{
m_1 = int((ry[n_refl*2+1]/2.+0.5)*my);
m_2 = int((rz[n_refl*2+1]/2.+0.5)*mz);
gsl_matrix_set(Intensity_result,m_1,m_2,
gsl_matrix_get(Intensity_result,m_1,m_2)
+Intensity[n_refl*2+1]);
// Richtung, in welche am meisten Licht gestreut wird
if (gsl_matrix_get(Intensity_result,m_1,m_2) > Intensity_max)
3.6 Entstehung eines Regenbogens
{
Intensity_max = gsl_matrix_get(Intensity_result,m_1,m_2);
angle_max = atan( sqrt( sqr(ry[n_refl*2+1])
+sqr(rz[n_refl*2+1])));
}
148
149
150
151
152
}
153
}
154
}
155
}
156
157
123
}
158
159
160
161
162
163
164
165
166
167
168
169
170
171
m1start = int(my*0.1); m1stop = int(my*0.9);
m2start = int(mz*0.6); m2stop = int(mz*1.0);
for (m_1=m1start; m_1<m1stop; m_1++)
for (m_2=m2start; m_2<m2stop; m_2++)
{
out_stream << (m_1/float(my)-0.5) * 2. << " "
<< (m_2/float(mz)-0.5) * 2. << " "
<< gsl_matrix_get(Intensity_result,m_1,m_2) << "\n";
}
cout << " Zahl der m_1 : " << m1stop-m1start << "\n";
cout << " Zahl der m_2 : " << m2stop-m2start << "\n";
cout << " Maximale Intensitaet : " << Intensity_max << "\n";
cout << " Winkel maximaler Intensitaet : " << angle_max*180./pi << "\n";
172
173
174
175
out_stream << "! GLE-Eingabe-Datei generiert von regen1.cpp\n";
out_stream.close();
}
Die Funktion hit wurde gemäß (3.31) bis (3.38) an eine ellipsoidförmige
Grenzfläche angepasst. Im Hauptprogramm wird mit Hilfe dieser Funktion
für eine Vielzahl paralleler Lichtstrahlen, die von der Sonne kommen, geprüft, ob sie den Regentropfen treffen. Wenn dies der Fall ist, wird der Weg
dieses Lichtstrahles mit n refl max Reflektionen innerhalb des Tropfens verfolgt, bis er dann bei der (n refl max+1)-ten Berührung mit der Grenzfläche
Wasser–Luft den Tropfen verlässt. Jeder dieser Strahlen trägt zur Gesamtintensität bei, weswegen wir ihre jeweiligen Einzelintensitäten aufsummieren.
Mit der Abfrage in Zeile 139 beschränken wir uns auf rückgestreutes Licht
(den Regenbogen sehen wir immer nur, wenn wir die Sonne im Rücken haben!) und können uns zusätzlich mit dem zweiten Teil der Bedingung auf
Licht beschränken, das genau n refl max Reflektionen im Regentropfen erfahren hat. Diese Einschränkung ist hilfreich, um den ersten und zweiten
Regenbogen getrennt zu untersuchen, da diese stark unterschiedliche Intensitäten aufweisen (siehe auch Übungsaufgabe 3.4).
Im letzten Teil des Programms erkennen Sie unschwer das Herausschreiben der gefundenen Intensitäten, allerdings nur in einem eingeschränkten
Bereich, der bewußt die Richtung genau entgegengesetzt zur Sonne ausspart.
In dieser Richtung würden Sie nämlich ein starkes Maximum der Intensität
finden, das in der Realität jedoch ohne Bedeutung ist, weil diese Richtung bei
Tag unterhalb des Horizontes liegt. Anschließend werden noch die Zahl der
Punkte in horizontaler und vertikaler Richtung, das Intensitätsmaximum, sowie der Öffnungswinkel des Regenbogens (Winkel maximaler Intensitaet)
124
3 Optik
Abb. 3.8. Regenbogen, wie er durch kugelförrmige Wassertropfen hervorgerufen
wird. Da Lichtstreuung in der Atmosphäre nicht berücksichtigt wird, ist der Himmel
schwarz und nicht blau
auf dem Bildschirm ausgegeben. Die Öffnungswinkel können wir mit den
Werten in der Literatur vergleichen, während wir die anderen Zahlen zur
Generierung der graphischen Darstellung benötigen.
In Abb. 3.8 sehen Sie die so generierte Helligkeitsverteilung, in der Sie
sehr schön den Regenbogen erkennen. Die Öffnungswinkel betragen ca 42.6
Grad für das rote Licht, 42.4 Grad beim grünen Licht und 42.2 Grad für
blaues Licht. Es erstaunt Sie vielleicht, dass die Breite des Regenbogens lediglich 0.4 Grad beträgt – generell neigt der Mensch dazu, die Größe von
Himmelserscheinungen zu überschätzen, beispielsweise beträgt die scheinbare Größe der Sonnenscheibe lediglich 0.5 Grad, wesentlich weniger als die
meisten Menschen schätzen würden.
3.6.4 Der Regenbogen bei ellipsoidförmigen Regentropfen
Bis zu diesem Punkt haben wir kugelförmige Regentropfen betrachtet – obwohl unser Programm regen1.cpp so konstruiert ist, dass es auch Regentropfen in Betracht zieht, die die Form eines Rotationsellipsoids haben. Indem wir
die beiden Radien a und b in der Eingabedatei verschieden wählen, erhalten
wir diesen allgemeineren Fall, wobei a<b (β < 1) ein abgeflachtes Ellipsoid
(eine Art Diskusscheibe) bedeutet und r1>r2 (β > 1) ein langgestrecktes
Ellipsoid (eine Art Zigarre). Für eine verhältnismäßig schwache Abplattung
(r1=1 und r2=1.2) erhalten wir den Regenbogen in Abb. 3.9, der bereits
stark von der uns bekannten Kreisform abweicht.
Der Grund für diese starke Veränderung der Form des Regenbogens bei
nur geringfügig verformten Wassertropfen liegt in dem komplizierten Weg,
den der Lichtstrahl im Regentropfen nimmt, und bei dem er dreimal von
der Oberfläche des Regentropfens beeinflusst wird: zweimal bei Brechung
und zweimal in Reflektion. Dadurch verstärken sich eventuelle Abweichungen
vom Strahlverlauf bei kugelförmigen Regentropfen. Aus der Tatsache, dass
Regenbögen – zumindest für das bloße Auge – praktisch kreisförmig sind,
kann man also umgekehrt schließen, dass dies noch in ausgepägterem Maß
für die Regentropfen der Fall sein muss, auch wenn sie wohl nicht völlig
kugelförmig sind.
3.7 Grundlagen der Wellenoptik
125
Abb. 3.9. Regenbogen, wie er durch Wassertropfen hervorgerufen wird, die die
Form eines abgeflachten Rotationsellipsoids haben. Da Lichtstreuung in der Atmosphäre nicht berücksichtigt wird, ist der Himmel schwarz und nicht blau
3.7 Grundlagen der Wellenoptik
In der Wellenoptik wird – im Gegensatz zur Strahlenoptik – berücksichtigt,
dass Licht eine Wellenlösung der Maxwellschen Gleichungen ist, also eine
Schwingung von elektrischem und magnetischem Feld darstellt. Wir wollen an
dieser Stelle die genaue Form dieser Lösung nicht herleiten, sondern lediglich
einige ihrer Eigenschaften ins Gedächtnis rufen:
–
Licht ist eine transversale Welle, d.h. elektrisches und magnetisches Feld
stehen immer senkrecht auf der Ausbreitungsrichtung.
– Das elektrische und magnetische Feld stehen außerdem senkrecht aufeinander.
– Die Knoten des elektrischen Feldes sind Maxima des magnetischen Feldes
und umgekehrt.
Eine entscheidende Größe in der vorhin besprochenen Strahlenoptik war
die Intensität des Lichtes, die wir auch hier angeben können:
I=
1 c
1
cε0 εr E 2 +
B2 .
2
2 µ0 µr
(3.39)
c ist die Vakuumlichtgeschwindigkeit, ε0 bzw µ0 sind die Dielektrizitätskonstante bzw. die Permeabilität des Vakuums und εr bzw. µr sind Materialkonstanten, die die Dielektrizität bzw. Permeabilität relativ zum Vakuumwert festlegen. An (3.39) sieht man sehr schön, dass die Energie sowohl im
elektrischen als auch im magnetischen Feld stecken kann. Im Fall von Licht
wechselt die Energie zwischen diesen beiden Formen hin und her, so wie dies
bei einer mechanischen Schwingung eines Pendels zwischen potentieller und
kinetischer Energie der Fall ist.
Die etwas unschönen Vorfaktoren in Gleichung (3.39) kann man durch
Einführung skalierter Variabler
√
E = cε0 εr E
(3.40)
c
B =
B
(3.41)
µ0 µr
126
3 Optik
loswerden, was zudem den Vorteil hat, dass danach elektrisches und magnetisches Feld die selbe Dimension haben und die Amplituden dieser beider
Felder im Falle von Licht identisch sind. Wir werden im Weiteren diese skalierten Größen verwenden, der Übersichtlichkeit halber allerdings die Striche
wieder weglassen.
Besonders bequem ist es, das elektrische und magnetische Feld zu einer
komplexen Größe
A = E + iB
(3.42)
zusammenzufassen. Dies hat zum einen den Vorteil, dass sich die Formel für
die Intensität I weiter vereinfacht zu
I=
1
|A|2 ,
2
(3.43)
vor allem aber schreiben sich sowohl ebene als auch Kugelwellen besonders
einfach in dieser Darstellung.
3.8 Ebene Wellen und Kugelwellen
Ein wichtiges Konzept ist die Zerlegung eines beliebigen Lichtfeldes nach einer
Basis, wobei wir hier die zwei wichtigsten solcher Elementarwellen vorstellen
wollen: zum einen ebene Wellen und zum anderen Kugelwellen. Von beiden
Zerlegungen werden wir im weiteren Verlauf Gebrauch machen.
Die ebene Welle wird am Besten durch eine sehr weit entfernte Lichtquelle,
die ununterbrochen leuchtet, realisert. In diesem Fall sind das elektrische und
magnetische Feld gegeben durch
E = E0 cos(kz − ωt + φ0 )ex
(3.44)
B = B0 sin(kz − ωt + φ0 )ey .
(3.45)
Dabei haben wir die Freiheit bei der Wahl des Koordinatensystems so ausgenutzt, dass die Fortpflanzung der Welle in z-Richtung erfolgt und E und B
in der x- bzw. y-Achse liegen. Mit der komplexen Schreibweise (3.42) sieht
das ganze etwas übersichtlicher aus:
A = A0 exp (i(kz − ωt + φ0 )) .
(3.46)
Die Phase φ0 können Sie auch in die komplexe Amplitude A0 einbeziehen.
Bei der Kugelwelle handelt es sich im Gegensatz dazu um ein Lichtfeld,
wie es von einer punktförmigen Lichtquelle erzeugt wird, die das Licht isotrop
in alle Raumrichtungen ausstrahlt. Um die Abnahme der Lichtintensität mit
wachsender Entfernung richtig zu bestimmen, betrachten wir die Energie in
einer Kugelschale mit Radius R um den Ausgangspunkt der Kugelwelle. Aus
der Forderung, dass die Abstrahlung isotrop ist, folgt, dass die Intensität I
über die Kugelfläche konstant sein muss, d.h. der Energiestrom durch diese
3.9 Interferenz
127
Kugelfläche ist proportional zu IR2 . Da dieser Energiestrom für alle diese
Kugelflächen gleich sein muss – es kann ja nirgendwo Energie verloren gehen
– haben wir
(3.47)
I ∝ 1/R2 ,
woraus wiederum
A ∝ 1/R
folgt. Damit erhalten wir für die Kugelwelle
1
A = A0 exp(i(kR − ωt + φ0 )) .
R
(3.48)
(3.49)
3.9 Interferenz
Die unmittelbarste Konsequenz der Tatsache, dass Licht eine Schwingung
darstellt, ist Interferenz. Überlagern sich nämlich zwei solcher Schwingungen,
so heißt das, dass diese beiden Felder vektoriell addiert werden müssen, um
die Gesamtsituation zu beschreiben. Betrachten wir zur besseren Übersicht
zunächst zwei monochromatische, ebene Lichtwellen, die sich in identischer
Richtung (entlang der z-Achse) ausbreiten und die selbe Wellenlänge haben.
Außerdem sollen beiden Lichtwellen so polarisiert sein, dass das elektrische
Feld in x-Richtung und das magnetische Feld in y-Richtung zeigt. Letzteres
hat zur Folge, dass wir den vektoriellen Charakter von A außer acht lassen
können.
A1 = A0,1 exp(i(kz − ωt + φ1 ))
A2 = A0,2 exp(i(kz − ωt + φ2 )) .
(3.50)
(3.51)
Dabei haben wir die Phasen φ1 und φ2 so gewählt, dass A1 und A2 reell und
positiv sind. Überlagern wir nun diese beiden Wellen, erhalten wir als Summe
für A:
A = A1 + A2
= (A0,1 + A0,2 ) exp(i(kz − ωt)) .
(3.52)
(3.53)
Die Intensität schließlich ist proportional zum Betragsquadrat
also erhalten wir
|A|2 = A21 + A22 + 2A1 A2 cos(φ2 − φ1 ) ,
(3.54)
Iges = I1 + I2 + 2 I1 I2 cos(φ2 − φ1 ) .
(3.55)
Das Interessante an diesem Ergebnis ist, dass die Gesamtintensität nicht
gleich der Summe der Einzelintensitäten ist, sondern ein sogenannter Interferenzterm 2A1 A2 cos(φ2 − φ1 ) hinzukommt. Dieser Term kann positiv
(konstruktive Interferenz) oder negativ (destruktive Interferenz) sein, so dass
die Intensität sowohl größer als auch kleiner als erwartet sein kann. Im Extremfall (A1 = A2 und φ2 − φ1 = π) können sich die beiden Wellen sogar
komplett auslöschen und die Gesamtintensität ist null.
128
3 Optik
3.10 Das Huygenssche Prinzip
Das Huygenssche Prinzip besagt, dass man sich jeden Punkt einer Wellenfront
als Ausgangspunkt einer kugelförmigen Elementarwelle vorstellen kann und
dass diese Elementarwellen so interferieren, dass sie alle zusammen die weitere
Zeitentwicklung des gesamten Lichtwellenfeldes beschreiben.
Wir wollen uns diesen Sachverhalt an einer ebenen Welle verdeutlichen,
deren Fortpflanzungsrichtung wir in Richtung der z-Achse wählen. Um nun
das Huygenssche Prinzip anzuwenden, benötigen wir eine Wellenfront, z.B.
die xy-Ebene. Jeder Punkt A dieser Ebene ist Ausgangspunkt einer Kugelwelle und trägt so zum Lichtfeld im gesamten Raum bei. Berechnen wir nun
konkret das Feld an einem Punkt B im Abstand L von der Wellenfront. Die
Aufsummation (in diesem Fall eine Integration) des Huygensschen Prinzip
ergibt das Integral
(3.56)
A ∝ A0 dxdy exp(ikl)/l ,
wobei l der Abstand des betrachteten Punktes vom Punkt (Ax , Ay , 0) ist.
Wenn wir den Punkt, an dem wir das Lichtfeld berechnen wollen, auf die
z-Achse legen, ist dieser Abstand
(3.57)
l = A2x + A2y + L2 .
Offensichtlich hängt l nur vom Abstand r = A2x + A2y des Punktes A vom
Ursprung ab – daher ist der Beitrag aller Punkte auf dem Kreis in Abb 3.10
identisch.
Abb. 3.10. Schematische Darstellung zur Konstruktion einer ebenen Welle aus Kugelwellen. Links zu sehen ist die Ebene, die Ausgangspunkt der Elementarwellen ist.
Eingezeichnet in diese Ebene ist ein Kreis konstanten Abstands zum betrachteten
Punkt A
Fassen wir diese Punkte zusammen, verbleibt von dem Doppelintegral in
(3.56) nur noch ein Einfachintegral:
3.11 Berechnung von Beugungsmustern
∞
A ∝ 2πA0
129
r
dr exp(ikl)
l
(3.58)
dl exp(ikl)
(3.59)
0
∞
= 2πA0
L
=
2πiA0
exp(ikL) .
k
(3.60)
Für den letzten Schritt mussten wir die Konvergenz des Integrals durch einen
Konvergenz erzeugenden Faktor erzwingen – am einfachsten indem man k
einen kleinen Imaginärteil zuweist, den man dann gegen null gehen lässt.
Das Endergebnis (3.60) sagt uns nicht nur, dass sich in der Tat die Kugelwellen zu einer ebenen Welle (von der wir ja ausgegangen sind) aufsummieren,
sondern liefert auch den fehlenden Proportionalitätsfaktor −ik/(2π).
3.11 Berechnung von Beugungsmustern
Wir können das Huygenssche Prinzip dazu verwenden, das Beugungsmuster
hinter einer Blende (z.B. hinter einem Spalt oder einem Doppelspalt) zu
berechnen. Gegenüber der vorangegangen Berechnung bedeutet es lediglich
dass der Integrationsbereich in (3.56) auf den offenen Teil der Blende eingeschränkt werden muss. Besonders übersichtlich gestaltet sich die Berechnung
des Beugungsmusters bei einer kreisförmigen Blende, mit der wir uns in diesem Abschnitt befassen werden. Betrachten wir dazu zunächst Abb 3.11:
Abb. 3.11. Schematische Darstellung einer kreisförmigen Blende (im Hintergrund)
und der Bildebene (im Vordergrund). Dargestellt ist ein beliebig herausgegriffener
Lichtweg von der Blende zu einem Punkt auf der Bildebene, der x0 von der optischen
Achse entfernt ist
130
3 Optik
Die Einführung einer Blende bricht die Translationssymmetrie gegenüber
Verschiebungen in x- und y-Richtung und führt eine optische Achse ein (die
Gerade, die senkrecht durch den Mittelpunkt der Kreisblende geht). Während
es vorher naheliegend war, die z-Achse so zu legen, dass sie durch den Punkt
A geht, an dem wir das Licht berechnen wollten, müssen wir uns nun entscheiden, ob es nicht sinnvoller ist, sie in die optische Achse zu legen. Wir
entscheiden uns für diese Vorgehensweise und bezeichnen mit x0 den Abstand von A von der optischen Achse. Die Kreisblende ist dann ein Kreis
mit Radius R um die z-Achse, während jener Kreis, der alle Punkte gleichen
Abstands l von A enthält, gegenüber der optischen Achse um x0 versetzt ist.
Dieser letztere Kreis hat Radius r und Sie sehen in Abb 3.11, dass er unter
Umständen nur teilweise innerhalb der Öffnung der Blende liegt (dieser Teil,
den wir h nennen wollen, ist in der Abbildung mit einer durchgezogenen Linie
gezeichnet, während der Rest des Kreises punktiert ist).
Dieser Tatsache müssen wir nun Rechnung tragen, wobei wir drei Fälle
unterscheiden müssen:
– Wenn r + x0 < R ist, liegt dieser Kreis vollständig innerhalb der Öffnung
der Blende und h ist durch 2πr gegeben.
– Wenn r ∈ [R−x0 , R+x0 ], liegt der Kreis teilweise innerhalb dieser Öffnung
und wir müssen noch berechnen, wie groß dieser Teil h(r) ist.
– Wenn r > R+x0 ist, liegt der Kreis gar nicht mehr innerhalb der Öffnung,
h ist null und es gibt keinen Beitrag zum Integral (3.56).
Wenn wir für jeden Wert von r das zugehörige Bogenstück h kennen, ergibt
sich die komplexe Amplitude A des Gesamtfeldes als Verallgemeinerung des
Integrals (3.59):
A = 2πA0
dl exp(ikl)h(r(l)) .
(3.61)
Beachten Sie bitte, dass der erste der oben genannten Fälle nur auftritt,
wenn x0 < R ist, was bedeutet dass der Punkt x0 in dem Bereich ist, der nach
den Gesetzen der Strahlenoptik ausgeleuchtet wird. Im Folgenden bezeichnen
wir den Beitrag dieses ersten Falles zu A mit A1 , den des zweiten Falles mit
A2 , während der dritte Fall offensichtlich keinen Beitrag zu A liefert.
A1 können wir analytisch berechnen, hingegen werden wir das aus A2
resultierende Integral im Computer bestimmen müssen. Für die numerische
Implementation benötigen wir eine kleine Nebenrechnung. Am Einfachsten
ist es, wenn wir den Kreis mit dem Winkel φ zur x-Achse parametrisieren
und den Winkel φ0 am Schnittpunkt mit der Kreisblende berechnen:
x20 + R2 + 2x0 R cos φ0 = r2
r2 − x20 − R2
cos φ0 =
.
2x0 R
(3.62)
(3.63)
Wenn x0 im Intervall [R −x0 , R +x0 ] liegt, ist der Bruch auf der rechten Seite
von (3.63) zwischen −1 und 1, so dass ein zugehöriger Winkel φ0 existiert.
3.11 Berechnung von Beugungsmustern
131
Die Länge des relevanten Kreisbogens (innerhalb der Kreisblende) ist nun
2(π − φ0 ) ,
(3.64)
so dass wir schließlich
1
A2 =
ik
1
=
ik
R+x
0
exp(ikl)
l
(3.65)
exp(ikl)
.
l
(3.66)
dr (r − φ0 (r))
r=R−x0
l2
dl (r(l) − φ0 (l))
l=l1
erhalten. Dabei sind l1 und l2 definiert durch
2
l12 = (R − x0 ) + L2
l22
2
2
= (R + x0 ) + L .
(3.67)
(3.68)
Nun benötigen wir noch A1 , von dem wir ja oben behauptet haben, dass
wir es analytisch berechnen können. Im Wesentlichen können wir diese Berechnung aus Abschn. 3.10 übernehmen:
R−x
0
1
A1 =
ik
1
=
ik
dr r
r=0
l1
dl l
l=L
exp(ikl)
l
exp(ikl)
l
= exp(ikl1 ) − exp(ikL) .
1
2
3
4
5
6
7
(3.69)
(3.70)
(3.71)
/**************************************************************************
* Name:
kreisbl1.cpp
*
* Zweck:
Berechnet das Beugungsmuster hinter einer kreisfoermigen
*
*
Blende ( Kohaerenzlaenge unendlich )
*
* Gleichung: siehe Buch
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
8
9
10
11
12
13
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_integration.h>
<math.h>
"tools.h"
14
15
using namespace std;
16
17
18
//-- globale Variablen
double R, R2, x_02, x_0, L, k;
19
20
//-------------------------------------------------------------------------
132
3 Optik
21
22
double f(double dist, void *params)
23
24
25
{
double r, r2, phi, res, arg;
26
27
28
29
30
31
32
33
34
35
36
r2 = sqr(dist)-sqr(L);
r = sqrt(r2);
res = 0;
if (r<R-x_0) res = 2.*pi;
else if (r<R+x_0)
{
arg = (r2-R2+x_02)/(2.*x_0*r);
if (fabs(arg)<1) { phi = acos(arg);
res = 2.*phi; }
}
37
38
39
return res;
}
40
41
//-------------------------------------------------------------------------
42
43
main( int argc, char *argv[] )
44
45
{
46
47
48
49
50
51
52
53
54
//-- Definition der Variablen
int
limit = 1000;
int
n1, nmax;
double epsabs = 1.e-10;
double epsrel = 1.e-4;
double A_real, A_imag, A_1_real, A_1_imag, A_2_real, A_2_imag;
double l_0, l_start, l_end, delta_l, x_max, dx, abserr, Intensity;
double mu = 10;
55
56
57
gsl_integration_workspace *w;
gsl_integration_qawo_table *wf;
58
59
60
ifstream in_stream;
ofstream out_stream;
61
62
63
64
65
66
67
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: kreisbl1 infile outfile\n";
exit(1);
}
68
69
70
71
72
73
74
75
76
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von kreisbl1.cpp\n";
inout(R,"R");
inout(L,"L");
inout(k,"k");
inout(nmax,"nmax");
inout(x_max,"x_max");
in_stream.close();
77
78
79
//-- Berechnung einiger benoetigter Parameter -dx = x_max / nmax;
3.11 Berechnung von Beugungsmustern
80
R2 = sqr(R);
81
82
83
84
for (n1 = nmax-1; n1>=0; n1--)
{
x_0 = (n1+0.5)*dx; x_02 = sqr(x_0);
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
if (R>x_0)
{
l_0 = sqrt(sqr(R-x_0)+sqr(L));
l_start = l_0;
l_end
= sqrt(sqr(R+x_0)+sqr(L));
A_1_real = 2.*pi / k * (sin(k*l_0)-sin(k*L));
A_1_imag = 2.*pi / k * (cos(k*l_0)-cos(k*L));
}
else
{
A_1_real = 0;
A_1_imag = 0;
l_start = sqrt(sqr(x_0-R)+sqr(L));
l_end
= sqrt(sqr(x_0+R)+sqr(L));
}
delta_l = l_end-l_start;
102
103
104
105
w = gsl_integration_workspace_alloc(100000);
wf = gsl_integration_qawo_table_alloc(k, delta_l,
GSL_INTEG_COSINE, 100000);
106
107
108
109
gsl_function F;
F.function = &f;
F.params
= μ
110
111
112
gsl_integration_qawo(&F, l_start, epsabs, epsrel, limit,
w, wf, &A_2_real, &abserr);
113
114
115
gsl_integration_workspace_free(w);
gsl_integration_qawo_table_free(wf);
116
117
118
119
w = gsl_integration_workspace_alloc(100000);
wf = gsl_integration_qawo_table_alloc(k, delta_l,
GSL_INTEG_SINE, 100000);
120
121
122
gsl_integration_qawo(&F, l_start, epsabs, epsrel, limit,
w, wf, &A_2_imag, &abserr);
123
124
125
gsl_integration_workspace_free(w);
gsl_integration_qawo_table_free(wf);
126
127
128
129
130
131
132
A_real = A_1_real + A_2_real;
A_imag = A_1_imag + A_2_imag;
Intensity = (sqr(A_real)+sqr(A_imag)) * sqr(k/2./pi);
out_stream << x_0 << " " << Intensity << "\n";
cout
<< x_0 << " " << Intensity << "\n";
}
133
134
135
out_stream.close();
}
133
134
3 Optik
Die Real- und Imaginärteile der Integrale A1 und A2 finden Sie im Programm als A 1 real und A 1 imag bzw A 2 real und A 2 imag. Es fällt
Ihnen vielleicht auf, dass die Funktion f in diesem Programm nicht direkt der Integrand ist, sondern von diesem der schnell oszillierende Anteil
exp(iωl) abgespalten wurde. Integrale mit solchen schnell oszillierenden Anteilen stellen nämlich für die Standard-Algorithmen zur Integration eine besondere Schwierigkeit dar, so dass hierfür eine speziell angepasste Routine
(gsl integration qawo) verwendet wurde.
2
I / I0
1
0
0
0.5 10
-3
-3
10
1.5 10
-3
x0
Abb. 3.12. Beugungsmuster hinter einer kreisförmigen Blende
Der Bereich bis x0 = 10−3 in Abb 3.12 entspricht dem Teil der Bildebene,
der in der geometrischen Optik ausgeleuchtet ist, während der verbleibende
Teil der Bildebene im Schatten läge. Sie sehen, dass die Wellennatur des Lichtes diese scharfe Trennung aufhebt, so dass zum einen auch etwas Licht in
den Rand des Schattenbereiches eindringt und zum anderen auch im ausgeleuchteten Teil dunkle Stellen (in diesem Fall wegen der Rotationssymmetrie
dunkle Kreise) auftreten. Beachten Sie außerdem, dass im Bereich konstruktiver Interferenz die Intensität höher ist als in der geometrischen Optik zu
erwarten wäre (I/I0 = 1), so dass die über die gesamte Bildebene gemittelte Intensität die selbe ist. Letzteres bringt zum Ausdruck, dass auch in der
Wellenoptik konstruktive und destruktive Interferenz zusammen die Energieerhaltung nicht verletzen dürfen. Interessant ist auch, dass die Mitte des
ausgeleuchteten Bereichs dunkel bleibt: I(x0 = 0) = 0.
3.12 Kohärenz
Bis jetzt haben wir ausschließlich die Interferenz von völlig monochromatischem Licht betrachtet, was eine Lichtquelle impliziert, die schon immer
geleuchtet hat und auch in Zukunft immer leuchten wird! Diese Annahme
ist natürlich irrealistisch, jede Lichtwelle muss einen zeitlichen Anfang und
ein zeitliches Ende haben. Im Falle thermischer Lichtquellen (also z.B. einer
Glühbirne oder der Sonne) haben wir es mit einer statistischen Überlagerung
sehr vieler und sehr kurzer solcher Lichtwellenzüge zu tun, so dass der zeitliche Verlauf des elektrischen Feldes in etwa so aussieht, wie es in Abb. 3.13
3.12 Kohärenz
135
Abb. 3.13. Schematische Darstellung des elektrischen Feldes von Licht mit endlicher Kohärenzlänge
dargestellt ist. Die Frage, wie es mit der Interferenz solchen Lichts aussieht,
wird uns zum Begriff der Kohärenz führen.
Wir beginnen bei (3.55), wobei wir jedoch den Phasen φ1 und φ2 statistischen Charakter zuweisen, wonach wir als Ausdruck für die Intensität
(3.72)
Iges = I1 + I2 + 2 I1 I2 cos(φ1 − φ2 ) .
erhalten. Die eckigen Klammern auf der rechten Seite von (3.72) bedeuten den
statistischen Mittelwert des innerhalb der Klammern stehenden Ausdrucks –
für die Grundlagen der Statistik, soweit nicht vorhanden, verweisen wir den
Leser auf das folgende Kap. 4. Gegenüber der geometrischen Optik, in der
Intensitäten einfach addiert werden, erhalten wir einen zusätzlichen Term,
den Interferenzterm
2 I1 I2 cos(φ1 − φ2 ) .
(3.73)
Im Fall von unterschiedlichen Lichtquellen ist die Phasendifferenz völlig
zufällig und gleichverteilt, so dass sich der Interferenzterm im Mittel weghebt und wir das Ergebnis der geometrischen Optik erhalten. Licht aus unterschiedlichen Quellen kann also nicht miteinander interferieren.
Wie sieht es nun aus mit Licht, das aus einer Quelle kommt, aber auf unterschiedlichen Wegen auf die Bildebene gelangt? Ein Beispiel hierfür ist die
Kreisblende aus dem vorhergehenden Abschnitt, aber auch im Regenbogen
gibt es mehrere mögliche Wege des Lichtstrahls, die zum selben Ausfallswinkel führen. Der Weglängendifferenz ∆l entspricht ein Laufzeitunterschied
∆t = ∆l/c: die beiden Lichtwellen sind also zu verschiedenen Zeiten bei der
Lichtquelle gestartet! In diesem Fall ist der Mittelwert cos(φ1 − φ2 ) eine
Funktion der Laufzeitdifferenz ∆t und eine charakteristische Eigenschaft der
Lichtquelle. Bei einer rein monochromatischen Lichtquelle ist die Phasendifferenz
(3.74)
φ1 − φ2 = ω(t1 − t2 )
fest, so dass die Mittelung trivial ist:
cos(φ1 − φ2 ) = cos(ω(t1 − t2 )) .
(3.75)
Diesen Faktor, der nur von der Frequenz des Lichtes herrührt, spaltet man
vom Mittelwert cos(φ1 − φ2 ) ab und definiert den verbleibenden Teil als
136
3 Optik
Kohärenzfunktion:
C(∆t) = cos(φ(t) − φ(t + ∆t))/ cos(ω∆t) .
(3.76)
Dabei bringt diese Schreibweise schon implizit zum Ausdruck, dass diese
Kohärenzfunktion aus Symmetriegründen nur von der Laufzeitdifferenz ∆t
abhängen kann. Betrachten wir Abb. 3.13, so wird klar, dass die Kohärenzfunktion für sehr kleine Zeiten den Wert 1 annehmen muss (auf kleinen Skalen ist der Verlauf sinusoidal), während umgekehrt im Limes ∆t → ∞ die
Kohärenzfunktion auf null abfallen muss, da die einzelnen Wellenzüge voneinander unabhängig sind. Darüberhinaus können wir zunächst keine Aussage
über die Kohärenzfunktion machen und tatsächlich kann sie von Fall zu Fall
sehr verschieden aussehen, häufig jedoch hat sie die Form einer Exponentialfunktion:
|∆t|
C(∆t) = exp −
,
(3.77)
tcoh
wobei die Kohärenzzeit tcoh eine für den Prozess der Lichtentstehung charakteristische Konstante ist. Die in der Literatur ebenfalls oft verwendete
Kohärenzlänge lcoh ist definiert durch
lcoh = ctcoh .
(3.78)
Setzen wir die Kohärenzfunktion (3.76) in den Ausdruck für die Intensität
(3.72) ein, so erhalten wir
I = I1 + I2 + 2 I1 I2 C(t1 − t2 ) cos(ω(t1 − t2 ))
(3.79)
= A1 A1 C(0) cos(0) + A2 A2 C(0) cos(0)
+2Re (A1 A2 ) C(t1 − t2 ) cos(ω(t1 − t2 )) .
(3.80)
Dabei haben wir davon Gebrauch gemacht, dass sowohl Kohärenzfunktion
als auch der Kosinus symmetrisch sind und bei null den Wert eins haben. Die
Darstellung (3.80) hat den Vorteil, dass sie leicht auf mehrere sich überlagernde Wellen oder sogar auf den kontinuierlichen Fall verallgemeinert werden
kann:
I=
An Am C(tn − tm ) cos(ω(tn − tm ))
(3.81)
n,m
bzw.
I=
dx1
dx2 A(x1 )A (x2 )C (t(x1 ) − t(x2 ))
× cos [ω(t(x1 ) − t(x2 ))] ,
(3.82)
wobei wir jedoch nicht implizieren wollen, dass die Integrationsvariablen x1
bzw. x2 Ortscharakter haben müssen, sondern einfach verschiedene mögliche
Wege parametrisieren.
3.13 Beugung bei endlicher Kohärenzlänge
137
3.13 Beugung bei endlicher Kohärenzlänge
Die Berechnung von Beugungsmustern unter Berücksichtigung der endlichen
Kohärenzlänge lcoh diskutieren wir zunächst an einem Beispiel, das wir – allerdings für lcoh = ∞ – bereits besprochen haben: die Beugung hinter einer
kreisförmigen Blende. Das hat den Vorteil, dass wir die gewonnenen Ergebnisse vergleichen können und so eine einfache Überprüfungsmöglichkeit für
deren Richtigkeit haben.
Wenn wir die Ausdrücke für die Intensität bei unendlicher ((3.54) mit
(3.61)) und endlicher Kohärenzlänge (3.82) vergleichen, so stellen wir fest,
dass aus dem Einfachintegral, das das Programm kreisbl1.cpp zu lösen
hatte, nun ein Doppelintegral wird:
(3.83)
I = dl1 dl2 h(l1 )h(l2 ) exp(ik(l1 − l2 ))C(|x1 − x2 |) .
Das erste Problem mit diesem Integral erkennen wir, wenn wir für die
Kohärenzfunktion
C(|x1 − x2 |) = exp(−α|x1 − x2 |)
(3.84)
die Normierung berechnen wollen, was uns (h(l) = 2π) auf das Integral
∞ ∞
dl1 dl2 exp(ik(l1 − l2 )) exp(−α|l1 − l2 |)
L
(3.85)
L
führt, welches divergent ist. Das kann daran liegen, dass die Koheränzfunktion zumindest für große Argumente unphysikalisch ist oder unsere Annahme
einer ebenen Welle zur Berechnung der Normierungskonstante das Problem
darstellt, da ebene Wellen nicht realisierbar sind und unendlich viel Energie
enthalten würden. Welcher der beiden Gründe hier auch vorliegt, es ist in beiden Fällen legitim, die Normierung auf später zu verschieben und nach der
Berechnung des Beugungsmusters aus der Energieerhaltung zu bestimmen.
Damit sollte die Implementation des Doppelintegrals (3.83) eigentlich kein
Problem darstellen:
1
2
3
4
5
6
7
/**************************************************************************
* Name:
kreisbl2.cpp
*
* Zweck:
Berechnet das Beugungsmuster hinter einer kreisfoermigen
*
*
Blende (endliche Kohaerenzlaenge)
*
* Gleichung: siehe Buch
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
8
9
10
11
12
13
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_integration.h>
<math.h>
"tools.h"
138
3 Optik
14
15
using namespace std;
16
17
18
19
//-- globale Variablen
double R, R2, x_0, x_02, L, k, l2_global, alpha;
gsl_integration_qawo_table *wf;
20
21
//-------------------------------------------------------------------------
22
23
double f_inner(double l1_shifted, void *params)
24
25
26
27
{
double r, r2, res, phi, arg, l1;
res = 0;
28
29
l1 = l1_shifted + L;
30
31
32
33
34
if (l1 < L) return 0;
r2 = sqr(l1_shifted)+2.*L*l1_shifted;
r = sqrt(r2);
arg = 0;
35
36
37
if (fabs(x_0+r) < R)
res = res + 2.*pi;
38
39
40
41
42
43
else if (fabs(x_0-r) < R)
{
arg = (r2+x_02-R2)/(2.*x_0*r);
if (fabs(arg)<=1) res = res + 2.*acos(arg);
}
44
45
return res*exp(-alpha*fabs(l1-l2_global));
46
47
}
48
49
//-------------------------------------------------------------------------
50
51
double f_outer_real(double l2_shifted, void *params)
52
53
54
55
56
57
58
59
{
int limit = 1000;
double r, r2, phi, arg, res, l1_start, l2, Int_inner_real_1, abserr;
double mu = 10;
double epsabs = 1.e-17;
double epsrel = 1.e-5;
gsl_integration_workspace *w;
60
61
res = 0;
62
63
64
l2 = l2_shifted + L;
l2_global = l2;
65
66
67
68
if (l2 < L) return 0;
r2 = sqr(l2_shifted)+2.*L*l2_shifted;
r = sqrt(r2);
69
70
71
72
if (fabs(x_0+r) < R)
res = res + 2.*pi;
3.13 Beugung bei endlicher Kohärenzlänge
73
74
75
76
77
139
else if (fabs(x_0-r) < R)
{
arg = (r2+x_02-R2)/(2.*x_0*r);
if (fabs(arg)<=1) res = res + 2.*acos(arg);
}
78
79
80
81
gsl_function F;
F.function = &f_inner;
F.params
= μ
82
83
84
85
86
if (R>x_0)
l1_start = 0;
else
l1_start = sqrt(sqr(x_0-R)+sqr(L))-L;
87
88
w = gsl_integration_workspace_alloc(100000);
89
90
91
gsl_integration_qawo(&F, l1_start, epsabs, epsrel, limit,
w, wf, &Int_inner_real_1, &abserr);
92
93
gsl_integration_workspace_free(w);
94
95
96
return Int_inner_real_1*res;
}
97
98
//-------------------------------------------------------------------------
99
100
double f_outer_imag(double l2_shifted, void *params)
101
102
103
104
105
106
107
108
{
int limit = 1000;
double r, r2, phi, res, arg, l1_start, l2, Int_inner_real_1, abserr;
double mu = 10;
double epsabs = 1.e-17;
double epsrel = 1.e-5;
gsl_integration_workspace *w;
109
110
res = 0;
111
112
113
l2 = l2_shifted + L;
l2_global = l2;
114
115
116
117
if (l2 < L) return 0;
r2 = sqr(l2_shifted)+2.*L*l2_shifted;
r = sqrt(r2);
118
119
120
if (fabs(x_0+r) < R)
res = res + 2.*pi;
121
122
123
124
125
126
else if (fabs(x_0-r) < R)
{
arg = (r2+x_02-R2)/(2.*x_0*r);
if (fabs(arg)<=1) phi = res + 2.*acos(arg);
}
127
128
129
130
131
gsl_function F;
F.function = &f_inner;
F.params
= μ
140
132
133
134
135
3 Optik
if (R>x_0)
l1_start = 0;
else
l1_start = sqrt(sqr(x_0-R)+sqr(L))-L;
136
137
w = gsl_integration_workspace_alloc(100000);
138
139
140
gsl_integration_qawo(&F, l1_start, epsabs, epsrel, limit,
w, wf, &Int_inner_real_1, &abserr);
141
142
gsl_integration_workspace_free(w);
143
144
145
return Int_inner_real_1*res;
}
146
147
//-------------------------------------------------------------------------
148
149
main( int argc, char *argv[] )
150
151
{
152
153
154
155
156
157
158
159
160
//-- Definition der Variablen
int
limit = 1000;
int
n1, nmax;
double epsabs = 1.e-12;
double epsrel = 1.e-3;
double Int1_real, Int1_imag, Intensity;
double l2_start, l2_end, delta_l2, x_max, dx, abserr, delta;
double mu = 10;
161
162
163
gsl_integration_workspace *w;
gsl_function F;
164
165
166
ifstream in_stream;
ofstream out_stream;
167
168
169
170
171
172
173
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: kreisbl2 infile outfile\n";
exit(1);
}
174
175
176
177
178
179
180
181
182
183
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von kreisbl2.cpp\n";
inout(R,"R");
inout(L,"L");
inout(k,"k");
inout(nmax,"nmax");
inout(x_max,"x_max");
inout(delta,"delta");
inout(alpha,"alpha");
in_stream.close();
184
185
186
187
//-- Berechnung einiger benoetigter Parameter -dx = x_max / nmax;
R2 = sqr(R);
188
189
190
for (n1=0; n1<=nmax; n1++)
{
3.13 Beugung bei endlicher Kohärenzlänge
191
141
x_0 = (n1+0.5)*dx; x_02 = sqr(x_0);
192
193
194
195
196
197
198
199
200
201
202
203
if (R>x_0)
{
l2_start
l2_end
}
else
{
l2_start
l2_end
}
delta_l2 =
= 0;
= sqrt(sqr(R+x_0)+sqr(L))-L;
= sqrt(sqr(x_0-R)+sqr(L))-L;
= sqrt(sqr(x_0+R)+sqr(L))-L;
l2_end-l2_start;
204
205
206
207
w = gsl_integration_workspace_alloc(10000);
wf = gsl_integration_qawo_table_alloc(k, delta_l2,
GSL_INTEG_COSINE, 10000);
208
209
210
F.function = &f_outer_real;
F.params
= μ
211
212
213
gsl_integration_qawo(&F, l2_start, epsabs, epsrel, limit,
w, wf, &Int1_real, &abserr);
214
215
216
gsl_integration_workspace_free(w);
gsl_integration_qawo_table_free(wf);
217
218
219
220
w = gsl_integration_workspace_alloc(10000);
wf = gsl_integration_qawo_table_alloc(k, delta_l2,
GSL_INTEG_SINE, 10000);
221
222
223
F.function = &f_outer_imag;
F.params
= μ
224
225
226
gsl_integration_qawo(&F, l2_start, epsabs, epsrel, limit,
w, wf, &Int1_imag, &abserr);
227
228
229
gsl_integration_workspace_free(w);
gsl_integration_qawo_table_free(wf);
230
231
232
233
234
Intensity = (Int1_real+Int1_imag) * sqr(k/2./pi);
out_stream << x_0 << " " << Intensity << "\n";
cout
<< x_0 << " " << Intensity << "\n";
}
235
236
237
out_stream.close();
}
Wenn Sie den Programmcode jedoch aufmerksam studieren, werden Sie
einige Feinheiten bemerken, die für die erfolgreiche Berechnung des Interferenzmusters ausschlaggebend sind:
Zunächst fällt auf, dass zur Berechnung des Doppelintegrals (3.83) dieses
umgeformt wurde:
I = dl1 dl2 h(l1 )h(l2 ) exp(ik(l1 − l2 )) exp(−α|l1 − l2 |)
142
3 Optik
I / I0
2
1
0
0
0.5 10
-3
-3
10
-3
1.5 10
x0
Abb. 3.14. Beugungsmuster einer 1 mm großen, kreisförmigen Blende im Abstand von 10 cm. Die durchgezogene Linie entspricht der Situation unendliche
Kohärenzlänge, während die gestrichelte Linie einer Koherärenzlänge von 1 µm
entspricht
=
dl1
dl2 h(l1 )h(l2 ) (cos(kl1 ) cos(kl2 ) + sin(kl1 ) sin(kl2 ))
× exp(−α|l1 − l2 |)
(3.86)
Dies hat den Vorteil, dass sowohl für das innere als auch das äußere Integral
die uns schon bekannte Routine gsl integration qawo verwendet werden
kann. Außerdem wurden die Integrationsvariablen l1 bzw l2 um L verschoben,
um zu vermeiden, dass bei der Berechnung von r zwei fast gleich große Zahlen
subtrahiert werden müssen, was auf Kosten der Genauigkeit gehen würde.
Bei den Genauigkeitsanforderungen zur Berechnung des inneren bzw.
äußeren Integrals – festgelegt durch die Variablen epsabs und epsrel –
stellen Sie fest, dass diese Anforderungen für die innere Integrale um zwei
Zehnerpotenzen höher angesetzt wurden als bei den beiden äußeren Integralen. Dies ist nötig, da sonst die Ungenauigkeiten im inneren Integral eine
Konvergenz des äußeren Integrals verhindern.
Ein Zusatzprogramm kreisbl2 norm.cpp bewerkstelligt abschließend die
Normierung. Diese beiden Programme stellen keine besonderen Probleme dar,
weswegen wir sie hier nicht im Detail vorstellen – Sie finden Sie jedoch selbstverständlich auf der CD zum Buch.
In Abb. 3.14 erkennen Sie, dass das Beugungsmuster bei unendlicher
Kohärenzlänge (durchgezogene Kurve) genau Abb. 3.12 enspricht. Bei endlicher Kohärenzlänge wird das Beugungsmuster hingegen geglättet (gestrichelte Linie) – ein Effekt, der mit kleiner werdender Kohärenzlänge immer
ausgeprägter wird.
Übungen
3.1 Strahlengang durch an einer Linse
Das Programm strahl3.cpp konstruiert den Strahlengang durch eine Linse
für ein Strahlenbündel, das parallel zur optischen Achse auf die Linse fällt.
Dies entspricht einem unendlich weit entfernten Objekt mit Objekthöhe null.
Übungen
143
Modifizieren Sie dieses Programm dahingehend, dass die Strahlen zwar parallel zueinander aber in einem beliebigen Winkel zur optischen Achse einfallen
– diese Situation entspricht einem Objekt, das weiterhin unendlich weit entfernt ist, aber nicht auf der optischen Achse liegt.
3.2 Linsenfehler
Es gibt eine ganze Reihe weiterer Linsenfehler, die Sie mit strahl5.cpp untersuchen können – eine Aufstellung gibt Tabelle 3.1, wobei statt der Objekthöhe l der Objektwinkel φ zur Charakterisierung benutzt wird.
tan φ = l/d .
(3.87)
Tabelle 3.1. Linsenfehler
Bezeichnung
Größenordn. Bemerkung
Öffnungsfehler
∝ R3
Koma
Astigmatismus
∝ R2 tan φ
∝ R tan2 φ
Bildfeldwölbung ∝ R tan2 φ
Verzeichnung
∝ tan3 φ
auch sphärische Aberration, Strahlen am
Linsenrand werden zu stark gebrochen
Asymmetrie der Kaustik
es gibt statt einem Brennpunkt zwei
Brennstriche
die Bildfläche ist nicht mehr eben, sondern
gewölbt
der Abbildungsmaßstab hängt von der
Entfernung zur optischen Achse ab
Sie können durch Variation der Parameter (insbesondere des Linsenradius R und der Objekthöhe l) die einzelnen Linsenfehler betonen und so
getrennt untersuchen. Wer Kenntnisse in geometrischer Optik besitzt, kann
an geeigneter Stelle des Strahlengangs eine Blende einbringen und dadurch
die Linsenfehler reduzieren.
3.3 Regenbogen von tropfenförmigen Regentropfen
Sie können das Regenbogenprogramm so modifizieren, dass es statt Tropfen
in Form von Rotationsellipsoiden z.B. tropfenförmige Regentropfen annimmt,
z.B. etwas in der Form
r = r0 + r1 /(ϑ − ϑ0 ) ,
(3.88)
wobei θ der Winkel gegen die Vertikale ist. Für r0 = 0.8 und ϑ0 = 0.2 ergibt
sich ein schöner (wenn auch falscher) Tropfen (Abb. 3.15):
144
3 Optik
Abb. 3.15. Typische, aber falsche Tropfenform
3.4 Der zweite Regenbogen
Wie Sie sicherlich schon einmal gesehen haben, können unter günstigen Bedingungen auch zwei Regenbögen beobachtet werden. Um den sogenannten
zweiten Regenbogen zu bekommen, müssen Sie in unserem Programm die
Variable n ray max auf 2 (statt 1 beim ersten Regenbogen) setzen. Ermitteln
Sie die Intensität dieses zweiten Regenbogens im Vergleich zum ersten und
bestimmen Sie den Öffnungswinkel.
Wenn Sie einen Ausschnitt der beiden Regenbögen betrachten (Abb. 3.16),
stellen Sie zudem fest, dass die Reihenfolge der Farben bei den beiden verschieden ist.
Abb. 3.16. Ausschnittsvergrößerung vom ersten (linkes Bild) und zweiten Regenbogen (rechtes Bild). Deutlich zu sehen ist, dass die Reihenfolge der Farben beim
zweiten Regenbogen gegenüber dem ersten gerade invertiert ist – also rot innen und
blau außen liegt. (Eine Farbversion dieser Abbildung finden Sie übrigens auf der
Buch-CD im Verzeichnis figures)
3.5 Interferenz hinter einer Kreisscheibe
Das Interferenzmuster hinter einer Kreisscheibe ist quasi das Komplementär
zu dem der Kreisblende. Dieses Beugungsmuster direkt durch numerische
Integration über den Außenbereich auszurechnen ist schwierig, weil dieses
Übungen
145
Integral bis ins Unendliche ausgedehnt ist. Sie können aber die Tatsache ausnutzen, dass sich die Felder (also nicht die Intensitäten!) von der Kreisblende
(siehe Abschn. 3.11) und die des gesuchten Lichtfeldes zu den Feldern einer
ebenen Welle addieren müssen. Damit können Sie direkt das Interferenzmuster hinter einer Kreisscheibe angeben.
4 Statistische Physik
Bei einer Vielzahl physikalischer Probleme ist der Zustand aus mikroskopischer Sicht nicht bekannt – ein typisches Beispiel hierfür ist ein Gas, bei
dem der Zustand durch die Koordinaten und Impulse aller beteiligten Atome
gegeben wäre. In diesem Fall helfen uns die mikroskopischen Bewegungsgleichungen alleine also nicht weiter und es stellt sich die Frage, warum sich
ein Gas überhaupt immer gleich verhält. Die Antwort lautet, dass wir ein
Gas nicht mikroskopisch betrachten, sondern dieses außerordentlich komplexe System mit einer Vielzahl von Freiheitsgraden durch einige wenige Zustandsgrößen wie Druck oder Temperatur beschreiben, bei denen sich unsere
Unkenntnis durch die Vielzahl beteiligter Teilchen quasi herausmittelt. Die
Aufgabe der statistischen Physik ist es, diese Zusammenhänge herzustellen
und die makroskopischen Zustandsgleichungen mit den Methoden der Statistik auf die mikroskopischen Bewegungsgleichungen zurückzuführen.
4.1 Grundbegriffe der Statistik
Bevor wir zur Anwendung von statistischen Verfahren auf physikalische Probleme kommen, müssen wir zunächst einige mathematische Grundbegriffe
aus der Statistik klären. Der erste dieser Begriffe ist der des Zufallsexperiments. Dabei handelt es sich um ein Experiment, dessen Ausgang wir nicht
vorhersagen können und das wir – zumindest im Prinzip – beliebig oft wiederholen können. Ein typisches Beispiel ist das Werfen einer Münze, bei dem wir
vorher nicht sagen können, welche Seite anschließend nach oben zeigen wird.
In diesem Fall wären die Menge der möglichen Ereignisse gleich der Menge
mit den Elementen Kopf und Zahl. Ebensogut kann dieser Ereignisraum aber
auch natürliche Zahlen (beim Werfen eines Würfels) oder reelle Zahlen (zum
Beispiel Zeiten) enthalten. Insbesondere kann die Ergebnismenge endlich oder
abzählbar unendlich sein oder sogar ein Kontinuum bilden.
Das Ergebnis x eines konkreten solchen Zufallsexperiments wird Ereignis
genannt. Macht man nun viele identische Zufallsexperimente, kann man die
Wahrscheinlichkeit für ein bestimmtes Ereignis feststellen, indem man die
Zahl nx der Zufallsexperimente, bei denen dieses Ereignis eintrat, durch die
Zahl aller Zufallsexperimente N teilt:
148
4 Statistische Physik
Px = lim
N →∞
nx
.
N
(4.1)
Ist der Ereignisraum ein Kontinuum, so wird in den meisten Fällen dieser
Grenzwert null liefern, so dass man hier statt der Wahrscheinlichkeit selbst
die Wahrscheinlichkeitsdichte p definiert:
p(x) = lim
lim
∆x→0 N →∞
n(x, ∆x)
,
N
(4.2)
wobei n(x, ∆x) die Zahl der Ereignisse im Intervall [x − ∆x/2, x + ∆x/2] ist.
Da wir wissen, dass in jedem Zufallsexperiment irgendein Ereignis eintritt,
muss die Summe aller Wahrscheinlichkeiten gleich eins sein:
Px = 1 ,
(4.3)
x
bzw. im Fall eines Kontinuums:
dx p(x) = 1 .
(4.4)
In den nachfolgenden Abschnitten werden wir zur besseren Übersichtlichkeit nur die Ausdrücke für diskrete Ereignisräume angeben, sofern der
entsprechende Ausdruck für kontinuierliche Räume offensichtlich ist.
4.2 Bestimmung von Wahrscheinlichkeiten
An dieser Stelle wollen wir die empirische Bestimmung einer Wahrscheinlichkeit p durch Wiederholung des betreffenden Zufallsexperimentes genauer
unter die Lupe nehmen. Gegeben sei ein Zufallsexperiment mit zwei möglichen Ausgängen, die wir positiv und negativ nennen. Die Wahrscheinlichkeit
für den postiven Ausgang sei p und entsprechend die Wahrscheinlichkeit für
den negativen Ausgang 1 − p. Wenn wir dieses Experiment N -mal wiederholen, so erhalten wir n+ positive und n− negative Ausgänge, woraus wir
empirisch die Wahrscheinlichkeit p abschätzen:
pemp =
n+
.
N
(4.5)
Im Extremfall können wir jedoch immer den negativen Ausgang erhalten,
obwohl p nicht null ist, d.h. diese Bestimmung ist mit einer Unsicherheit
behaftet, die daher rührt, dass wir N zwar groß wählen können, aber nie den
Grenzübergang N → ∞ wirklich vollziehen können.
Um dies zu quantifizieren, definieren wir eine Wahrscheinlichkeit PN (n)
(wir lassen im Folgenden den Index + der Übersichtlichkeit wegen weg) dafür,
dass wir bei N Zufallsexperimenten genau n positive Ereignisse beobachten.
4.2 Bestimmung von Wahrscheinlichkeiten
149
Sofern unsere Zufallsexperimente alle voneinander unabhängig sind (Näheres
zu diesem Begriff siehe Abschn. 4.4), berechnet sich diese Wahrscheinlichkeit
zu
N
(4.6)
PN (n) =
pn (1 − p)N −n ,
n
wobei
N
n
=
N!
n! (N − n)!
(4.7)
die Zahl der Möglichkeiten angibt, die n positiven Ereignisse auf die N Zufallsexperimente zu verteilen.
Uns interessieren nun der Mittelwert und die Varianz der empirisch bestimmten Wahrscheinlichkeit, also benötigen wir
n
(4.8)
P1 =
PN (n)
N
n
n 2
P2 =
PN (n) .
(4.9)
N
n
Um diese Größen zu berechnen, betrachten wir zunächst die Funktion
fN (α, β) =
N N
n=0
n
αn β N −n = (α + β)N .
(4.10)
Wie wir sehen, liefert der erste Ausdruck für fN (α = p, β = 1 − p) gerade
den Mittelwert von eins und konsequenterweise liefert die rechte Seite das
korrekte Ergebnis (p + 1 − p)N = 1. Entsprechend erhalten wir
α ∂
(4.11)
P1 =
fN (α, β)
α=p
N ∂α
β=1−p
p
= N (p + 1 − p)N −1
(4.12)
N
=p
(4.13)
α ∂
α ∂
fN (α, β) (4.14)
P2 =
α=p
N ∂α N ∂α
β=1−p
p
(1 + p(N − 1)) .
=
N
(4.15)
Daraus erhalten wir den Mittelwert der empirisch bestimmten Wahrscheinlichkeit pemp zu
(4.16)
pemp = p
und deren Varianz zu
p2emp − pemp 2 =
1 p − p2 .
N
(4.17)
150
4 Statistische Physik
Beachten Sie bitte, dass die Varianz proportional
zu 1/N abfällt, das heißt
√
die Standardabweichung fällt nur mit 1/ N ab – um also einen doppelt so
genauen Wert von p zu erhalten müssen Sie viermal so viele Zufallsexperimente durchführen. Interessant ist außerdem, dass die Unsicherheit für p = 0
und für p = 1 verschwindet (hier ist man sich sicher, dass alle Zufallsexperimente negativ bzw. positiv ausgehen) und für p = 1/2 ihr Maximum 1/4N
annimmt.
4.3 Mittelwerte und Momente
Wenn auf dem Ergebnisraum eine Addition erklärt ist, so macht es Sinn, den
Mittelwert zu definieren:
x =
xPx .
(4.18)
x
Statt dem Mittelwert von x selbst können wir natürlich auch nach dem Mittelwert von jeder Funktion von x fragen:
f (x)Px ,
(4.19)
f (x) =
x
insbesondere nach den Mittelwerten von x2 , x3 . . . Diese Mittelwerte heißen
zweites, drittes, viertes Moment und so weiter. Das erste Moment ist gleich
dem Mittelwert (4.18), das nullte Moment ist immer identisch eins (4.3). An
dieser Stelle muss betont werden, dass der Mittelwert f (x) im Allgemeinen
verschieden von der Funktion des Mittelwertes f (x) ist.
4.4 Bedingte Wahrscheinlichkeiten und Korrelationen
Die konzeptionell schwierigsten Begriffe und deswegen auch die am häufigsten missinterpretierten Grundbegriffe, die wir für dieses Kapitel benötigen,
sind die der bedingten Wahrscheinlichkeit und der Korrelation. Betrachten
wir hierzu ein Zufallsexperiment, dessen Ergebnisse als N -Tupel von Zahlen
dargestellt werden können. Dabei könnte es sich zum Beispiel um das Werfen von N unterscheidbaren Würfel handeln, bei denen wir die Augenzahl
des n-ten Würfel an die n-te Stelle des N -Tupels schreiben. Andere Beispiele wären die Energien zweier in einem Experiment erzeugter Photonen oder
zwei Komponenten des Geschwindigkeitsvektors eines Teilchens. Die beiden
Begriffe bedingte Wahrscheinlichkeit und Korrelation dienen der Klärung der
statistischen Unabhängigkeit von solchen Einzelereignissen.
Neben den Wahrscheinlichkeiten für die Einzelergebnisse einer konkreten Komponenten des N -Tupels können wir auch sogenannte Verbundwahrscheinlichkeiten bilden, dass z.B. der erste Würfel Ergebnis a und der zweite
Würfel Ergebnis b liefert. Diese Verbundwahrscheinlichkeit können wir uns
4.4 Bedingte Wahrscheinlichkeiten und Korrelationen
151
nun zusammensetzen aus der Wahrscheinlichkeit Pa für Ergebnis a beim ersten Würfel mal der Wahrscheinlichkeit Pb|a dafür, dass der zweite Würfel b
liefert, unter der Bedingung, dass das Ergebnis des ersten Würfels a ist. Diese
Zusatzbedingung ist enorm wichtig, denn es ist nicht a priori selbstverständlich, dass die Ergebnisse der beiden Würfel voneinander unabhängig sind.
Wenn diese Zusatzbedingung zu keiner Änderung der Wahrscheinlchkeiten
führt, wenn also
Pb|a = Pb
für alle a, b
(4.20)
gilt, dann spricht man von unabhängigen Ereignissen. Bei zwei normalen
Würfeln muss beispielsweise das Ergebnis beim einen Würfel unabhängig
vom Ergebnis beim zweiten Würfel sein. Ist die Bedingung (4.20) für irgendein Paar a, b nicht erfüllt, sprechen wir hingegen von korrelierten Ereignissen.
Der Nachteil dieser Formel ist, dass wir sie im Zweifelsfall für alle a und
alle b verifizieren müssen, bevor wir sicher sein können, dass die beiden Ereignisse voneinander unabhängig sind. Aus diesem Grund ist die Berechnung
der Korrelation
(4.21)
Cxy = xy − xy
eine schöne Alternative, auch wenn deren Aussage streng genommen nicht so
weit geht wie die Forderung (4.20). Um die Bedeutung von Cxy zu verstehen, berechnen wir die Korrelation unter der Voraussetzung, dass die beiden
Ereignisse x und y voneinander unabhängig sein sollen:
xypx py|x −
xpx
ypy
Cxy =
x,y
=
x,y
x
xypx py −
x
xpx
y
ypy
(4.22)
y
=0.
Wenn dieser Ausdruck also nicht verschwindet, können wir sicher sein,
dass die beiden betrachteten Ereignisse miteinander korreliert sind – umgekehrt können wir leider aus Cxy = 0 nicht sicher auf statistische Unabhängigkeit schließen.
Als letzten Begriff in diesem Abschnitt wollen wir die Korrelationsfunktion einführen. Dazu stellen wir uns vor, dass unsere Zufallsvariablen x und
y von einem Parameter, z.B. der Zeit t, abhängen. Als Beispiel können Ort
und Geschwindigkeit eines Teilchens dienen, dessen Bewegung auf Grund
von Stößen mit anderen, nicht beobachteten Teilchen zufälliger Natur ist.
Die Korrelationsfunktion ist nun definiert durch
Cxy (t, ∆t) = x(t)y(t + ∆t) − x(t)y(t + ∆t) .
(4.23)
In stationären Situationen, z.B. im thermischen Gleichgewicht, kann diese
Korrelationsfunktion nicht von der Zeit t, sehr wohl aber von der Zeitdifferenz
∆t abhängen.
152
4 Statistische Physik
4.5 Dynamik bei statistischen Problemen
Eine stochastische Beschreibung physikalischer Probleme wird notwendig,
wenn die Anfangsbedingung nicht genau bekannt ist oder die Dynamik selbst
stochastischer Natur ist, d.h. die Bewegungsgleichung eine Zufallskomponente enthält. Letzteres bedarf einer Erklärung, denn klassisch liegt die Dynamik eindeutig fest, wenn alle Zustandsvariablen bekannt sind – es besteht
also gar kein Spielraum für einen statistischen Charakter der Bewegung. Der
einzige Ausweg aus diesem logischen Dilemma, ist, dass wir nur einen Teil
der Zustandsvariablen erfasst haben und einen relevanten Teil des Systems
unterschlagen haben.
Als Illustration dessen, was wir damit meinen, ziehen wir die Brownsche
Bewegung heran. Dabei handelt es sich um die Bewegung von kleinen, aber
im Mikroskop noch wahrnehmbaren Teilchen (z.B. Bärlappsporen) in einer
Flüssigkeit. Der Botaniker Robert Brown stellte 1827 fest, dass diese Teilchen eine Zitterbewegung vollführen, konnte jedoch keinen Grund für dieses
Zittern finden.1 Die Erklärung lieferte Einstein im Jahre 1905: die Bärlappsporen werden von den Flüssigkeitsmolekülen, die im Mikroskop nicht optisch
aufgelöst werden können, gestoßen. In diesem Fall sind also die Moleküle der
Flüssigkeit der Teil, den wir nicht berücksicht haben und auch gar nicht
können, ohne ein nicht handhabbares System von 1020 gekoppelten Differentialgleichungen zu bekommen.
Wie sieht nun die Struktur der Bewegungsgleichung eines solchen Teilchens aus? Sie hat zum einen einen deterministischen und zum anderen einen
stochastischen Anteil:
d
x = ẋdet + ẋstoch .
(4.24)
dt
Hierbei umfasst der Vektor x alle Zustandsvariablen, d.h. z.B. bei einem
mechanischen System alle Orte und Impulse.
Eine solche Differentialgleichung mit einem stochastischen Anteil heißt
stochastische Differentialgleichung [26] und sie macht nur Sinn, wenn man
die statistischen Eigenschaften dieses Terms kennt. Im folgenden Abschnitt
werden wir sehen, wie mit einer stochastischen Differentialgleichung gerechnet
werden kann und wie man sie numerisch implementiert.
Einen völlig anderen Zugang erhält man, wenn man sich bereits bei der
Bewegungsgleichung auf Mittelwerte – bzw. allgemeiner Momente – der Zustandsvariablen beschränkt. Auf diese Weise erhält man aus (4.24) formal
z.B. für den Mittelwert der Variablen xn :
d
xn = ẋn,det + ẋn,stoch .
dt
1
(4.25)
Tatsächlich war Brown nicht der erste, der eine solche Beobachtung machte, aber
der erste, der zum Schluss kam, dass es sich dabei nicht um ein Anzeichen von
Leben handelt, sondern jedes Material, sofern es nur zu einem sehr feinen Pulver
verrieben wird, dieses Phänomen zeigt.
4.6 Der Random-Walk
153
Ob die beiden Terme auf der rechten Seite ausgewertet werden können und
ob sie auf ein geschlossenes Differentialgleichungssystem führen, ist von Fall
zu Fall unterschiedlich. In einigen Fällen hilft auch die Näherung
f (x) ≈ f (x) ,
(4.26)
die allerdings voraussetzt, dass die Wahrscheinlichkeitsverteilung im Phasenraum gut lokalisiert ist.
Der dritte und letzte Zugang, den wir an dieser Stelle ansprechen wollen,
ist eine Bewegungsgleichung für die Wahrscheinlichkeitsverteilung P , die sogenannte Mastergleichung. Alle drei Zugänge sowie deren numerische Implementation werden wir im folgenden Abschnitt am Beispiel des Random-Walk
diskutieren.
4.6 Der Random-Walk
Als Einstieg in die statistische Physik beginnen wir mit einem einfachen Modell für die Brownsche Bewegung, dem sogenannten Random-Walk, was soviel
wie Zufallsmarsch bedeutet.
Dazu betrachten wir Teilchen, die sich entlang einer Achse bewegen
können. Der Einfachheit halber nehmen wir zunächst an, dass diese Bewegung in diskreten (und äquidistanten) Schritten erfolgt: zum Zeitpunkt 1
bewegt sich das Teilchen um eine Einheit nach links oder rechts, desgleichen
zum Zeitpunkt 2, 3 und so weiter. Der entscheidende Punkt ist nun, dass die
Frage, in welche Richtung die Bewegung erfolgt, zufällig entschieden wird.
Bevor wir dieses Problem numerisch angehen werden, wollen wir zunächst
an seinen physikalischen Hintergrund erinnern und es außerdem analytisch
lösen. Beginnen wir mit der physikalischen Rechtfertigung dieses eigentlich
recht seltsamen Modells (das so wichtig ist, dass jeder Physikstudent mehrmals während seines Studiums damit belästigt wird). Das Teilchen, dessen
Bewegung wir betrachten, ist ein relativ großes Partikel in einer Flüssigkeit.
Die Zufallsschritte der Größe ±1 entsprechen der Zitterbewegung, die durch
Stöße mit kleineren, nicht mehr wahrnehmbaren Teilchen (den Flüssigkeitsmolekülen) herrühren (siehe Abschn. 4.5).
Kommen wir nun zur analytischen Behandlung des Problems. Aufgrund
des stochastischen Charakters der Bewegungsgleichung können wir die Bahn
des Teilchens nicht vorhersagen. Das einzige, was wir angeben können sind
Wahrscheinlichkeiten oder daraus resultierende Mittelwerte. Beginnen wir
mit dem Mittelwert m1 = x. Dieser Mittelwert hängt zunächst davon ab,
wo sich das Teilchen zum Zeitpunkt n = 0 aufgehalten hat und wie viele Zeiteinheiten (sprich Sprünge) inzwischen vergangen sind. Ohne Beschränkung
der Allgemeinheit setzen wir das Teilchen zu Anfang in den Ursprung und
berechnen
(4.27)
m1 (n) = m1 (n − 1) + ∆x .
154
4 Statistische Physik
Hierbei ist ∆x der Sprung, den das Teilchen bei einem Zeitschritt macht. Da
die Wahrscheinlichkeit für einen Sprung nach rechts (∆x = +1) genauso groß
ist wie für einen Sprung nach links (∆x = −1) ist der Mittelwert ∆x gleich
null. Also gilt
m1 (n) = m1 (n − 1) = m1 (n − 2) = . . . = m0 = 0 .
(4.28)
Das Teilchen bewegt sich also im Mittel nicht von der Stelle. Zur Beantwortung der Frage, wie weit sich das Teilchen nach n Schritten vom Ausgangspunkt entfernt hat, ist der Mittelwert x also ungeeignet. Die Tatsache,
dass dieser null ist, drückt ja nur aus, dass das Teilchen mit der gleichen
Wahrscheinlichkeit links vom Ursprung wie rechts vom Ursprung ist. Zur Berechnung eines mittleren Abstandes müssen wir den Mittelwert m2 = x2 berechnen.
m2 (n) = x(n)2 = (x(n − 1) + ∆x(n))2 = x(n − 1)2 + x(n − 1)∆x(n) + ∆x(n)2 .
(4.29)
(4.30)
(4.31)
Der erste Ausdruck ist identisch mit m2 (n − 1). Der zweite ist wegen der Unabhängigkeit von aktueller Position und nächstem Sprung identisch mit dem
Produkt der Mittelwerte x(n − 1)∆x(n). Da ∆x(n) null ist, verschwindet dieser Term. Der letzte Ausdruck ist der Mittelwert von eins, denn egal
ob ∆x(n) den Wert 1 oder -1 annimmt, das Quadrat ist in beiden Fällen 1.
Insgesamt erhalten wir also
m2 (n) = m2 (n − 1) + 1 = m2 (n − 2) + 2 = . . . = m2 (0) + n = n . (4.32)
√
Das bedeutet, dass der mittlere Abstand d = m2 nur mit der Wurzel der
Zeit anwächst – im Gegensatz zu einer linearen Zunahme bei einer deterministischen Bewegung (mit Ausnahme singulärer Punkte).
4.6.1 Stochastische Differentialgleichung des Random-Walk
Schreiten wir nun zu einer ersten (noch auszubauenden) numerischen Implementierung des Random-Walks:
1
2
3
4
5
/**************************************************************************
* Name:
rw1.cpp
*
* Zweck:
Simuliert den Random-Walk - ohne Skalierung
*
* verwendete Bibliothekl: GSL
*
**************************************************************************/
6
7
8
9
10
11
12
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_rng.h>
<gsl/gsl_randist.h>
<math.h>
"tools.h"
4.6 Der Random-Walk
155
13
14
using namespace std;
15
16
int main( int argc, char *argv[] )
17
18
19
20
{
const int N_max = 1000000;
const int n_bins = 100;
21
22
23
24
25
26
27
28
//-- Definition der Variablen
int
histo[2*n_bins+1], n, N, n_step, n_step_max;
double x[N_max], xmean, xvar, sum1, sum2, dx, d_bins, rand;
ifstream in_stream;
ofstream out_stream1, out_stream2;
const gsl_rng_type * T;
gsl_rng * r;
29
30
31
32
33
34
35
36
37
//-- Fehlermeldung,
if (argc<4)
{
cout << " Aufruf:
cout << "
cout << "
exit(1);
}
wenn Input- und Outputfilename nicht uebergeben wurden
rw1 infile outfile1 outfile2\n";
outfile1: Zeitentwickl. von Mittelwert und Varianz\n";
outfile2: Histogramm der Schlussverteilung\n";
38
39
40
41
42
43
44
45
46
47
48
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream1.open(argv[2]);
out_stream2.open(argv[3]);
out_stream1 << "! Ergebnis-Datei
out_stream2 << "! Ergebnis-Datei
in_stream >> n_step_max;
out_stream1 << "! n_step_max = "
out_stream2 << "! n_step_max = "
in_stream.close();
generiert von rw1\n";
generiert von rw1\n";
<< n_step_max << "\n";
<< n_step_max << "\n";
49
50
51
52
53
//-- Initialisierung des Zufallszahlengenerators
gsl_rng_env_setup();
T = gsl_rng_default;
r = gsl_rng_alloc (T);
54
55
56
//-- Anfangsbedingungen
for (N=0; N<N_max; N++) x[N] = 0;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//-- Zeitschleife -for (n_step=1; n_step<=n_step_max; n_step++)
{
sum1 = 0; sum2 = 0;
for (N=0; n<N_max; N++)
{
rand = gsl_rng_uniform (r);
if (rand > 0.5) dx = 1;
else dx = -1;
x[N] = x[N] + dx;
sum1 = sum1 + x[N];
sum2 = sum2 + sqr(x[N]);
}
xmean = sum1 / N_max;
156
72
73
74
4 Statistische Physik
xvar = sum2 / N_max - sqr(xmean);
out_stream1 << n_step << " " << xmean << " " << xvar << "\n";
}
75
76
77
78
79
80
81
82
83
84
85
86
for (n=0; n<=2*n_bins+1; n++) histo[n] = 0;
d_bins = sqrt(xvar) * 5 / n_bins;
for (N=0; N<N_max; N++)
{
n = int( x[N] / d_bins + n_bins + 0.5);
if ((n>=0) && (n<=2*n_bins+1)) histo[n]++;
}
for (n=0; n<=2*n_bins+1; n++)
out_stream2 << (n-n_bins) * d_bins << " " << histo[n] / N_max / d_bins
<< " " << exp( -(sqr(n-n_bins)*d_bins) / 2 / xvar )
/ sqrt( 2 * pi * xvar );
87
88
89
90
out_stream1.close();
out_stream2.close();
}
Aufgrund des statistischen Charakters des Problems hat die Bewegung eines einzelnen Teilchens nur begrenzte Aussagekraft. Deswegen simulieren wir
im Programm rw1.cpp gleich die Dynamik einer Vielzahl von Teilchen (ein
Ensemble mit identischen Anfangsbedingungen, dessen Größe durch die Variable n max festgelegt wird. Die zufälligen Stöße sorgen nun dafür, dass sich
diese Teilchen voneinander wegbewegen und eine immer weiter ausgedehnte Verteilung bilden. Dieser Zufallsprozess spiegelt sich in den Zeilen 64–66
wider: gsl rng uniform liefert eine gleichverteilte Zufallszahl im Intervall
[0, 1]; die nachfolgende Fallunterscheidung gewährleistet, dass mit gleicher
Wahrscheinlichkeit ein Schritt nach links bzw. nach rechts gemacht wird. In
der Ergebnisdatei rw1.res speichern wir den Mittelwert und die Varianz des
Aufenthaltsortes ab. Wenn sowohl unsere analytische Berechnung als auch
unsere numerische Implementierung korrekt sind, sollte ersterer bei etwa 0
bleiben, und die zweite linear ansteigen. Zusätzlich legen wir eine weitere
Ergebnisdatei rw1 1.res an, die ein Histogramm des Endzustandes abspeichert: Dazu definieren wir ein Feld histo, dessen Einträge der Häufigkeit
von Endwerten in einem bestimmten x-Intervall entsprechen. Das heißt, dass
histo(0) gleich der Teilchen ist, die sich am Ende der Simulation im Intervall [-d bins/2,d bins/2] befunden haben, und allgemeiner ist histo(n)
die Zahl der Teilchen im Intervall [(n-1/2)*d bins, (n+1/2)*d bins].
Doch zunächst zum zeitlichen Verlauf des Mittelwertes und der Varianz
des Aufenthaltsortes. Wir sehen an der Skala in Abb. 4.1(a), dass der Mittelwert ungefähr bei null bleibt. Dieser Mittelwert ließe sich auf noch niedrigere
Werte drücken, indem man die Zahl n max der Teilchen erhöht. Entscheidend ist auch eigentlich nicht, dass der ermittelte Mittelwert klein (gegen
die typischen Einzelwerte) ist, sondern die Tatsache, dass dieser Mittelwert
im Grenzfall unendlich großer Ensembles n max→ ∞ verschwindet – davon
sollte sich der Leser überzeugen, indem er Ergebnisse von dem auf der CD
befindlichen Programm rw1.cpp für mehrere Werte von n max vergleicht.
4.6 Der Random-Walk
0.01
100
(a)
x
(b)
2
x
0.0
157
50
-0.01
0
0
25
50
75
n
100
0
25
50
75
100
n
Abb. 4.1. Mittelwert (a) und Varianz (b) des Aufenthaltsortes eines Teilchens
beim Random-Walk. Das Ensemble umfasst 106 Teilchen
Der lineare Verlauf (mit der vorhergesagten Steigung von 1) im rechten
Diagramm (Abb. 4.1(b)) entspricht genau unseren Erwartungen, so dass wir
uns eigentlich sicher sein können, die Grundform des Random-Walk richtig
implementiert zu haben. Schauen wir uns jedoch das Histogramm das Endzustandes an:
Abb. 4.2. Histogramm des Endzustandes nach einem RandomWalk (n=100,
n max=106 ), wobei die Schrittweite beim Einzelschritt ±1 beträgt. Zum Vergleich
wurde eine Gaußfunktion mit gleicher Varianz eingezeichnet
Wenn wir genau hinsehen, stellen wir fest, dass das Histogramm keine
glatte Kurve ist, sondern eine Reihe von Spitzen bei den geraden Zahlen, was
vielleicht nicht das ist, was Sie erwartet hätten. Das muss aber so sein, weil
–
die Wahrscheinlichkeitsverteilung nur auf der Menge der ganzen Zahlen
definiert ist, eine glatte Funktion also gar nicht im Bereich des möglichen
liegt,
– unser Teilchen vom Ursprung startend eine gerade Zahl von Sprüngen um
eine Einheit gemacht hat und sich deshalb nicht an einem ungeraden n
befinden kann.
Aus diesem Grund wollen wir unser Modell für den Random-Walk später
verbessern, bevor wir es zur Analyse physikalischer Fragestellungen verwenden werden. Zunächst jedoch werden wir an diesem einfachsten Beispiel noch
158
4 Statistische Physik
die Mastergleichung erarbeiten und in einem Computerprogramm implementieren.
4.6.2 Mastergleichung des Random-Walks
Die Mastergleichung ist, wie schon gesagt, die Bewegungsgleichung für die
Wahrscheinlichkeitsverteilung P , die angibt mit welcher Wahrscheinlichkeit
man das Teilchen an einem bestimmten Ort findet. Da sich das Teilchen in
unserem Fall aufgrund der festgeschriebenen Schrittweite von ±1 immer nur
bei 0, ±1, ±2, . . . aufhalten kann, ist auch die Wahrscheinlichkeitsverteilung
P nur an diesen diskreten Punkten definiert. Da außerdem die Schritte nur zu
festgelegten Zeiten 1, 2, . . . erfolgen, ist auch die Zeit diskret. Zusammengefasst hat die Wahrscheinlichkeitsverteilung P also zwei diskrete Argumente,
wovon eines der Zeit und das andere dem Ort entspricht.
Die Mastergleichung muss nun angeben, wie man aus allen P (n, m) zu
einem festen Zeitpunkt n die Verteilung P (n + 1, m) zum nächsten Zeitpunkt
ausrechnet. In unserem Fall ist das verhältnismäßig einfach, da sich ein Teilchen nur dann am Ort m befinden kann, wenn
es zum vorhergehenden Zeitpunkt am Ort m − 1 war und es einen Schritt
nach rechts gemacht hat, oder
– es zum vorhergehenden Zeitpunkt am Ort m + 1 war und es einen Schritt
nach links gemacht hat.
–
Da die Wahrscheinlichkeiten für einen Schritt nach links bzw. nach rechts
beide 1/2 sind, ergibt sich die gesuchte Wahrscheinlichkeitsverteilung zum
Zeitpunkt n + 1 als:
P (n + 1, m) =
1
[P (n, m − 1) + P (n, m + 1)] .
2
(4.33)
Das ist bereits die gesuchte Mastergleichung, deren Umsetzung in ein Computerprogramm keine besonderen Schwierigkeiten bereitet:
1
2
3
4
5
/**************************************************************************
* Name:
master1.cpp
*
* Zweck:
Loest die Mastergleichung des Random-Walk - ohne Skalierung *
* verwendete Bibliothek: keine
*
**************************************************************************/
6
7
8
9
10
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
"tools.h"
11
12
using namespace std;
13
14
int main( int argc, char *argv[] )
15
16
17
{
const int n_max = 100;
4.6 Der Random-Walk
159
18
19
20
21
22
23
//-- Definition der Variablen
int
n, n_step, n_step_max;
double P[n_max], P1[n_max], x, xmean, xvar, sum1, sum2;
ifstream in_stream;
ofstream out_stream;
24
25
26
27
28
29
30
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<4)
{
cout << " Aufruf: master1 infile outfile\n";
exit(1);
}
31
32
33
34
35
36
37
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von master1.cpp\n";
inout(n_step_max,"n_step_max");
in_stream.close();
38
39
40
41
//-- Anfangsbedingungen
for (n=0; n<n_max; n++) P[n] = 0;
P[n_max/2] = 1;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//-- Zeitschleife -for (n_step=1; n_step<=n_step_max; n_step++)
{
P1[0] = 0.5 * P[1];
P1[n_max-1] = 0.5 * P[n_max-2];
for (n=1; n<n_max-1; n++) P1[n] = 0.5 * (P[n-1]+P[n+1]);
sum1 = 0; sum2 = 0;
for (n=0; n<n_max; n++)
{
x = n-n_max/2;
P[n] = P1[n];
sum1 = sum1 + x * P[n];
sum2 = sum2 + sqr(x) * P[n];
}
xmean = sum1;
xvar = sum2 - sqr(xmean);
cout << n_step << " " << xmean << " " << xvar << "\n";
out_stream << n_step << " " << xmean << " " << xvar << "\n";
}
62
63
out_stream.close();
64
65
66
67
68
69
70
71
out_stream.open(argv[3]);
for (n=0; n<n_max; n++)
out_stream << n-n_max/2 << " " << P[n] << " "
<< exp( -(sqr(n-n_max/2)) / 2 / xvar )
/ sqrt( 2 * pi * xvar ) << "\n";
out_stream.close();
}
Wenn wir dieses Programm rechnen lassen, stellen wir fest, dass der lineare Anstieg der Varianz mit der Zeit perfekt, d.h. ohne statistisches Rauschen
wiedergegeben wird. Der Grund liegt natürlich darin, dass die Bewegungsglei-
160
4 Statistische Physik
chung für die Wahrscheinlichkeitsverteilung P kein stochastisches Element
enthält, sondern der stochastische Charakter des Random-Walks lediglich
darin zum Ausdruck kommt, dass die Verteilung P nicht scharf sondern verschmiert ist.
Zumindest wenn wir die Mastergleichung numerisch lösen, haben wir diesen enormen Vorteil mit zwei Nachteilen erkauft:
–
Anstatt der Lösung einer Bewegungsgleichung haben wir es nun mit einem System gekoppelter Gleichungen zu tun. Insbesondere bei Systemen mit mehreren dynamischen Variablen steigt der numerische Aufwand
enorm an, weil die Wahrscheinlichkeitsverteilung ein mehrdimensionales
Feld darstellt.
– Die numerisch gewonnene Lösung ist nur richtig, solange die Wahrscheinlichkeitsverteilung P nicht den Rand des berücksichtigten räumlichen Bereiches erreicht. Im vorliegenden Fall ist das der Fall, solange n step <
n max ist.
Neben der Entwicklung des Mittelwertes und der Varianz liefert das Programm master1.cpp auch die Wahrscheinlichkeitsverteilung P selbst, die
unserem Histogramm in Abb. 4.2 entspricht:
Abb. 4.3. Wahrscheinlichkeitsverteilung des Endzustandes nach einem RandomWalk, wobei die Schrittweite beim Einzelschritt ±1 beträgt. Die durchgezogene
Linie ist eine Gaußfunktion mit der selben Varianz
Der Vorteil der Lösung der Mastergleichung statt der stochastischen Differentialgleichung liegt darin, dass man nicht gezwungen ist, sehr große Ensembles zu benutzen, um zu genauen Ergebnissen zu gelangen. Man löst eine
Bewegungsgleichung und erhält ein im Rahmen der Rechengenauigkeit exaktes Ergebnis.
4.6.3 Verbesserung des Random-Walk-Modells
Sowohl in Abb. 4.2 als auch in Abb. 4.3 spiegelt sich eine Unzulänglichkeit
unseres bisherigen Modells wieder, die wir bereits angesprochen haben: Zu
einem gegebenen Zeitpunkt hält sich das Teilchen entweder nur an einem
4.6 Der Random-Walk
161
geraden oder nur an einem ungeraden Ort auf – das heißt, dass die Wahrscheinlichkeitsverteilung P diesen untypischen, gezackten Verlauf hat, der in
den beiden Abbildungen zum Ausdruck kommt.
Als Verbesserung bietet sich an, neben den beiden Sprüngen um ±1 auch
zu erlauben, dass das Teilchen an seinem Ort verweilt. Dies ist Gegenstand
der Übungsaufgabe 4.1. Hier wollen wir sofort den Übergang zu Gauß-förmig
verteilten Sprungweiten machen. Dies bedeutet, dass wir lediglich die Zeilen
...
65
66
67
rand = gsl_rng_uniform (r);
if (rand > 0.5) dx = 1;
else dx = -1;
...
im Programm rw1.cpp durch
...
65
dx = gsl_rng_gaussian(r,dv_0);
...
ersetzen müssen. Sie finden das so entstandene Programm als rw2.cpp auf
der beigelegten CD-ROM. Dabei haben wir gleichzeitig die Größe unseres
Ensembles drastisch reduziert (von 106 auf 104 Teilchen), um im zugehörigen Histogramm des Endzustandes (Abb. 4.4) überhaupt einen Unterschied
zwischen dem Ergebnis der Simulation (durchgezogene Kurve) und der theoretischen Vorhersage (eine Gaußfunktion, gestrichelte und dünner gezeichnete
Kurve) zu sehen.
Abb. 4.4. Histogramm des Endzustandes nach einem Random-Walk, wobei die
Schrittweite beim Einzelschritt Gauß-förmig verteilt ist. Zum Vergleich wurde gestrichelt die vorhergesagte Gaußfunktion eingezeichnet
Zu einer kompletten Implementierung ist es jedoch noch nötig, dass wir
die Verbindung zu einer physikalischen Problemstellung herstellen – dabei
werden wir feststellen, dass wir uns noch Gedanken über die korrekte Skalierung der Sprungweite mit dem Zeitintervall ∆t machen müssen, die wir
bisher einfach zu 1 gesetzt hatten.
162
4 Statistische Physik
Als naheliegendste Realisation wählen wir die Diffusion, genauer gesagt
die Bewegung eines freien Teilchens, bei dem nicht wie bisher dessen Ort
sondern dessen Geschwindigkeit einer Diffusion gemäß
v 2 = Dt
(4.34)
unterliegt. Unser Teilchen bekommt nun also Stöße, bei denen sich dessen
Geschwindigkeit stochastisch ändert. Diese führen zu einem nicht deterministischen Term in der Bewegungsgleichung
d
v = ξ(t) ,
dt
(4.35)
für die Geschwindigkeit v, die wir der Einfachheit halber skalar ansetzen,
was physikalisch bedeutet, dass wir die Bewegung auf eine Raumdimension
reduzieren. In (4.35) haben wir uns darüber hinaus auf eine freie Bewegung
eingeschränkt, so dass die Bewegungsgleichung keinen determinstischen Anteil hat. Der stochastische Term ξ(t) ist mittelwertsfrei
ξ(t) = 0
(4.36)
und hängt streng genommen von den Details beim Stoß ab, in vielen Fällen
ist jedoch die Markov-Näherung erlaubt:
ξ(t)ξ(t ) = D δ(t − t ) .
(4.37)
Integrieren wir (4.35) über ein kleines Zeitintervall ∆t so erhalten wir:
t+∆t
dt ξ(t )
v(t + ∆t) = v(t) +
(4.38)
t
= v(t) + Λ(∆t) .
(4.39)
Die neu definierte Größe Λ enthält die über das Zeitintervall [t, t + ∆t] akkumulierten Stöße und ist selbst eine Zufallszahl mit Gauß-förmiger Wahrscheinlichkeitsverteilung. Für die Implementierung im Computerprogramm
benötigen wir den Mittelwert
t+∆t
dt ξ(t ) Λ =
(4.40)
t
=0
(4.41)
sowie die Varianz
t+∆t
Λ =
2
t+∆t
dt ξ(t )ξ(t ) dt
t
= D∆t .
(4.42)
t
(4.43)
4.6 Der Random-Walk
163
Da die Standardabweichung die Wurzel der Varianz ist, bedeutet (4.43), dass
die Schrittweite nur mit der Wurzel von ∆t zunimmt – und nicht proportional
zu ∆t, wie es bei deterministischen Prozessen der Fall ist.
Das entsprechende Programm finden Sie als rw3.cpp auf der CD:
1
2
3
4
5
/**************************************************************************
* Name:
rw3.cpp
*
* Zweck:
Simuliert den Random-Walk - mit Skalierung
*
* verwendete Bibliothekl: GSL
*
**************************************************************************/
6
7
8
9
10
11
12
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_rng.h>
<gsl/gsl_randist.h>
<math.h>
"tools.h"
13
14
using namespace std;
15
16
int main( int argc, char *argv[] )
17
18
19
20
{
const int N_max = 1000000;
const int n_bins = 100;
21
22
23
24
25
26
27
28
29
//-- Definition der Variablen
int
histo[2*n_bins+1], N, n, n_step, n_step_max;
double x[N_max][2], xmean, xvar, vmean, vvar, D, t_max, t, dt;
double sum1, sum2, sum3, sum4, dx, dv, dv_0, d_bins;
ifstream in_stream;
ofstream out_stream1, out_stream2;
const gsl_rng_type * T;
gsl_rng * r;
30
31
32
33
34
35
36
37
38
//-- Fehlermeldung,
if (argc<4)
{
cout << " Aufruf:
cout << "
cout << "
exit(1);
}
wenn Input- und Outputfilename nicht uebergeben
rw3 infile1 outfile1\n";
outfile1: Zeitentwickl. von Mittelwert und Varianz\n";
outfile2: Histogramm der Schlussverteilung\n";
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
wurden
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream1.open(argv[2]);
out_stream2.open(argv[3]);
in_stream >> D;
in_stream >> t_max;
in_stream >> dt;
in_stream.close();
out_stream1 << "! Ergebnis-Datei generiert von rw3.cpp\n";
out_stream1 << "! D = " << D << "\n";
out_stream1 << "! t_max = " << t_max << "\n";
out_stream1 << "! dt = " << dt << "\n";
out_stream2 << "! Ergebnis-Datei generiert von rw3.cpp\n";
out_stream2 << "! D = " << D << "\n";
out_stream2 << "! t_max = " << t_max << "\n";
164
54
55
56
4 Statistische Physik
out_stream2 << "! dt = " << dt << "\n";
n_step_max = t_max / dt;
dv_0 = D * sqrt(dt);
57
58
59
60
61
//-- Initialisierung des Zufallszahlengenerators
gsl_rng_env_setup();
T = gsl_rng_default;
r = gsl_rng_alloc (T);
62
63
64
65
//-- Anfangsbedingungen
t = 0;
for (n=0; n<N_max; n++) {x[n][0] = 0; x[n][1] = 0;}
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//-- Zeitschleife -for (n_step=1; n_step<=n_step_max; n_step++)
{
t = t+dt;
sum1 = 0; sum2 = 0;
sum3 = 0; sum4 = 0;
for (N=0; N<N_max; N++)
{
dx = x[N][1] * dt;
dv = gsl_ran_gaussian(r,dv_0);
x[N][0] = x[N][0] + dx;
x[N][1] = x[N][1] + dv;
sum1 = sum1 + x[N][0];
sum2 = sum2 + sqr(x[N][0]);
sum3 = sum3 + x[N][1];
sum4 = sum4 + sqr(x[N][1]);
}
xmean = sum1 / N_max;
xvar = sum2 / N_max - sqr(xmean);
vmean = sum3 / N_max;
vvar = sum4 / N_max - sqr(vmean);
out_stream1 << t << " " << xmean << " " << xvar
<< " " << vmean << " " << vvar << "\n";
}
87
88
89
90
91
92
93
94
95
96
97
98
for (n=0; n<=2*n_bins+1; n++) histo[n] = 0;
d_bins = sqrt(xvar) * 5 / n_bins;
for (N=0; N<N_max; N++)
{
n = int( x[N][0] / d_bins + n_bins + 0.5);
if ((n>=0) && (n<=2*n_bins+1)) histo[n]++;
}
for (n=0; n<=2*n_bins+1; n++)
out_stream2 << (n-n_bins) * d_bins << " " << histo[n] / N_max / d_bins
<< " " << exp( -(sqr(n-n_bins)*d_bins) / 2 / xvar )
/ sqrt( 2 * pi * xvar );
99
100
101
102
out_stream1.close();
out_stream2.close();
}
Die Ergebnisse dieses Programms sind in Abb. 4.5 dargestellt:
Unter Umständen überraschen Sie zwei Details in Abb. 4.5:
– die lineare Drift im Mittelwert x des Ortes (Abb. 4.5(a)),
– das quadratische Ansteigen der Varianz x2 mit der Zeit t (Abb. 4.5(b)).
Um die lineare Drift des Ortsmittelwertes zu verstehen, betrachten Sie bitte
die Zeitentwicklung von v in Abb. 4.5(c): Durch die endlichen Größe des
4.6 Der Random-Walk
0.0
x
(a)
x
165
2
(b)
2000
-0.02
1000
-0.04
0
0
5
10
0
5
t
(c)
0.0
v
10
t
2
v
(d)
75
50
-0.005
25
-0.01
0
0
5
10
0
t
5
10
t
Abb. 4.5. Mittelwert und Varianz von Ort und Geschwindigkeit eines Teilchens,
das eine Geschwindigkeitsdiffusion erfährt.
betrachteten Ensembles ist dieser Mittelwert nicht exakt null, sondern fluktuiert, wobei er in unserem Beispiel überwiegend negative Werte annimmt.
Da sich der Ort durch Integration
t
x(t) =
dt v(t ) (4.44)
0
ergibt, driftet der Ortsmittelwert langsam in den negativen Bereich. Sie sollten anhand der Beispielprogramme überprüfen, dass bei einer anderen Initialisierung des Zufallszahlengenerators diese Drift auch in Richtung positiver
Werte erfolgen kann und dass deren Steigung mit größer werdendem Ensemble abnimmt.
Um die quadratische Zunahme von x2 zu verstehen, muss man sich klar
machen, dass Ort und Geschwindigkeit des Teilchens nicht statistisch unabhängig sind, sondern sich auf der positiven x−Achse überwiegend Teilchen
mit positiver Geschwindigkeit befinden, während auf der negativen x−Achse
überwiegend Teilchen mit negativer Geschwindigkeit sind. Diese Teilchen behalten im Mittel ihre Geschwindigkeit bei, wandern also nach rechts bzw.
links und führen zu einem quadratischen Anwachsen der Varianz des Ortes.
Um diese Argumentation zu untermauern, fügen wir einen Programmabschnitt ein, der eben diese Korrelation vx − v x zwischen Ort und Geschwindigkeit berechnet. Dazu führen wir zusätzlich zu den Variablen sum1
bis sum4 eine Variable sum5 ein, die entsprechend
sum5 = sum5 + x(n,1) * x(n,2)
166
4 Statistische Physik
aufsummiert wird. Daraus erhalten wir die gesuchte Korrelation corr
corr = sum5 / dfloat(n_max) - xmean*vmean
Betrachten wir diese Korrelation in Abhängigkeit von der Zeit, so erhalten
wir Abb. 4.6, der wir entnehmen, dass v und x tatsächlich stark korreliert
sind. Die Tatsache, dass die Korrelation positiv ist, bedeutet, dass Teilchen
vx
40
20
0
0
5
10
t
Abb. 4.6. Korrelation zwischen Ort und Geschwindigkeit eines freien Teilchens,
das einer Geschwindigkeitsdiffusion unterliegt.
mit positiver Geschwindigkeit am wahrscheinlichsten rechts vom Ursprung
und Teilchen mit negativer Geschwindigkeit am wahrscheinlichsten links vom
Ursprung anzutreffen sind.
Ein Problem stellt die Tatsache dar, dass die Varianz der Geschwindigkeit ohne Beschränkung linear anwächst, denn diese Größe ist direkt mit der
mittleren kinetischen Energie verknüpft:
1
1
Ekin = mv 2 = mv 2 2
2
(4.45)
Dass die mittlere Energie immer weiter anwächst, ist natürlich nicht akzeptabel und stellt einen Fehler unseres Modells dar, den wir erst korrigieren
müssen, bevor wir den gewonnenen Ergebnissen vertrauen können. Zu diesem Zweck erinnern wir uns an die Einsteinsche Interpretation der Brownschen Bewegung: die relativ großen, sichtbaren Teilchen erhalten Stöße durch
andere, kleine und deswegen nicht wahrnehmbare Teilchen. Nach heutigem
Sprachgebrauch bilden diese kleinen Teilchen ein Reservoir, das als so groß
angenommen wird, dass es durch die sichtbaren Teilchen nicht beeinflusst
wird. Wir müssen nun einige Eigenschaften dieses Reservoirs kennen, ohne
dass wir uns jedoch für die Dynamik der einzelnen Teilchens des Reservoirs zu
interessieren brauchen. Grundsätzlich geht man davon aus, dass das Reservoir
im thermischen Gleichgewicht ist, was bedeutet, dass die Geschwindigkeitsverteilung in ihm Gauß-förmig ist. Wenn wir darüber hinaus annehmen, dass
sich das Reservoir in Ruhe befindet und nur aus einer Teilchensorte besteht,
wird es durch nur zwei Parameter beschrieben: seiner Temperatur und der
Masse seiner Teilchen.
4.6 Der Random-Walk
167
Betrachten wir nun etwas genauer die Stöße zwischen den sichtbaren Teilchen, die uns interessieren, und den nicht mehr wahrnehmbaren Teilchen. Wie
stark dieser Stoß ist und welche Richtung der Impulsübertrag hat, hängt sowohl von der relativen Geschwindigkeit als auch vom sogenannten Stoßparameter ab. Dieser Stoßparameter ist der Abstand, in dem der Schwerpunkt des
einen Teilchens an dem des anderen vorbeifliegen würde, wenn es keine Wechselwirkung zwischen den beiden Teilchen gäbe (es also zu gar keinem Stoß
kommen würde). In einer Dimension gibt es diesen Stoßparameter natürlich
nicht, dort ist per definitionem jeder Stoß zentral.
Um diese Stöße korrekt abzubilden, müssten wir streng genommen für
jede Relativgeschwindigkeit eine Wahrscheinlichkeitsverteilung angeben, die
angibt, mit welcher Wahrscheinlichkeit ein Stoß mit einem bestimmten Impulsübertrag stattfindet – bzgl. dieser Vorgehensweise verweisen wir auf Abschn. 4.8. Wir vereinfachen das Modell jedoch dahingehend, dass die Wahrscheinlichkeit für einen Stoß unabhängig von der Relativgeschwindigkeit ist
und der Impulsübertrag ein bestimmter Prozentsatz der Relativgeschwindigkeit ist. Die erste Annahme ist sicherlich nie erfüllt und auch die zweite
Annahme ist nur in eindimensionalen Situationen erfüllt, in denen jeder Stoß
zentral ist. Trotzdem wollen wir dieses stark vereinfachte Modell analysieren und werden feststellen, dass es eine erstaunlich gute Beschreibung der
Realität liefert.
Zunächst jedoch müssen wir uns überlegen, wie wir den obigen Annahmen
in einem Algorithmus gerecht werden können:
Als erstes bestimmt ein Zufallszahlengenerator die Geschwindigkeit v1 des
stoßenden Teilchens. Da sich das Reservoir im thermischen Gleichgewicht
befindet und keine zusätzliche Driftgeschwindigkeit aufweist, müssen diese
Zufallszahlen Gauß-förmig um den Ursprung verteilt sein.
– Durch Subtraktion der Geschwindigkeit v2 des gestoßenen Teilchens erhalten wir daraus die Relativgeschwindigkeit:
–
vrel = v2 − v1 .
–
(4.46)
Da der Impulsübertrag proportional zu vrel sein soll, müssen wir jetzt
nur noch mit einem festen Faktor multiplizieren, der zum einen von der
Häufigkeit von Stößen und zum anderen vom Massenverhältnis des (sichtbaren) gestoßenen und der (unsichtbaren) stoßenden Teilchen abhängt.
Wir erkennen die Implementation dieser Vorgehensweise beim Programm
rw4.cpp in Zeile 70:
1
2
3
4
5
/**************************************************************************
* Name:
rw4.cpp
*
* Zweck:
Simuliert den Random-Walk - mit Skalierung
*
* verwendete Bibliothekl: GSL
*
**************************************************************************/
6
7
#include <iostream>
168
8
9
10
11
12
4 Statistische Physik
#include
#include
#include
#include
#include
<fstream>
<gsl/gsl_rng.h>
<gsl/gsl_randist.h>
<math.h>
"tools.h"
13
14
using namespace std;
15
16
int main( int argc, char *argv[] )
17
18
19
20
{
const int N_max = 1000000;
const int n_bins = 100;
21
22
23
24
25
26
27
28
29
//-- Definition der Variablen
int
histo[2*n_bins+1], N, n, n_step, n_step_max;
double x[N_max][2], xmean, xvar, vmean, vvar, sum1, sum2, sum3, sum4;
double t_max, t, dt, prob, v_therm, kick_strength, d_bins, dx, dv;
ifstream in_stream;
ofstream out_stream, out_stream2;
const gsl_rng_type * T;
gsl_rng * r;
30
31
32
33
34
35
36
37
38
39
//-- Fehlermeldung,
if (argc<4)
{
cout << " Aufruf:
cout << "
cout <<
cout << "
exit(1);
}
wenn Input- und Outputfilename nicht uebergeben wurden
rw4 infile1 outfile1\n";
outfile1: Zeitentwicklung von Mittelwert";
" und Varianz\n";
outfile2: Histogramm der Schlussverteilung\n";
40
41
42
43
44
45
46
47
48
49
50
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream2.open(argv[2]);
inout(v_therm,"v_therm");
inout(kick_strength,"kick_strength");
inout(prob,"prob");
inout(t_max,"t_max");
inout(dt,"dt");
in_stream.close();
n_step_max = t_max / dt;
prob = prob * dt;
51
52
53
54
55
//-- Initialisierung des Zufallszahlengenerators
gsl_rng_env_setup();
T = gsl_rng_default;
r = gsl_rng_alloc (T);
56
57
58
59
//-- Anfangsbedingungen
t = 0;
for (N=0; N<N_max; N++) {x[N][0] = 0; x[N][1] = 0;}
60
61
62
63
64
65
66
//-- Zeitschleife -for (n_step=1; n_step<=n_step_max; n_step++)
{
t = t+dt;
sum1 = 0; sum2 = 0;
sum3 = 0; sum4 = 0;
4.6 Der Random-Walk
67
68
69
70
71
72
73
74
75
76
77
78
79
80
169
for (n=0; n<N_max; n++)
{
dx = x[N][1] * dt;
dv = (gsl_ran_gaussian(r,v_therm) - x[N][1]) * kick_strength;
x[N][0] = x[N][0] + dx;
x[N][1] = x[N][1] + dv;
sum1 = sum1 + x[n][0];
sum2 = sum2 + sqr(x[N][0]);
sum3 = sum3 + x[N][1];
sum4 = sum4 + sqr(x[N][1]);
}
xmean = sum1 / N_max;
xvar = sum2 / N_max - sqr(xmean);
vmean = sum3 / N_max;
vvar = sum4 / N_max - sqr(vmean);
out_stream << t << " " << xmean << " " << xvar
<< " " << vmean << " " << vvar << "\n";
}
81
82
83
84
85
86
87
88
89
90
91
for (n=0; n<=2*n_bins+1; n++) histo[n] = 0;
d_bins = sqrt(xvar) * 5 / n_bins;
for (N=0; N<N_max; N++)
{
n = int( x[N][0] / d_bins + n_bins + 0.5);
if ((n>=0) && (n<=2*n_bins+1)) histo[n]++;
}
out_stream.close();
out_stream2.close();
}
Betrachten wir nun in Abb. 4.7 wieder die Zeitentwicklung von Ort und
Impuls (jeweils Mittelwert und Varianz) so sehen wir, dass nach einer Übergangszeit, in der die Varianz der Geschwindigkeit linear anwächst, diese dann
100
(a)
0.002
x
x
(b)
2
0.0
50
-0.002
0
0
5
10
0
5
t
v
t
(c)
0.002
10
2
3
(d)
v
0.0
2
-0.002
1
-0.004
0
0
5
10
t
0
5
10
t
Abb. 4.7. Zeitentwicklung von Mittelwert und Varianz des Aufenthaltsortes und
der Geschwindigkeit eines Teilchens mit einer – einigermaßen – realistischen Modellierung der Brownschen Bewegung.
170
4 Statistische Physik
einen stabilen Wert annimmt (Abb. 4.7(d)). Außerdem nimmt danach die Varianz des Ortes (Abb. 4.7(b)) nur noch linear und nicht mehr quadratisch zu.
Das ist das Verhalten, wie es ein Brownsches Teilchen in der Realität wirklich
zeigt!
4.7 Thermisches Hüpfen
Im vorangegangenen Abschnitt haben wir den Grundstein für nichttriviale
Probleme aus der statistischen Physik gelegt, auf dem wir nun aufbauen wollen. Während die Beispiele des letzten Abschnittes jedoch noch analytisch
lösbar waren (also eigentlich gar keiner numerischen Behandlung bedurften),
wenden wir uns nun Problemen zu, die ohne Hilfe des Computers nicht mehr
gelöst werden können. Dabei werden wir feststellen, dass wir unsere Grundprogramme nur geringfügig verändern müssen!
Das Problem, dem wir uns nun zuwenden wollen, ist thermisches Hüpfen.
Stellen wir uns dazu ein Teilchen vor, das sich unter dem Einfluss einer Kraft
bewegt, die wir als Gradient eines Potentials beschreiben können. Dieses Potential soll mehrere Minima (also stabile Gleichgewichtszustände) besitzen.
Wenn das Teilchen nun in einem dieser Minima ruht, wird es dort für immer verharren. Wenn wir uns jedoch vorstellen, dass es durch andere Teilchen
gestoßen wird, kann es durch diese Stöße dieses Minimum verlassen und eventuell in einen anderen stabilen Zustand gelangen.
Die Modifikation gegenüber rw3.cpp besteht lediglich darin, dass wir zu
der statistischen Bewegung eine deterministische hinzufügen – wir ersetzen
also nur die Zeilen, in denen dv berechnet wird, durch:
dv = (gsl_ran_gaussian(r,v_therm) - x[N][1]) * kick_strength
+ dv_det(x[N][0]);
Der deterministische Anteil hängt nur vom Ort des Teilchens ab, da wir
die Bewegung in einem zeitunabhängigen Potential zugrunde legen wollen –
die Verallgemeinerung auf zeitabhängige Potentiale oder auf Kräfte, die nicht
durch ein Potential beschrieben werden können, stellt jedoch kein größeres
Problem dar.
Nun müssen wir noch das Potential festlegen, in dem sich das Teilchen
bewegen soll. Grundsätzlich sind alle Potentiale geeignet, die mehrere Minima
haben – wir wählen hier ein Polynom 4. Ordnung mit zwei unterschiedlich
tiefen Minima (siehe Abb. 4.8):
V (x) =
4
n=0
mit
an xn .
(4.47)
4.7 Thermisches Hüpfen
171
Abb. 4.8. Potentialverlauf für ein Brownsches Teilchen.
a2 = −0.5
(4.48)
a3 = −0.01
a4 = 0.005 .
(4.49)
(4.50)
Die beiden verbleibenden Koeffizienten a0 und a1 sind null.
Als letztes müssen wir noch einige Gedanken darauf verwenden, was wir
als Anfangsverteilung P (x, t = 0) wählen wollen und welche Variablen wir
ausgeben wollen, um das Hüpfen zu verfolgen. Als Ausgangssituation wählen
wir sinnvollerweise, dass das Teilchen in einem der beiden Minima ruht. Um
zu erreichen, dass das Teilchen mit einer möglichst hohen Wahrscheinlichkeit seine Ausgangsposition verlässt und in die andere Potentialmulde hüpft,
wählen wir als Anfangsort das flachere der beiden Potentialminima, also das
linke.
Unser Programm soll dann zum einen für jeden Zeitpunkt die Wahrscheinlichkeit ausgeben, das Teilchen links bzw. rechts zu finden. Zum anderen
möchten wir wenigstens am Ende der Zeitentwicklung auch die komplette Wahrscheinlichkeitsverteilung P (x, tend ) in einer getrennten Ausgabedatei
abspeichern. Damit steht unser erstes Programm zum thermischen Hüpfen:
1
2
3
4
5
/**************************************************************************
* Name:
th1.cpp
*
* Zweck:
Simuliert thermisches Huepfen zwischen zwei Potentialminima *
* verwendete Bibliothekl: GSL
*
**************************************************************************/
6
7
8
9
10
11
12
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_rng.h>
<gsl/gsl_randist.h>
<math.h>
"tools.h"
13
14
using namespace std;
15
16
17
18
//-- globale Variablen
double dt;
double a1, a2, a3, a4;
19
20
//-------------------------------------------------------------------------
172
4 Statistische Physik
21
22
double dv_det(double x)
23
24
25
26
27
{
double a = -2*a2*x - 3*a3*sqr(x) - 4*a4*pow(x,3);
return a * dt;
}
28
29
//-------------------------------------------------------------------------
30
31
int main( int argc, char *argv[] )
32
33
34
35
{
const int N_max = 1000000;
const int n_bins = 100;
36
37
//-- Definition der Variablen
38
39
40
41
42
43
44
45
int
histo[2*n_bins+1], n, N, n_step, n_step_max;
double x[N_max][2], xmean, xvar, sum1, sum2, sum3, sum4, dx, dv;
double v_therm, kick_strength, prob_links, prob_rechts, t_max, t, d_bins;
ifstream in_stream;
ofstream out_stream;
const gsl_rng_type * T;
gsl_rng * r;
46
47
48
49
50
51
52
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<4)
{
cout << " Aufruf: th1 infile outfile1 outfile2\n";
exit(1);
}
53
54
55
56
57
58
59
60
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von th1.cpp\n";
inout(v_therm,"v_therm");
inout(kick_strength,"kick_strength");
inout(t_max,"t_max");
inout(dt,"dt");
in_stream.close();
61
62
63
64
65
//-- Berechnung einiger benoetigter Parameter
n_step_max = t_max / dt;
v_therm = v_therm / sqrt(dt);
kick_strength = kick_strength * dt;
66
67
68
69
70
71
//-a1 =
a2 =
a3 =
a4 =
Festlegung des Potentials
0;
-0.5;
-0.01;
0.005;
72
73
74
75
76
//-- Initialisierung des Zufallszahlengenerators
gsl_rng_env_setup();
T = gsl_rng_default;
r = gsl_rng_alloc (T);
77
78
79
//-- Anfangsbedingungen
t = 0;
4.7 Thermisches Hüpfen
80
81
82
83
84
for (N=0; N<N_max; N++)
{
x[N][0] = (-3*a3-sqrt(9*a3*a3-32*a2*a4))/(8*a4);
x[N][1] = 0;
}
173
// Potentialmin.
// in Ruhe
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//-- Zeitschleife -for (n_step=1; n_step<=n_step_max; n_step++)
{
t = t+dt;
sum1 = 0; sum2 = 0;
sum3 = 0; sum4 = 0;
prob_links = 0; prob_rechts = 0;
for (N=0; N<N_max; N++)
{
dx = x[N][1] * dt;
dv = (gsl_ran_gaussian(r,v_therm) - x[N][1]) * kick_strength
+ dv_det(x[N][0]);
x[N][0] = x[N][0] + dx;
x[N][1] = x[N][1] + dv;
sum1 = sum1 + x[N][0];
sum2 = sum2 + sqr(x[N][0]);
if (x[N][0]<0) prob_links++;
else prob_rechts++;
}
prob_links = prob_links / float(N_max);
prob_rechts = prob_rechts / float(N_max);
xmean = sum1 / float(N_max);
xvar = sum2 / float(N_max) - sqr(xmean);
out_stream << t << " " << prob_links << " " << prob_rechts << "\n";
}
111
112
out_stream.close();
113
114
115
116
117
118
119
120
121
122
123
124
125
out_stream.open(argv[3]);
for (n=0; n<2*n_bins+1; n++) histo[n] = 0;
d_bins = sqrt(xvar) * 5. / float(n_bins);
cout << xvar << " " << d_bins << " " << N_max << "\n";
for (N=0; N<N_max; N++)
{
n = int( x[N][0] / d_bins + n_bins + 0.5);
if ((n>=0) && (n<2*n_bins+1)) histo[n]++;
}
for (n=0; n<2*n_bins+1; n++)
out_stream << (n-n_bins) * d_bins << " "
<< histo[n] / float(N_max) / d_bins << "\n";
126
127
128
out_stream.close();
}
Das Programm th1.cpp liefert uns zum einen in der ersten Ausgabedatei den Zeitverlauf der Aufenthaltswahrscheinlichkeit links bzw. rechts (Zeile
109) und zum anderen in der zweiten Ausgabedatei die Wahrscheinlichkeitsdichte P (x) (Zeile 114–127). Wenn wir uns nun zunächst die beiden Aufenthaltswahrscheinlichkeiten rechts und links als Funktion der Zeit anschauen
(Abb. 4.9), stellen wir fest, dass wie erwartet die Wahrscheinlichkeit links
immer mehr abnimmt, während sie rechts entsprechend zunimmt. Allerdings
174
4 Statistische Physik
sind im stationären Zustand, der sich für t → ∞ einstellt, nicht alle Teilchen im tieferen Minimum, vielmehr befinden sich ca. 26% weiterhin auf der
rechten Seite. Der Grund hierfür ist der geringe Unterschied in der Tiefe der
beiden Potentialminima, was dazu führt, dass – bei effektiv endlicher Temperatur des Reservoirs – Teilchen auch vom tieferen Minimum in das höhere
zurückhüpfen können.
Abb. 4.9. Aufenthaltswahrscheinlichkeit im linken (durchgezogene Linie) bzw.
rechten Potentialminimum (gestrichelte Linie) als Funktion der Zeit
Für t → ∞ stellt sich das thermische Gleichgewicht ein, bei dem die
beiden Potentialmulden so besetzt sind, dass sich das thermische Hüpfen
von links nach rechts sowie in der umgekehrten Richtung die Waage halten.
Bei t = 300 ist dies bereits sehr gut erfüllt – Abb. 4.10 zeigt uns diese
Gleichgewichtsverteilung, wie sie in der zweiten Ausgabedatei von th1.cpp
abgespeichert wurde:
Abb. 4.10. Aufenthaltswahrscheinlichkeit P (x), die sich bei t = 300 eingestellt hat
Interessant ist es, das Potential so zu variieren, dass der Potentialunterschied zwischen den beiden Mulden kontrolliert werden kann. Dies ist Gegenstand der Übungsaufgabe 4.2. Hier jedoch wollen wir das Potential in einer
anderen Weise variieren, um uns einer anderen Fragestellung zuzuwenden.
Uns interessiert an dieser Stelle die Zeitskala, die das System braucht, um
den Gleichgewichtszustand zu erreichen. Dazu betrachten wir Potentiale der
Form
4.7 Thermisches Hüpfen
175
1
V (x) = −ax20 x2 + ax4 .
(4.51)
2
Unabhängig von a hat dieses Potential zwei gleich tiefe Minima bei x =
±x0 , an denen das Potential V den Wert −ax40 /2 annimmt. Der Parameter a
erlaubt es uns, die Höhe der Potentialbarriere zwischen diesen beiden Minima
zu variieren, denn bei null hat V unabhängig von a den Wert null.
Wenn wir für die Wahscheinlichkeit P1 , das Teilchen links anzutreffen, ein
exponentielles Zeitverhalten der Form
t
1
1
exp −
(4.52)
P1 (t) = + P1 (0) −
2
τ
2
ansetzen und die Anfangssituation so wählen, dass P1 (0) = 1 ist, erhalten
wir die für das Hüpfen charakterische Zeit τ aus der Bedingung, dass
1
1
1+
(4.53)
P1 (τ ) =
2
e
und entsprechend
P2 (τ ) =
1
2
1−
1
e
(4.54)
ist. Die Modifikationen, die wir – ausgehend vom Programm th1.cpp – machen müssen, sind also
– Einschränkung der betrachteten Potentiale auf Potentiale vom Typ (4.51).
– Die stochastische Differentialgleichung wird nicht mehr bis zu einer vorgegebenen Endzeit tend integriert, sondern bis zum ersten Mal P2 den
durch (4.54) festgelegten Wert überschreitet.
Sie finden das entsprechend modifizierte Programm als th2.cpp auf der CDROM zum Buch. Das Ergebnis (Abb. 4.11) zeigt das erwartete starke Ansteigen der Hüpfzeit τ mit der Höhe der Potentialbarriere, also mit a.
Abb. 4.11. Charakteristische Hüpfzeit τ als Funktion des Parameters a, der die
Höhe der Potentialbarriere charakterisiert
Bei genauem Hinsehen erkennen Sie in Abb. 4.11, dass die Thermalisierung für a → 0 nicht verschwindet, was die Tatsache widerspiegelt, dass auch
176
4 Statistische Physik
ohne Barriere das Brownsche Teilchen Zeit braucht, um von seinem Startpunkt in die rechte Hälfte des Ortsbereichs zu gelangen. Erstaunlicher ist
die Tatsache, dass τ mit wachsendem a zunächst abnimmt und erst ab etwa
a = 0.1 ansteigt.
4.8 Thermalisierung in Gasen
In diesem Abschnitt werden wir uns genauer mit dem Übergang ins thermische Gleichgewicht beschäftigen und ein mikroskopisches Modell auf der Basis einer Master-Gleichung dafür aufstellen und behandeln. Dabei werden wir
uns auf einatomige Gase beschränken. Bei ihnen gibt es nur translatorische
Freiheitsgrade und die kinetische Energie ist eine Funktion der Geschwindigkeit:
1
(4.55)
Ekin = mv 2 .
2
Da wir uns außerdem auf ein freies, homogenes Gas beschränken, reduziert
sich unsere Aufgabe darauf, die Geschwindigkeitsverteilung P (v) zu einem
bestimmten Zeitpunkt zu kennen. Der einzige Prozess, der zu Änderungen in
dieser Geschwindigkeitsverteilung – und damit zur Thermalisierung – führt,
sind Stöße der Teilchen untereinander. Da wir rotatorische Freiheitsgrade
ausgeschlossen haben und darüber hinaus annehmen wollen, dass die inneren Freiheitsgrade von Atomen zu hohe Energien erfordern, um angeregt
zu werden, sind diese Stöße elastisch. Bezeichnen wir mit p(v1 , v2 , ∆v) die
Häufigkeit für Stöße zwischen zwei Teilchen mit den Geschwindigkeiten v 1
und v 2 , bei denen der Geschwindigkeitsübertrag vom zweiten auf das erste
Teilchen ∆v ist, so erhalten wir für P (v) die folgende Master-Gleichung:
d
P (v) = dv 1 dv 2 p(v 1 , v2 , v − v 1 )
dt
− dv 2 d∆v p(v, v 2 , ∆v) .
(4.56)
Diese spezielle Master-Gleichung für die Geschwindigkeitsverteilung in einem Gas wird Boltzmann-Gleichung genannt [27]. Der erste Term beschreibt
Stöße, bei denen eines der beiden Teilchen nach dem Stoß die Geschwindigkeit
v hat; der zweite Term enthält die Stöße, bei denen ein Teilchen vor dem Stoß
die Geschwindigkeit v hatte. Die Häufigkeit von Stößen ist proportional zur
Wahrscheinlichkeit, Teilchen mit den erforderlichen Geschwindigkeiten anzutreffen. Der Proportionalitätsfaktor – der differentielle Wirkungsquerschnitt
σ – kann aus Symmetriegründen nur von der Relativgeschwindigkeit v 1 − v 2
und dem Geschwindigkeitsübertrag ∆v abhängen:
p(v 1 , v 2 , ∆v) = P (v 1 )P (v 2 ) σ(v 1 − v 2 , ∆v) .
Wenn wir dies in (4.56) einsetzen, erhalten wir:
(4.57)
4.8 Thermalisierung in Gasen
d
P (v) =
dt
177
dv 1 dv 2 P (v 1 )P (v 2 )σ(v 1 − v 2 , v − v 1 )
− dv 2 d∆v P (v)P (v 2 )σ(v − v 2 , ∆v)
= dv 1 dv 2 d∆vP (v 1 )P (v 2 )σ(v 1 − v 2 , ∆v)
× [δ(v − v 1 − ∆v) − δ(v 1 − v)] .
(4.58)
(4.59)
Dieses Doppelintegral (4.59) können wir durch Übergang zu den Fouriertransformierten Funktionen
P̃ (u) = dv P (v) exp(iuv)
(4.60)
σ̃(u12 , ∆u) = dv 12 d∆v σ(v 12 , ∆v) exp(iu12 v 12 + i∆u∆v) .(4.61)
auf ein Einfachintegral reduzieren:
d
1
du1 P̃ (u1 )P̃ (u − u1 )
P̃ (u) =
dt
(2π)3
× (σ̃(u − u1 , u) − σ̃(u − u1 , 0)) .
(4.62)
Zur besseren Übersichtlichkeit verzichten wir im weiteren auf die Tilde zur
Kennzeichnung, dass die Fouriertransformierten Funktionen gemeint sind.
4.8.1 Energieerhaltung
Bevor wir uns an die numerische Implementierung der Bewegungsgleichung
(4.62) machen können, müssen wir noch den Wirkungsquerschnitt σ festlegen. Hierzu müssen wir uns jedoch zunächst überlegen, ob dieser differentielle Wirkungsquerschnitt irgendwelchen Einschränkungen durch einzuhaltende
Erhaltungssätze unterliegt. Die Geschwindigkeiten vor dem Stoß sind v1 bzw
v2 vor dem Stoß und v1 + ∆v bzw. v2 − ∆v nach dem Stoß. Wir sehen, dass
die Impulserhaltung automatisch erfüllt ist, die Energieerhaltung müssen wir
jedoch durch eine zusätzliche Bedingung sicherstellen:
v12 + v22 = (v1 + ∆v)2 + (v2 − ∆v)2
0 = 2(v1 − v2 )∆v + 2∆v 2
2 2
v2 − v1
v2 − v1
=
.
∆v −
2
2
(4.63)
∆v liegt also auf einem Kreis um (v2 − v1 )/2, der durch den Ursprung und
durch v2 −v1 geht – ersteres besagt, dass kein Impulsübertrag keinen Konflikt
mit der Energieerhaltung darstellt, und letzteres besagt, dass auch Stöße
178
4 Statistische Physik
erlaubt sind, bei denen nachher Teilchen 1 die Geschwindigkeit hat, die vorher
Teilchen 2 hatte, und umgekehrt.
Wenn wir uns der Anschaulichkeit halber auf zwei Dimensionen beschränken, so können wir das Fourierintegral von (4.61) auf diesen Kreis
beschränken (bei drei Dimensionen würde Bedingung 4.63 zu einer Integration über eine Kugeloberfläche führen). Dazu führen wir die folgenden Winkel
ein, wobei wir die x-Achse in Richtung des Vektors u12 legen:
Abb. 4.12. Schematische Darstellung zur Berechnung des Wirkungsquerschnittes
im Fourierraum
Damit ergibt sich
u12 v 12 = u12 v12 cos φ12
(4.64)
1
(4.65)
∆u∆v = ∆u (v 12 + v12 eφ )
2
1
= ∆uv12 (cos(φ12 − φ∆u ) + cos(φ − φ∆u )) .
(4.66)
2
Für das Integral (4.61) erhalten wir:
σ (u12 , ∆u) = dv12 dφ dφ12 σ(v 12 , ∆v(φ12 − φ∆u ), φ12 − φ∆u )
∆uv12
× exp i u12 v12 cos φ12
(cos(φ12 − φ∆u ) + cos(φ − φ∆u ))
2
dφ dθ σ(v12 , φ )
= dv12
(4.67)
∆uv12
× exp i u12 v12 cos(θ + φ∆u ) +
(cos θ + cos(φ + θ))
,
2
wobei wir die Winkel
und
φ = φ − φ12
θ = φ12 − φ∆u
(4.68)
(4.69)
4.8 Thermalisierung in Gasen
179
eingeführt haben.
Zunächst überzeugen wir uns davon, dass der Imaginärteil von σ(u12 , ∆u)
null ist:
Im(σ(u12 , ∆u)) = dv12 dφ dθ σ(v12 , φ ) sin ξ ,
(4.70)
mit
ξ = u12 v12 cos(θ + φ∆u ) +
∆uv12
(cos θ + cos(φ + θ)) .
2
(4.71)
Wir müssen lediglich die Integration über θ betrachten. ξ lässt sich mittels
der Additionstheoreme in die Form
ξ = A cos(θ + B)
(4.72)
bringen, wobei uns die Werte von A und B nicht zu interessieren brauchen.
Da die Integration über θ ohnehin über alle Werte von −π bis π geht, können
wir die Verschiebung B eliminieren und erhalten einen Integranden, der antisymmetrisch in θ ist. Aufgrund des symmetrischen Integrationsbereiches
[−π, π] muss das Integral also null ergeben.
Schwieriger ist der Realteil von σ(u12 , ∆u), dessen Wert wir numerisch
berechnen müssen. Dazu müssen wir zunächst den Wirkungsquerschnitt im
Geschwindigkeitsraum, dessen genaue Form wir bisher offen gelassen haben,
festlegen, z.B.:
2
cos2 (∆φ)
(4.73)
σ = σ0 exp −αv12
Wenn wir versuchen, das Dreifach-Integral (4.67) direkt numerisch für viele Werte von u12 und ∆u zu berechnen, stellen wir schnell fest, dass die
benötigten Rechenzeiten enorm sind. Glücklicherweise können wir für die
Abhängigkeit (4.73) des Wirkungsquerschnitts von v12 die Integration über
v12 analytisch ausführen, so dass nur ein Doppelintegral verbleibt:
2
a
σ0 π
dφ
σ(u12 , ∆u) =
dθ exp
,
(4.74)
2 α
4α
wobei wir die Abkürzung
1
a = u12 cos(θ + φ∆u ) + ∆u (cos θ + cos(φ + θ))
2
(4.75)
eingeführt haben.
Das Programm sigma.cpp berechnet dieses Doppelintegral für
n3 ∆u
n1 ∆u
∆u =
ni = −nmax , . . . nmax . (4.76)
u12 =
n2 ∆u
n4 ∆u
180
1
2
3
4
5
6
4 Statistische Physik
/**************************************************************************
* Name:
sigma.cpp
*
* Zweck:
Berechnet den Wirkungsquerschnitt im Fourierraum
*
* Gleichung: siehe Buch
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
14
15
#include
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_vector.h>
<gsl/gsl_integration.h>
<gsl/gsl_sort_vector.h>
<gsl/gsl_sort.h>
<math.h>
"tools.h"
16
17
using namespace std;
18
19
20
21
//--globale Variablen
double u_12, delta_u, phi_delta, alpha;
int
count_outer;
22
23
24
25
26
27
//--Parameter fuer die innere bzw. mittlere Integration
struct param_type
{
double theta;
};
28
29
//------------------------------------------------------------------------
30
31
double f_inner(double phi_s, void *params)
32
33
34
35
{
double a;
double theta
= ((struct param_type *) params) -> theta;
36
37
38
39
40
a = u_12 * cos(theta+phi_delta)
+ 0.5 * delta_u * (cos(theta)+cos(phi_s+theta));
return 0.5 * sqrt(pi/alpha) * exp(-0.25*sqr(a)/alpha);
}
41
42
//------------------------------------------------------------------------
43
44
double f_outer(double theta, void *params)
45
46
47
48
49
50
{
int
limit = 1000;
double epsabs = 1.e-8, epsrel = 1.e-8;
double result, abserr;
param_type params_inner;
51
52
53
gsl_integration_workspace *w_inner
= gsl_integration_workspace_alloc(10000);
54
55
56
57
gsl_function F_inner;
F_inner.function = &f_inner;
F_inner.params
= &params_inner;
58
59
gsl_integration_qag(&F_inner, -pi, pi, epsabs, epsrel, limit,
4.8 Thermalisierung in Gasen
181
2, w_inner, &result, &abserr);
60
61
62
gsl_integration_workspace_free(w_inner);
63
64
65
return result;
}
66
67
//------------------------------------------------------------------------
68
69
main( int argc, char *argv[] )
70
71
{
72
73
74
75
76
77
78
79
//-- Definition der Variablen
int
limit = 1000;
int
n, n1, n2, n3, nmax, nmax1;
double epsabs = 1.e-5, epsrel = 1.e-5;
double result, abserr;
double u1, u2, du, dphi, delta_u_array[1001*1001], delta_u_2[1001*1001];
double mu = 10;
80
81
82
ifstream in_stream;
ofstream out_stream;
83
84
85
86
87
88
89
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: sigma infile outfile\n";
exit(1);
}
90
91
92
93
94
95
96
97
98
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von sigma\n";
inout(alpha,"alpha"); inout(du,"du");
inout(nmax1,"nmax1");
in_stream.close();
out_stream.precision(12);
99
100
101
102
103
104
105
106
107
108
109
110
111
n = 0;
gsl_vector *delta_u_array_sorted = gsl_vector_alloc((nmax1+2)*(nmax1+1)/2);
for (n1 = 0; n1<=nmax1; n1++)
for (n2 = 0; n2<=n1; n2++)
{
u1 = n1 * du;
u2 = n2 * du;
delta_u_array[n] = sqrt( sqr(u1)+sqr(u2) );
gsl_vector_set(delta_u_array_sorted,n,delta_u_array[n]);
n++;
}
nmax = n;
112
113
114
115
116
117
118
size_t nsize = delta_u_array_sorted->size;
gsl_permutation * perm = gsl_permutation_alloc(nsize);
gsl_permutation * rank = gsl_permutation_alloc(nsize);
gsl_sort_vector_index(perm, delta_u_array_sorted);
gsl_permutation_inverse(rank,perm);
182
119
120
121
122
123
124
125
4 Statistische Physik
delta_u_2[0] = 0;
n = 1;
for (n1=1; n1<(nmax1+2)*(nmax1+1)/2 ; n1++)
if (delta_u_array[perm->data[n1]]!=delta_u_array[perm->data[n1-1]])
{ delta_u_2[n] = delta_u_array[perm->data[n1]]; n++; }
nmax = n;
cout << " nmax = " << nmax << "\n";
126
127
128
129
130
131
132
133
for (n1=0; n1<nmax; n1++)
{
for (n2=0; n2<nmax; n2++)
{
u_12
= delta_u_2[n1];
delta_u = delta_u_2[n2];
count_outer = 0;
134
135
136
gsl_integration_workspace *w_outer
= gsl_integration_workspace_alloc(10000);
137
138
139
140
gsl_function F_outer;
F_outer.function = &f_outer;
F_outer.params
= μ
141
142
143
gsl_integration_qags(&F_outer, -pi, pi, epsabs, epsrel, limit,
w_outer, &result, &abserr);
144
145
gsl_integration_workspace_free(w_outer);
146
147
148
149
150
151
result = 0.5 * result;
out_stream << u_12 << " " << delta_u << " " << result << "\n";
}
cout << n1 << " " << result << "\n";
}
152
153
154
out_stream.close();
}
In den Funktionen f inner bzw. f outer erkennen Sie den Integranden
für die innere bzw. äußere Integration, wobei letztere eine Integration über
f inner beinhaltet. Beachten Sie, dass der Integrand nur von den Beträgen
von u12 und ∆u abhängt. Das Programm macht davon insofern Gebrauch, als
es in den Zeilen 100–111 die vorkommenden Beträge sortiert und eventuell
mehrfach vorkommende Werte streicht. Im konkreten Fall von nmax = 35
reduziert dies den Aufwand von ca. 1.68 Millionen Berechnungen des Doppelintegrals auf 287296.
Nachdem wir nun den Wirkungsquerschnitt im u-Raum kennen, können
wir die Zeitentwicklung (4.62) selbst implementieren:
1
2
3
4
5
/**************************************************************************
* Name:
therm.cpp
*
* Zweck:
Berechnet die Thermalsierung von Gasen im Fourierraum
*
* Gleichung: siehe Buch
*
**************************************************************************/
6
7
8
#include <iostream>
#include <fstream>
4.8 Thermalisierung in Gasen
9
10
183
#include <math.h>
#include "tools.h"
11
12
using namespace std;
13
14
15
16
17
//-- globale
const int
const int
const int
Variablen
nmax = 35;
nmax_sorted = 536;
nges = 2*nmax+1;
18
19
//-------------------------------------------------------------------------
20
21
main( int argc, char *argv[] )
22
23
{
24
25
26
27
28
29
30
31
32
33
34
35
//-- Definition der Variablen
int
n1, n2, n3, n4, m1, m2, m3, m4, hit, n_t, n_t_max;
double s, sum_real, sum_imag, dt, dv, v1, v2, du, u1, u2, t_max, arg;
double N_1, v_1, sigma_1, a_1, N_2, v_2, sigma_2, a_2, v_quer, v2_quer;
double min_real, max_imag, sigma[nges][nges][nges][nges];
double P_u_real[nges][nges], P_u_imag[nges][nges];
double P_v_real[nges][nges], P_v_imag[nges][nges];
double dP_dt_real[nges][nges], dP_dt_imag[nges][nges], P_1d[nges];
const double eps = 1.e-4;
ifstream in_stream, in_stream2;
ofstream out_stream;
36
37
38
39
40
41
42
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<4)
{
cout << " Aufruf: therm infile1 infile2 outfile\n";
exit(1);
}
43
44
45
46
47
48
49
50
51
52
//-- Einlesen der Parameter -in_stream.open(argv[1]);
in_stream2.open(argv[2]);
out_stream.open(argv[3]);
out_stream << "! Ergebnis-Datei generiert von therm.cpp\n";
inout(N_1,"N_1");
inout(v_1,"v_1");
inout(sigma_1,"simga_1");
inout(N_2,"N_2");
inout(v_2,"v_2");
inout(sigma_2,"sigma_2");
inout(t_max,"t_max");
inout(n_t_max,"n_t_max");
in_stream.close();
53
54
55
56
57
58
59
//-- Berechnung einiger benoetigter Parameter -du = 0.4;
dt = t_max / n_t_max;
dv = pi / du / nmax;
a_1 = 0.5 / sqr(sigma_1);
a_2 = 0.5 / sqr(sigma_2);
60
61
62
63
64
65
66
67
//-- Zustand im Geschwindigkeitsraum
for (n1=-nmax; n1<=nmax; n1++)
for (n2=-nmax; n2<=nmax; n2++)
{
v1 = n1*dv;
v2 = n2*dv;
P_v_real[n1+nmax][n2+nmax] = N_1 * exp(-a_1*(sqr(v1-v_1)+sqr(v2)))
184
68
69
70
71
72
73
74
75
76
77
78
4 Statistische Physik
+ N_2 * exp(-a_2*(sqr(v1-v_2)+sqr(v2)));
P_v_imag[n1+nmax][n2+nmax] = 0;
P_u_imag[n1+nmax][n2+nmax] = 0;
sum_real = sum_real + P_v_real[n1+nmax][n2+nmax];
}
for (n1=-nmax; n1<=nmax; n1++)
for (n2=-nmax; n2<=nmax; n2++)
{
P_v_real[n1+nmax][n2+nmax] = P_v_real[n1+nmax][n2+nmax] / sum_real;
P_u_real[n1+nmax][n2+nmax] = P_v_real[n1+nmax][n2+nmax];
}
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//-- Transformation in den u-Raum
sum_real = 0;
for (n1=0; n1<nges; n1++)
for (n2=0; n2<nges; n2++)
{
u1 = (n1-nmax)*du;
u2 = (n2-nmax)*du;
P_u_real[n1][n2] = 0;
P_u_imag[n1][n2] = 0;
for (n3=0; n3<nges; n3++)
for (n4=0; n4<nges; n4++)
{
v1 = (n3-nmax)*dv;
v2 = (n4-nmax)*dv;
arg = u1*v1 + u2*v2;
P_u_real[n1][n2] = P_u_real[n1][n2] + P_v_real[n3][n4]*cos(arg);
P_u_imag[n1][n2] = P_u_imag[n1][n2] + P_v_real[n3][n4]*sin(arg);
}
sum_real = sum_real + sqr(P_u_real[n1][n2]) + sqr(P_u_imag[n1][n2]);
}
for (n1=0; n1<nges; n1++)
for (n2=0; n2<nges; n2++)
{
P_u_real[n1][n2] = P_u_real[n1][n2] / sum_real;
P_u_imag[n1][n2] = P_u_imag[n1][n2] / sum_real;
}
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//-- Einlesen von sigma im u-Raum
cout << "Einlesen!\n";
hit = 0;
for (n1 = 0; n1<nmax_sorted; n1++)
{
cout << n1 << "\n";
for (n2 = 0; n2<nmax_sorted; n2++)
{
in_stream2 >> u1;
in_stream2 >> u2;
in_stream2 >> s;
for (m1 = 0; m1<=nmax; m1++)
{
if (fabs(m1*du) > u1+eps) break;
for (m2 = sqrt(sqr(u1/du)-sqr(m1)); m2<=nmax; m2++)
if (fabs(sqrt(sqr(m1*du)+sqr(m2*du)) - u1) < eps)
{
for (m3 = 0; m3<=nmax; m3++)
{
if (fabs(m3*du) > u2+eps) break;
4.8 Thermalisierung in Gasen
for (m4 = sqrt(sqr(u2/du)-sqr(m3)); m4<=nmax; m4++)
if (fabs(sqrt(sqr(m3*du)+sqr(m4*du)) - u2) < eps)
{
sigma[m1+nmax][m2+nmax][m3+nmax][m4+nmax] = s;
sigma[-m1+nmax][m2+nmax][m3+nmax][m4+nmax] = s;
sigma[m1+nmax][-m2+nmax][m3+nmax][m4+nmax] = s;
sigma[-m1+nmax][-m2+nmax][m3+nmax][m4+nmax] = s;
sigma[m1+nmax][m2+nmax][-m3+nmax][m4+nmax] = s;
sigma[-m1+nmax][m2+nmax][-m3+nmax][m4+nmax] = s;
sigma[m1+nmax][-m2+nmax][-m3+nmax][m4+nmax] = s;
sigma[-m1+nmax][-m2+nmax][-m3+nmax][m4+nmax] = s;
sigma[m1+nmax][m2+nmax][m3+nmax][-m4+nmax] = s;
sigma[-m1+nmax][m2+nmax][m3+nmax][-m4+nmax] = s;
sigma[m1+nmax][-m2+nmax][m3+nmax][-m4+nmax] = s;
sigma[-m1+nmax][-m2+nmax][m3+nmax][-m4+nmax] = s;
sigma[m1+nmax][m2+nmax][-m3+nmax][-m4+nmax] = s;
sigma[-m1+nmax][m2+nmax][-m3+nmax][-m4+nmax] = s;
sigma[m1+nmax][-m2+nmax][-m3+nmax][-m4+nmax] = s;
sigma[-m1+nmax][-m2+nmax][-m3+nmax][-m4+nmax] = s;
hit++;
break;
}
}
break;
}
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
}
152
153
154
155
156
185
}
}
cout << " hit = " << hit << " ( Sollwert: " << pow(nmax+1,4) << ")\n";
in_stream2.close();
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//-- Zeitschleife
for (n_t = 1; n_t <= n_t_max; n_t++)
{
sum_real = 0;
sum_imag = 0;
for (n1=-nmax; n1<=nmax; n1++)
for (n2=-nmax; n2<=nmax; n2++)
{
dP_dt_real[n1+nmax][n2+nmax] = 0;
dP_dt_imag[n1+nmax][n2+nmax] = 0;
for (m1=max(-nmax,n1-nmax); m1<=min(nmax,n1+nmax); m1++)
for (m2=max(-nmax,n2-nmax); m2<=min(nmax,n2+nmax); m2++)
{
dP_dt_real[n1+nmax][n2+nmax] = dP_dt_real[n1+nmax][n2+nmax] +
P_u_real[n1-m1+nmax][n2-m2+nmax] * P_u_real[m1+nmax][m2+nmax]
* (sigma[n1-m1+nmax][n2-m2+nmax][n1+nmax][n2+nmax]
- sigma[n1-m1+nmax][n2-m2+nmax][0+nmax][0+nmax])
- P_u_imag[n1-m1+nmax][n2-m2+nmax] * P_u_imag[m1+nmax][m2+nmax]
* (sigma[n1-m1+nmax][n2-m2+nmax][n1+nmax][n2+nmax]
- sigma[n1-m1+nmax][n2-m2+nmax][0+nmax][0+nmax]);
dP_dt_imag[n1+nmax][n2+nmax] = dP_dt_imag[n1+nmax][n2+nmax] +
P_u_real[n1-m1+nmax][n2-m2+nmax] * P_u_imag[m1+nmax][m2+nmax]
* (sigma[n1-m1+nmax][n2-m2+nmax][n1+nmax][n2+nmax]
- sigma[n1-m1+nmax][n2-m2+nmax][0+nmax][0+nmax])
+ P_u_imag[n1-m1+nmax][n2-m2+nmax] * P_u_real[m1+nmax][m2+nmax]
* (sigma[n1-m1+nmax][n2-m2+nmax][n1+nmax][n2+nmax]
- sigma[n1-m1+nmax][n2-m2+nmax][0+nmax][0+nmax]);
}
186
186
187
188
189
190
191
192
193
194
195
196
197
4 Statistische Physik
sum_real = sum_real + sqr(dP_dt_real[n1+nmax][n2+nmax]);
sum_imag = sum_imag + sqr(dP_dt_imag[n1+nmax][n2+nmax]);
}
cout << n_t << " " << sum_real << " " << sum_imag << "\n";
for (n1=-nmax; n1<=nmax; n1++)
for (n2=-nmax; n2<=nmax; n2++)
{
P_u_real[n1+nmax][n2+nmax] = P_u_real[n1+nmax][n2+nmax]
+ dP_dt_real[n1+nmax][n2+nmax] * dt;
P_u_imag[n1+nmax][n2+nmax] = P_u_imag[n1+nmax][n2+nmax]
+ dP_dt_imag[n1+nmax][n2+nmax] * dt;
}
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
//-- Ruecktransformation in den v-Raum
sum_real = 0; v_quer = 0; v2_quer = 0; min_real = 1.e6; max_imag = 0;
for (n1=0; n1<nges; n1++)
for (n2=0; n2<nges; n2++)
{
v1 = (n1-nmax)*dv;
v2 = (n2-nmax)*dv;
P_v_real[n1][n2] = 0;
P_v_imag[n1][n2] = 0;
for (n3=0; n3<nges; n3++)
for (n4=0; n4<nges; n4++)
{
u1 = (n3-nmax)*du;
u2 = (n4-nmax)*du;
arg = -(u1*v1 + u2*v2);
P_v_real[n1][n2] = P_v_real[n1][n2] + P_u_real[n3][n4]*cos(arg)
- P_u_imag[n3][n4]*sin(arg);
P_v_imag[n1][n2] = P_v_imag[n1][n2] + P_u_real[n3][n4]*sin(arg)
+ P_u_imag[n3][n4]*cos(arg);
}
sum_real = sum_real + P_v_real[n1][n2];
}
//-- Projektion auf P_1d
for (n1=0; n1<nges; n1++)
{
P_1d[n1] = 0;
for (n2=0; n2<nges; n2++)
{
v1 = (n1-nmax)*dv;
v2 = (n1-nmax)*dv;
P_v_real[n1][n2] = P_v_real[n1][n2] / sum_real;
P_v_imag[n1][n2] = P_v_imag[n1][n2] / sum_real;
v_quer = v_quer + v1 * P_v_real[n1][n2];
v2_quer = v2_quer + (sqr(v1)+sqr(v2)) * P_v_real[n1][n2];
if (P_v_real[n1][n2]<min_real) min_real = P_v_real[n1][n2];
if (fabs(P_v_imag[n1][n2])>max_imag)
max_imag = fabs(P_v_imag[n1][n2]);
P_1d[n1] = P_1d[n1] + P_v_real[n1][n2];
}
out_stream << n1 << " " << P_1d[n1] << "\n";
}
cout << " v_quer = " << v_quer << "\n";
cout << " v2_quer = " << v2_quer << "\n";
cout << " min_real = " << min_real << "\n";
cout << " max_imag = " << max_imag << "\n";
}
4.8 Thermalisierung in Gasen
187
245
246
247
out_stream.close();
}
Hierbei haben wir uns, was den Anfangszustand anbelangt, auf zwei additiv überlagerte Gaußfunktionen festgelegt, deren Mittelwerte und Standardabweichungen (v 1 und sigma 1 bzw. v 2 und sigma 2) aus der Eingabedatei
eingelesen werden. Diese Situation deckt bereits zwei physikalisch interessante Problemstellungen ab:
–
ein Gasstrom trifft auf einen anderen bzw. auf ein ruhendes Gas, wobei
die kinetische Energie in Wärme umgewandelt wird,
– ein Teil des Gases hat eine andere Temperatur als der Rest und das Gemisch thermalisiert.
Nachdem wir in Zeile 61–78 den Ausgangszustand im Geschwindigkeitsraum konstruiert haben, müssen wir diesen in den u-Raum transformieren.
Da dies eine Fouriertransformation darstellt, bietet sich hierfür eigentlich eine FFT (siehe Anhang F) an. Man stellt jedoch fest, dass diese bei einem so
kleinen Wert von nmax kein befriedigendes Ergebnis liefert, so dass wir eine
zwar langsamere aber etwas genauere Alternative direkt, d.h. ohne Bibliotheksroutine, implementieren (Zeile 80–105).
Besondere Aufmerksamkeit verlangt auch das Einlesen des vorher berechneten Wirkungsquerschnitts σ (Zeile 107–154). Da das Programm sigma.cpp
diese Werte nach den Beträgen der beiden Argumente u12 und ∆u sortiert
abspeichert, müssen diese nun nach dem Einlesen von therm.cpp wieder umsortiert werden. Die hier gewählte Methode ist sicherlich nicht effektiv, aber
überschaubar und einigermaßen sicher: Im Prinzip werden für jeden eingelesenen Wert für alle Kombinationen von u12 und ∆u überprüft, ob deren
Beträge mit den eingelesenen übereinstimmt. Etwas beschleunigt wird dieses
Verfahren durch einige einfache Bedingungen, unter denen diese Suche abgebrochen werden kann. Zur Sicherheit wird zum Schluss (Zeile 155) überprüft,
ob jedes Element des Arrays sigma gefüllt wurde.
Nun folgt ab Zeile 158 die eigentliche Berechnung der Zeitentwicklung.
An dieser Stelle müssen wir uns fragen, was wir ausgeben wollen. Auf der
einen Seite sind dies Kontrollparameter, die wir auf den Bildschirm ausgeben, und anhand derer wir vorab kontrollieren können, ob das Ergebnis
korrekt sein kann. Auf der anderen Seite sind das die Größen, an denen wir
letztendlich interessiert sind und die uns einen guten Einblick in die ablaufende Physik geben. Zur Kontrolle eignen sich folgende unmittelbar einsichtige
Gesetzmäßigkeiten
Impulserhaltung: Die Mittelwerte von vx und vy müssen erhalten bleiben dabei ist insbesondere die x-Komponente interessant, da deren Mittelwert
in der Ausgangsverteilung nicht notwendigerweise null ist.
– Energieerhaltung: der Mittelwert von v 2 ist proportional zur Energie und
ist deshalb ebenfalls eine Erhaltungsgröße.
–
188
–
–
4 Statistische Physik
Im Geschwindigkeitsraum muss P reell sein.
Im Geschwindigkeitsraum darf kein Wert von P negativ werden.
Die letzten beiden Aussagen ergeben sich aus der Wahrscheinlichkeitsinterpretation von P (v). Im Programm überprüfen wir dies durch die Variablen
v quer (Impulserhaltung), v2 quer (Energieerhaltung), den betragsmäßig
größten Imaginärteil von P max imag, sowie den kleinsten Realteil von P
min real.
Schwieriger fällt die Entscheidung, was man letztlich in der Ergebnisdatei
haben möchte. In unserem Fall wollen wir einfach die Verteilung P (vx ) zu
verschiedenen Zeitpunkten ausgeben. Dazu projezieren wir die usprünglich
zweidimensionale Verteilung P (v), die im Feld P v real gespeichert ist, auf
P 1d. Auf der CD finden Sie ein kleines Hilfsprogramm extract therm.cpp,
das aus der Ergebnisdatei von therm.cpp einzelne Geschwindigkeitsverteilungen P (vx ) extrahiert.
P
0.06
(a)
P
0.03
0.06
(b)
P
0.03
0.0
0
5
vx
(c)
0.03
0.0
-5
0.06
0.0
-5
0
5
vx
-5
0
5
vx
Abb. 4.13. Zeitentwicklung der Wahrscheinlichkeitsverteilung P (vx ) bei der Thermalisierung eines Gases, bei dem ein Teil nach rechs und ein Teil nach links strömt.
Links dargestellt ist der Ausgangszustand, in der Mitte die Verteilung bei t = 50
und rechts die Geschwindigkeitsverteilung bei t = 500
In Abb. 4.13 sehen Sie zunächst ganz links die Ausgangsverteilung, die
aus zwei getrennten Gaußpaketen besteht: Das linke davon ist der Teil des
Gases, der nach links strömt, während umgekehrt das rechte Gaußpaket zu
den Gasmolekülen gehört, die nach rechts strömen. Im mittleren Bild (in
den gewählten Zeiteinheiten bei t = 50) sind beide Gaußpakete etwas verbreitert, ihre Position scheint sich aber nicht verändert zu haben. In der
Abbildung ganz rechts finden Sie schließlich den Zustand bei t = 500. Die
usprüngliche Separation in zwei Gaußpakete ist nun verschwunden, und wir
haben einen Zustand ähnlich dem, der sich als Endzustand einstellen wird:
eine Gaußverteilung um den Urspung, die jedoch breiter ist als jedes der Anfangspakete ganz links war, da kinetische Energie in Wärme umgewandelt
wurde. Aus der Tatsache, dass die Verteilung auch bei t = 500 noch deutlich von einer Gaußfunktion abweicht, entnehmen wir, dass das thermische
Gleichgewicht noch nicht erreicht wurde. Sie werden sich sicherlich fragen,
warum wir den Endzustand, das sich einstellende thermische Gleichgewicht
in Form einer Gauß-Verteilung, hier nicht zeigen: Der Grund ist, dass einer unserer Kontrollparameter, nämlich v2 quer, der proportial zur Energie
Übungen
189
ist, eine langsame Drift zu größeren Werten hat. Mit anderen Worten: Die
Energie in unserem System ist nicht genau erhalten, sondern steigt durch numerische Fehler langsam an. Da der Grund hierfür in der Tatsache liegt, dass
der von uns berücksichtigte Teil des Geschwindigkeitsraumes nur sehr geringe
Geschwindigkeiten berücksichtigt, kann dieses Problem etwas entschärft werden, indem man die Anfangsverteilungen im Geschwindigkeitsraum schärfer
lokalisiert, also größere a 1 und a 2 wählt. Da dies jedoch den Nachteil hat,
dass die Anfangsverteilung im u-Raum ausgedehnter wird und wir dann dort
mit denselben Problem konfrontiert werden, verspricht das keine wirkliche
Lösung dieses Problems.
Wenn wir uns hingegen einer anderen Fragestellung zuwenden, die ebenfalls in die Kategorie Thermalisierung in Gasen fällt, kann sich die Situation
zu unseren Gunsten verändern. Betrachten wir z.B. die Dynamik in einem
ruhenden Gasgemisch, in dem eine chemische Reaktion stattfindet, z.B.
2 H2 + O2 → 2 H2 O ,
dann wird unsere Bewegungsgleichung zwar zunächst durch zwei neue Aspekte komplizierter:
–
statt einer Teilchensorte haben wir es nun mit drei verschiedenen Gasmolekülen zu tun, was zur Folge hat, dass wir drei Wahrscheinlichkeitsverteilungen P1 , P2 und P3 unterscheiden müssen;
– wir müssen auch rotatorische und vibratorische Freiheitsgrade berücksichten;
– die chemische Reaktion führt zu zusätzlichen Termen in der Mastergleichung.
Im Gegenzug kommen wir jedoch in den Genuss einer nicht zu unterschätzenden Vereinfachung: aus Symmetriegründen müssen alle Wahrscheinlichkeitsverteilungen Pi (v) radialsymmetrisch sein, können also nur von |v| abhängen.
Übungen
4.1 Brownsche Bewegung mit diskreten Schritten
Im Text betrug die Schrittweite bei der Brownschen Bewegung zunächst ±1
und wurde dann durch Gauß-förmig verteilte Schrittweiten ersetzt, um die in
Abb. 4.2 zu erkennende ‘Kammstruktur’ zu eliminieren. Alternativ können
Sie Schritte mit den Schrittweiten ±1 (jeweils mit Wahrscheinlichkeit p <
1/2) und null (mit Wahrscheinlichkeit 1−2p) wählen. Das bedeutet, dass dem
Teilchen neben der Möglichkeit, nach links und nach rechts zu springen, auch
die Möglichkeit eingeräumt wird, bei der aktuellen Position zu verharren.
Untersuchen Sie die sich ergebende Wahrscheinlichkeitsverteilung für
190
4 Statistische Physik
– sehr kleine p,
– p = 1/3,
– Werte von p, die nur geringfügig kleiner als 1/2 sind.
4.2 Brownsche Bewegung mit Reibungsterm
In Abschn. 4.6 haben wir die Annahme gemacht, dass bei jedem Stoß der
Impulsübertrag proportional zur Relativgeschwindigkeit ist. Durch diese Annahme haben wir ein realistisches Verhalten inklusive dem Erreichen einer
thermischen Gleichgewichtsverteilung im Geschwindigkeitsraum erreicht. Alternativ hätten wir die unsichtbaren Teilchen auch als viskose Flüssigkeit
auffassen können, was zu einem Reibungsterm im deterministischen Teil der
Bewegungsgleichung führt:
dv
= −µv .
(4.77)
dt det
Gehen Sie vom Programm rw4.cpp aus, implementieren Sie die nötigen Änderungen und untersuchen Sie das so beschriebene System.
4.3 Random-Walk
Eine weitere Annahme in Abschn. 4.6 war, dass die Einzelschritte beim
Random-Walk statistisch unabhängig voneinander erfolgen. Diese Bedingung
können Sie jedoch fallen lassen und stattdessen annehmen, dass die Wahrscheinlichkeit nach rechts zu wandern, davon abhängt, ob der vorangegangene Schritt ebenfalls nach rechts erfolgte oder nicht. Gehen Sie dazu der
Einfachheit halber von rw1.cpp aus und setzen für die Wahrscheinlichkeit
eines Sprunges nach links
(1/2) + α
wenn Sprung n nach rechts erfolgte
Pn+1,links =
(4.78)
(1/2) − α
wenn Sprung n nach links erfolgte.
Untersuchen Sie insbesondere Fälle mit positivem und negativem α.
4.4 Thermisches Gleichgewicht
In Abschn. 4.7 haben wir gesehen, dass sich für t → ∞ ein Gleichgewicht in
der Besetzung der beiden Potentialmulden einstellt. Diese Gleichgewichtsverteilung hängt von dem Potentialunterschied der beiden Minima ab. Schreiben
Sie ein Programm, das diese Potentialdifferenz in einer Schleife variiert und
jeweils die Besetzung Plinks und Prechts im Gleichgewichtszustand ermittelt.
Übungen
191
4.5 Thermisches Hüpfen
In Abschn. 4.7 haben wir das thermisches Hüpfen zwischen zwei Potentialminima betrachtet. Das dort vorgestellte Programm (je nach Fragestellung
th1.cpp oder th2.cpp) lässt sich aber leicht auf Potentiale erweitern, die
mehr Minima aufweisen, z.B:
V (x) = −V0 cos(αx) .
(4.79)
Beachten Sie, dass nun die Unterscheidung links ⇔ rechts zur Positionsbestimmung des Teilchens nicht mehr ausreicht. Untersuchen Sie, wie das Teilchen (ausgehend vom Minimum bei x = 0) zuerst in die beiden benachbarten
Minima bei x = ±2π/α wandert, dann zu den übernächsten Minima bei
x = ±4π/α und so weiter. Beschreiben Sie den Vorgang durch einen Random
Walk mit endlicher, fixer Sprungweite, wie wir es bei rw1.cpp getan haben.
4.6 Boltzmann-Verteilung
Beim thermischen Hüpfen in Abschn. 4.7 haben wir die sich für t → ∞
einstellende Gleichgewichtsverteilung bestimmt (Abb. 4.10). Diese Verteilung
sollte durch die Formel
P (x) =
1
exp(−mβV (x))
N
(4.80)
(Boltzmann-Verteilung) beschrieben werden. N ist dabei ein Normierungsfaktor, der Parameter β = 1/kT (k ist die Boltzamnn-Konstante und T die
Temperatur) kann aus der Tatsache bestimmt werden, dass im thermischen
Gleichgewicht die mittlere Energie in jedem Freiheitsgrad durch kT /2 gegeben ist:
1
1
mv 2 = kT .
(4.81)
2
2
Fügen Sie in das Programm th1.cpp die Zeilen ein, um v 2 zu berechnen und
auszugeben. Bestimmen Sie dann mβ und damit die Boltzmann-Verteilung
(4.80). Wenn Sie dieses Ergebnis zusammen mit der Endverteilung P (x, tend )
in einem Diagramm darstellen, sollten die beiden Kurven nicht mehr voneinander zu trennen sein.
5 Quantenmechanik
In diesem Kapitel wollen wir in eine Welt eintauchen, die sich erst im letzten
Jahrhundert aufgetan hat: der Welt der Quanten. Diese Welt blieb deshalb
so lange unentdeckt, weil sie sich meistens erst erschließt, wenn man extrem
kleine Objekte betrachtet bzw. es mit extrem niedrigen Energien zu tun hat.
Diese Welt des Allerkleinsten scheint sich oft dem gesunden Menschenverstand zu entziehen – was darauf zurückzuführen ist, dass letzterer aus unserer
Erfahrungswelt mit normalen Dimensionen entstammt. Trotz seiner scheinbaren Widersprüche existiert ein – einigermaßen – sauberes mathematisches
Modell, das in der Lage ist, alle Experimente mit ihren scheinbaren Widersprüchen zu beschreiben: die Quantenmechanik.
5.1 Die mathematische Struktur der Quantenmechanik
Die mathematische Struktur der Quantenmechanik könnte bereits ein Buch
für sich füllen – aus diesem Grund beschränken wir uns an dieser Stelle auf
das Nötigste.
Ein quantenmechanischer Zustand wird mathematisch durch einen sogenannten Zustandsvektor |ψ repräsentiert. Hierbei haben wir gleich die in der
Quantenmechanik übliche Schreibweise mit einem senkrechten Strich auf der
einen Seite und einem nach außen gerichteten Winkel auf der anderen Seite
eingeführt. Der zu diesen Zustandsvektoren zugehörige Vektorraum heißt Hilbertraum H und kann je nach Problem sehr unterschiedlich aussehen. Seine
mathematische Struktur kann zum Beispiel diejenige des C N sein, also des
Raumes der Vektoren mit N komplexen Zahlen; sie kann aber auch äquivalent zu der der normierbaren komplexen Funktionen über den reellen Zahlen
sein.
5.2 Operationen im Hilbertraum
In diesem Abschnitt wollen wir in Erinnerung rufen, welche Operationen mit
Zustandsvektoren definiert sind. Zum einen kann man zwei beliebige Vektoren
des Hilbertraums addieren und diese Summe ist dann wieder ein Vektor des
Hilbertraums. Diese Addition ist kommutativ, es gilt also:
194
5 Quantenmechanik
|ψ1 + |ψ2 = |ψ2 + |ψ1 .
(5.1)
Ebenso kann man jeden Vektor des Hilbertraumes mit komplexen Zahlen
multiplizieren, und das Ergebnis dieser Multiplikation liegt wieder im Hilbertraum.
α|ψ ∈ H .
(5.2)
Diese Multiplikation ist ebenfalls kommutativ und darüber hinaus assoziativ,
d.h. bei aufeinanderfolgenden Multiplikationen ist die Reihenfolge irrelevant
und wir können auch alternativ zuerst die beiden Multiplikatoren miteinander multiplizieren und anschließend das Produkt dieses Ergebnisses mit dem
Zustandsvektor bilden:
αβ|ψ = βα|ψ = (αβ)|ψ .
(5.3)
Wir können aber auch zwei Vektoren miteinander multiplizieren und erhalten auf diese Weise eine komplexe Zahl, das sogenannte Skalarprodukt.
An dieser Stelle möchten wir die in Abschn. 5.1 eingeführte Schreibweise um
die für den dazugehörigen hermitesch transponierten Vektor erweitern: ψ|.
Hiermit schreibt sich das Skalarprodukt zweier Vektoren besonders einfach:
ψ1 |ψ2 ∈ C .
(5.4)
Mit der Einführung des Skalarproduktes können wir zwei Begriffe aus der
Linearen Algebra übertragen:
–
Die Länge l (oder auch der Betrag) eines Vektors |ψ definiert sich durch
l2 = ψ|ψ .
(5.5)
– Zwei Vektoren, deren Längen von null verschieden sind und deren Skalarprodukt null ist, bezeichnen wir als orthogonal.
An dieser Stelle nun können wir eine Einschränkung bezüglich Zustandsvektoren im Hilbertraum machen: Es ist nämlich nicht jeder Vektor des Hilbertraums ein erlaubter Zustandsvektor – vielmehr sind ausschließlich normierte
Vektoren als Zustandsvektoren erlaubt.
Abschließend wollen wir nun noch lineare Abbildungen innerhalb des Hilbertraumes einführen. Eine solche Abbildung lässt sich durch einen (linearen)
Operator  beschreiben, der auf einen beliebigen Vektor des Hilbertraums
angewendet einen neuen Vektor ebenfalls aus dem Hilbertraum ergibt:
Â|ψ ∈ H .
(5.6)
Der Operatorcharakter wird durch das kleine Hütchen über dem A explizit
zum Ausdruck gebracht. Linear bedeutet in diesem Fall, dass für beliebige λ
Â(λ|ψ) = λ(Â|ψ)
Â(|ψ + |χ) = Â|ψ + Â|χ
gilt.
(5.7)
(5.8)
5.3 Eigenzustände und ihre Verwendung als Koordinatensysteme
195
Für das Folgende müssen wir außerdem festhalten, dass die Operatoren
im Hilbertraum nicht unbedingt kommutativ sind, d.h. dass zwischen dem
Produkt ÂB̂ und B̂ Â unterschieden werden muss, wie man es z.B. auch von
Matrizen kennt. Diese Nichtvertauschbarkeit von Operatoren spielt in der
Quantenmechanik eine große Rolle, und weil derartige Ausdrücke sehr oft
vorkommen, bekommt die Differenz
[Â, B̂] = ÂB̂ − B̂ Â
(5.9)
ein eigenes Symbol – eben die eckigen Klammern auf der linken Seite der
Gleichung – und wird Kommutator genannt.
Bis zu diesem Punkt haben wir gegenüber der klassischen Physik noch
nichts Neues eingeführt, da wir noch keinerlei Aussagen über diese Operatoren getroffen haben. Der entscheidende Punkt und der endgültige Schritt
zur Quantenmechanik wird nun durch ein Postulat (also eine prinzipiell nicht
beweisbare Behauptung) gemacht: Der Kommutator von Operatoren, die in
der klassischen Dynamik kanonisch konjugierte Größen sind, ist ih̄, also z.B.
[x̂, p̂x ] = ih̄ .
(5.10)
Der entsprechende Kommutator bei nicht kanonisch konjugierten Größen hingegen soll null sein:
[x̂, p̂y ] = 0 .
(5.11)
Abschließend wollen wir noch in Erinnerung rufen, was unter der Funktion eines Operators, z.B. unter sin(d/dx) zu verstehen ist. Die Festlegung ist
die, dass solche Funktionen über ihre Potenzreihen definiert sind. In unserem
Beispiel des Sinus benötigen wir also zunächst die Potenzreihe der Sinusfunktion
∞
1
(−1)n z n .
(5.12)
sin(z) =
n!
n=0
Wenn wir nun für z den Operator d/dx einsetzen, erhalten wir den gesuchten
Sinus der ersten Ableitung nach x. Beachten Sie bitte, dass der so gewonnene
Operator beliebig hohe Ableitungen nach x enthält!
5.3 Eigenzustände und ihre Verwendung
als Koordinatensysteme
Operatoren und Zustände sind zunächst sehr abstrakte Begriffe – zumindest
wenn wir konkrete Probleme numerisch lösen wollen, müssen wir diese in eine
in Zahlen gegossene Form bringen. Hierzu wollen wir zunächst auf spezielle Zustände im Hilbertraum eingehen, nämlich die Eigenzustände zu einem
vorgegebenen Operator Â. Dabei handelt es sich um Zustände, die sich bei
Anwendung dieses Operators bis auf einen Faktor reproduzieren, also:
196
5 Quantenmechanik
Â|λ = λ|λ .
(5.13)
Der Faktor λ wird in diesem Zusammenhang Eigenwert und (5.13) Eigenwertgleichung genannt, und wir haben die in der Quantenmechanik übliche
Schreibweise benutzt, in der der Eigenzustand |λ durch seinen Eigenwert
charakterisiert wird. Trotz der ähnlichen Schreibweise ist genau zwischen der
Zahl λ und dem Zustand |λ zu unterscheiden! Die Situation ist übrigens vollkommen analog zu Eigenvektoren und Eigenwerten zu einer Matrix M , falls
Ihnen dies aus der Linearen Algebra vertraut ist.
Zunächst ohne Beweis ein paar Fakten über Eigenzustände und Eigenwerte, die wir kennen müssen, um das folgende nachvollziehen zu können. Wenn
Sie an den Beweisen dieser Behauptungen interessiert sind, finden Sie diese
z.B. in [7] oder [28].
Wenn Sie einen Eigenzustand |λ zu einem Operator gefunden haben,
erhalten Sie durch Multiplikation mit einer beliebigen (von null verschiedenen) Zahl α weitere Eigenzustände α|λ.
– Hat man zu einem Eigenwert zwei Eigenvektoren, so ist auch die Summe
dieser beiden wieder ein Eigenvektor zum selben Eigenwert.
– für selbstadjungierte Operatoren stehen Eigenzustände zu verschiedenen
Eigenwerte stehen immer senkrecht aufeinander, d.h. ihr Produkt ist null:
–
λ1 |λ2 = 0 .
–
(5.14)
Die Eigenzustände einer großen Klasse von Operatoren, nämlich der Hermiteschen Operatoren, bilden ein vollständiges System, d.h. jeder beliebige Zustand lässt sich als Linearkombination dieser Eigenzustände schreiben:
ψλ |λ .
(5.15)
|ψ =
λ
–
Die Operatoren zu allen physikalischen Größen sind Hermitesch.
An dieser Stelle wollen wir noch kurz erklären, was unter Entartung zu
verstehen ist. Die ersten beiden der oben angeführten Punkte besagen, dass
die Eigenvektoren zu einem Eigenwert unter Hinzunahme des Nullvektors
einen (mindestens eindimensionalen) Untervektorraum des Hilbertraums bilden. Falls dieser Raum nun eine Dimension größer als eins hat, spricht man
von Entartung. Da die Berücksichtigung von Entartung in den meisten Fällen
nur einen erhöhten Schreibaufwand darstellt, werden wir dieses Phänomen,
wenn nicht explizit erforderlich, vernachlässigen.
Wieviele Eigenvektoren (und zugehörige Eigenwerte) gibt es nun zu einem
vorgegebenen Operator? Das hängt ganz vom Operator ab – es gibt Operatoren, zu denen überhaupt keine Eigenvektoren existieren, es gibt aber auch
Operatoren, die sozusagen die maximal mögliche Zahl von Eigenvektoren
haben, deren Eigenvektoren nämlich eine Basis bezüglich des Hilbertraums
5.4 Orts- und Impulsdarstellung
197
darstellen. Insbesondere gehören alle Hermiteschen Operatoren in diese Kategorie. Auf diese Weise liefern uns Hermitesche Operatoren eine Basis, in
die wir alle Vektoren des Hilbertraums entwickeln können.
Gegeben sei ein Hermitescher Operator  mit Eigenwerten a1 , a2 , . . . Da
die zugehörigen Eigenvektoren eine Basis bilden ist
|ψ =
ai |ψ|ai (5.16)
i
=
ψi |ai .
(5.17)
i
Hierbei haben wir die Komponenten ψi durch
ψi = ai |ψ
(5.18)
definiert.
Wir können jedoch nicht nur Zustände nach einer Basis entwickeln, sondern auch Operatoren:
ai |Ô|aj |ai aj |
(5.19)
Ô =
ij
=
Oij |ai aj | .
(5.20)
ij
Zusammenfassend sind wir nun in der Lage, sowohl Zustände als auch
Operatoren des Hilbertraums durch Zahlenwerte, nämlich durch Koordinaten
bzgl. einer Basis, darzustellen.
5.4 Orts- und Impulsdarstellung
Ein wichtiger Spezialfall des letzten Abschnittes ist die Darstellung bzgl.
der Eigenzustände des Orts- bzw. des Impulsoperators. Im Vorgriff auf den
nächsten Abschnitt stellen wir hier fest, dass die Eigenwerte der beiden Operatoren die Orts- bzw. die Impulsvektoren sind und damit ein dreidimensionales Kontinuum bilden. Unter Anwendung des vorhergehenden Abschnitts
können wir einen beliebigen Zustand also nach Ortseigenzuständen
(5.21)
|ψ = d3 x x|ψ|x
= d3 x ψ(x) |x
(5.22)
oder Impulseigenzuständen
|ψ =
=
d3 p p|ψ|p
(5.23)
d3 p ψ(p) |p
(5.24)
198
5 Quantenmechanik
entwickeln, wobei wir jedoch die Summation über die Eigenzustände durch
entsprechende Integrale ersetzen müssen, da diese ein Kontinuum bilden.
Wie sieht nun der Ortsoperator in der Ortsdarstellung aus? Dazu entwickeln wir
x1 |x|x2 = x2 x1 |x2 = x2 δ(x1 − x2 ) .
Der Ortsoperator selber ist also
3
x̂ = d x1 d3 x2 x2 δ(x1 − x2 )|x1 x2 |
= d3 x x|xx| .
(5.25)
(5.26)
(5.27)
(5.28)
Wenn wir dies nun auf einen ebenfalls in Ortsdarstellung gegebenen Zustand ψ(x) anwenden, erhalten wir
x|x̂|ψ = d3 x x x|x x |ψ
(5.29)
= x ψ(x) .
(5.30)
Der Ortsoperator x̂ wird also lediglich zu einem Faktor x.
Entsprechendes gilt natürlich für den Impulsoperator in Impulsdarstellung. Wie aber sieht der Impulsoperator in Ortsdarstellung aus? Das einzige,
was wir über die Beziehung zwischen Orts- und Impulsoperator wissen, ist
der Kommutator (5.10). Wenn dieser erfüllt sein soll, muss der Impulsoperator durch −ih̄d/dx gegeben sein, denn für jede beliebige Funktion ψ(x)
gilt:
d
d
+ ih̄ x ψ(x) = ih̄ψ(x) .
−xih̄
(5.31)
dx
dx
Entsprechend ist der Ortsoperator in Impulsdarstellung ih̄d/dp.
Aus der Darstellung des Impulsoperators in Ortsdarstellung und umgekehrt kann man auch entnehmen, dass die Transformation von der einen in
die andere Darstellung eine Fouriertransformation (siehe auch Anhang F)
sein muss:
3 i
1
d3 x ψ(x) exp
xp
(5.32)
ψ(p) = √
h̄
2πh̄
und entsprechend
ψ(x) =
√
1
2πh̄
3 i
d3 p ψ(p) exp − xp .
h̄
(5.33)
5.6 Schrödingergleichung
199
5.5 Die Kopenhagener Interpretation
der Quantenmechanik
Bis zu diesem Punkt haben wir jede Menge mathematischer Begriffe eingeführt (Zustandsvektor, Skalarprodukt, Operator, Kommutator, . . . ), aber
noch keinen physikalischen Bezug hergestellt. Letztendlich aber muss die
Quantenmechanik wie jede andere physikalische Theorie den Ausgang von
Experimenten vorhersagen. Diesen Zusammenhang zwischen dem mathematischen Formalismus und dem Ergebnis von Messvorgängen liefert die sogenannte Kopenhagener Interpretation, die sich in drei Postulate zusammenfassen lässt.
Das erste Postulat macht eine Aussage darüber, welche Ergebnisse die
Messung haben kann. Wird an einem quantenmechanischen System die Messung einer Observablen O vorgenommen, so wird (Messfehler außer Acht
gelassen) immer ein Eigenwert o des zugehörigen Operators Ô gemessen.
Das zweite Postulat konkretisiert nun das Gesagte, indem es eine Aussage über die Wahrscheinlichkeit für das Eintreffen der möglichen Ergebnisse
macht: Die Wahrscheinlichkeit P (o) für einen konkreten Eigenwert o ist gleich
dem Absolutbetrag der Projektion des Zustandsvektors auf den entsprechenden Eigenvektor:
P (o) = |o|ψ|2 .
(5.34)
An dieser Stelle führen wir also eine Zufallskomponente ein – auch wenn
wir den Zustand des Systems genau kennen, können wir den Ausgang eines
konkreten Experiments nicht vorhersagen – es sei denn, dieser Zustand ist
ein Eigenzustand der Messobservablen. Dies hat eine andere Qualität als der
statistische Charakter aus der klassischen Physik (z.B. bei der Brownschen
Bewegung), der daher rührt, dass wir das System eben nicht vollständig kennen.
Das letzte Postulat schließlich legt fest, in welchem Zustand sich das System nach der Messung befindet, nämlich in der Projektion des ursprünglichen Zustands |ψ auf den Eigenraum zum Eigenwert o.
Mittels dieser Postulate lässt sich der Mittelwert einer Observablen berechnen:
P (on )on
(5.35)
O =
n
=
ψ|on on on |ψ
(5.36)
n
= ψ|Ô|ψ .
(5.37)
5.6 Schrödingergleichung
Wodurch wird nun die Dynamik in der Quantenmechanik festgelegt? Die
Grundgleichung – also das, was in der klassischen Mechanik die Newtonschen
200
5 Quantenmechanik
Axiome und in der Elektrodynamik die Maxwellschen Gleichungen sind – ist
hier die Schrödingergleichung, die festlegt, wie sich ein Zustand mit der Zeit
verändert:
d
(5.38)
ih̄ |ψ(t) = Ĥ|ψ(t) .
dt
Hierbei ist Ĥ der Hamiltonoperator, das quantenmechanische Pendant zur
Hamiltonfunktion, die in der klassischen Mechanik – im skleronomen Fall –
die Energie eines Systems als Funktion aller Orte und Impuls angibt.
Dabei haben wir uns auf das sogenannte Schrödingerbild beschränkt, in
dem die Zustandsvektoren zeitabhängig sind und die Operatoren nur eine
explizite Zeitabhängigkeit haben können. Auf die umgekehrte Sichtweise des
Heisenbergbildes werden wir in diesem Buch nicht eingehen.
5.7 Bestimmung des Hamilton-Operators
Wie wir aus (5.38) entnehmen, müssen wir zunächst den Hamiltonoperator
eines Problems bestimmen, ehe wir uns daran machen können, seine Bewegungsgleichung zu lösen. Dieser Schritt entspricht dem Bestimmen der Bewegungsgleichungen im Fall der klassischen Physik. Für Probleme, die eine
klassische Entsprechung haben, also zum Beispiel die Bewegung eines Teilchens in einem Potential, kann der Hamiltonoperator der Quantenmechanik
aus der Hamiltonfunktion der klassischen Physik bestimmt werden – daher
auch die Namensgebung Hamiltonoperator. Dies geschieht einfach dadurch,
dass man die klassischen Größen durch die entsprechenden Operatoren ersetzt – also die Ortsvariable x durch den Ortsoperator x̂, die Impulsvariable
p durch den Impulsoperator p̂ und so weiter.
5.8 Das freie Teilchen
Bevor wir uns komplizierteren Problemen zuwenden, wollen wir das in den
beiden vorangegangenen Abschnitten Zusammengefasste festigen, indem wir
es auf ein sehr einfaches Problem anwenden. Das einfachste Problem der
klassischen Physik ist ein freies Teilchen ohne äußere Kräfte, das sich in einem
Inertialsystem bekanntlich gleichförmig bewegt. Seine Hamiltonfunktion ist
durch
1 2
1 2
px + p2y + p2z
p =
(5.39)
H=
2m
2m
gegeben. Daraus ergibt sich der Hamiltonoperator des entsprechenden quantenmechanischen Problems:
Ĥ =
1 2
p̂x + p̂2y + p̂2z ,
2m
(5.40)
5.8 Das freie Teilchen
201
das heißt, sowohl H als auch die Impulskomponenten px , py und pz haben nun
Operatorcharakter. Das gleiche gilt für die Komponenten des Ortsvektors x,
y und z, die in (5.40) aber nicht auftreten.
Da der Hamiltonoperator eine Funktion des Impulsoperators ist, ist dieses
Problem am Einfachsten in der Impulsdarstellung zu lösen. Dazu entwickeln
wir die Schrödingergleichung nach den Impulseigenzuständen |px , py , pz =
|p (die analytische Betrachtung können wir dreidimensional durchführen,
während wir uns bei der nachfolgenden numerischen Behandlung auf eine
Dimension beschränken müssen):
d
1
d3 p p|ψ(t)|p =
d3 p p2 p|ψ(t)|p .
ih̄
(5.41)
dt
2m
Da die Impulseigenzustände |p linear unabhängig sind, kann dies nur erfüllt
sein, wenn die beiden Integranden gleich sind:
ih̄
d
d
p|ψ(t) = ih̄ ψ(p, t)
dt
dt
1 2
p ψ(p, t) .
=
2m
(5.42)
(5.43)
Diese Differentialgleichung lässt sich direkt lösen und wir erhalten als Lösung:
1 2
ψ(p, t) = ψ(p, 0) exp −i
p t .
(5.44)
2mh̄
Nachdem wir die Lösung im Impulsraum haben, können wir diese mittels
(5.33) in den Ortsraum transformieren:
ψ(x, t) =
1
√
2πh̄
3 i
d p ψ(p, t) exp − xp
h̄
3
.
(5.45)
Schreiten wir nun zur numerischen Implementation des Problems und
beginnen mit der vielleicht naheliegendsten Lösung: Die Schrödingergleichung
ist im Ortsraum eine partielle Differentialgleichung, die Ableitungen erster
Ordnung nach der Zeit enthält:
ih̄
∂
h̄2 ∂ 2
ψ(x) = −
ψ(x) .
∂t
2m ∂x2
(5.46)
Diese Differentialgleichung können wir direkt einer entsprechenden Bibliotheksroutine übergeben. Das Ergebnis könnte dann in ungefähr so aussehen
(im Folgenden setzen wir h̄ = m = 1):
1
2
3
4
5
/**************************************************************************
* Name:
frei1.cpp
*
* Zweck:
Simuliert ein quantenmechanisches freies Teilchen
*
* Gleichung: Schroedingergleichung ohne Potential
*
* verwendete Bibiliothek: GSL
*
202
6
5 Quantenmechanik
**************************************************************************/
7
8
9
10
11
12
13
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<math.h>
"tools.h"
14
15
using namespace std;
16
17
18
//-- Zahl der Stuetzstellen
const int nmax = 8192;
19
20
21
//-- globale Variablen
double dx;
22
23
//-------------------------------------------------------------------------
24
25
int f(double t, const double psi[], double dpsi_dt[], void *params)
26
27
28
29
{
int n;
double norm, norm1, norm2;
30
31
32
33
//-- Realteil
for (n=1; n<nmax-1; n++)
dpsi_dt[n] = -(psi[nmax+n+1]+psi[nmax+n-1]-2*psi[nmax+n]) / sqr(dx);
34
35
36
dpsi_dt[0]
= -(psi[nmax+1]+psi[2*nmax-1]-2*psi[nmax]) / sqr(dx);
dpsi_dt[nmax-1] = -(psi[nmax]+psi[2*nmax-2]-2*psi[2*nmax-1])/ sqr(dx);
37
38
39
40
//-- Imaginaerteil
for (n=nmax+1; n<2*nmax-1; n++)
dpsi_dt[n] = +(psi[n+1-nmax]+psi[n-1-nmax]-2*psi[n-nmax]) / sqr(dx);
41
42
43
dpsi_dt[nmax]
= +(psi[1]+psi[nmax-1]-2*psi[0]) / sqr(dx);
dpsi_dt[2*nmax-1] = +(psi[0]+psi[nmax-2]-2*psi[nmax-1]) / sqr(dx);
44
45
46
return GSL_SUCCESS;
}
47
48
//--------------------------------------------------------------------
49
50
51
int jac(double t, const double x[], double *dfdx, double dxdt[],
void *params)
52
53
54
55
{
return GSL_SUCCESS;
}
56
57
//-------------------------------------------------------------------------
58
59
main( int argc, char *argv[] )
60
61
62
63
64
{
//-- Definition der Variablen
int
n, n1, n_start, n_out, max_fct, status;
double psi[2*nmax], dpsi_dt[2*nmax], t, t1, t2, tend, dt, rtol, atol;
5.8 Das freie Teilchen
65
66
67
68
203
double alpha, p_0, x, h, hmax, daux, sum;
double mu = 10;
ifstream in_stream;
ofstream out_stream;
69
70
71
72
73
74
75
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: frei1 infile outfile\n";
exit(1);
}
76
77
78
79
80
81
82
83
84
85
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von frei1.cpp\n";
inout(tend,"tend");
inout(dx,"dx");
inout(n_out,"n_out");
inout(alpha,"alpha");
inout(p_0,"p_0");
inout(rtol,"rtol");
inout(atol,"atol");
in_stream.close();
86
87
88
89
//-- Berechnung einiger benoetigter Parameter -dt = tend / n_out;
h = 1.e-6;
90
91
92
93
94
95
96
97
98
99
100
//-- Anfangsbedingungen
t = 0;
max_fct = 1000000;
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
psi[n] = exp(-sqr(x/alpha)/2);
sum = sum + sqr(psi[n]);
}
sum = 1 / sqrt(sum);
101
102
103
104
105
106
107
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
psi[n+nmax] = -psi[n] * sum * sin(p_0*x);
psi[n] = psi[n] * sum * cos(p_0*x);
}
// Imaginaerteil
// Realteil
108
109
110
111
out_stream << t << "\n";
for (n=0; n<nmax; n++)
out_stream << psi[n] << " " << psi[n+nmax] << "\n";
112
113
114
115
116
117
const gsl_odeiv_step_type *T = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, 2*nmax);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(2*nmax);
gsl_odeiv_system sys = {f, jac, 2*nmax, &mu};
118
119
120
121
122
123
//-- Zeitschleife -t = 0;
for (n=1; n<=n_out; n++)
{
t1 = n * dt;
204
124
125
126
127
128
129
130
131
132
133
5 Quantenmechanik
cout << n << " " << t << "\n";
while (t<t1)
{
status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t1, &h, psi);
if (status != GSL_SUCCESS) break;
}
out_stream << t << "\n";
for (n1=0; n1<nmax; n1++)
out_stream << psi[n1] << " " << psi[n1+nmax] << "\n";
}
134
135
136
137
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
138
139
140
out_stream.close();
}
Besonderes Augenmerk verdient hier die Berechnung der zweiten Ableitung der Wellenfunktion ψ nach der sogenannten Dreipunktformel in Zeilen
33 und 40:
d2
1
ψ(xn ) ≈
(ψ(xn+1 ) − 2ψ(xn ) + ψ(xn−1 )) .
d2 x
(∆x)2
(5.47)
Am Rand des abgedeckten Intervalls, also bei x0 und xnmax−1 , lässt sich diese
Formel nicht direkt anwenden, da ψ(x−1 ) und ψ(xnmax ) nicht bekannt sind.
Für’s erste haben wir hier periodische Randbedingungen angenommen, bei
denen diese beiden fehlenden Werte durch ψ(xnmax−1 ) und ψ(x0 ) ersetzt werden. Wir werden diesen Punkt noch eingehender beleuchten und verschiedene
Alternativen zu dieser Wahl erläutern.
Ein zweiter Punkt, der der Erläuterung bedarf, ist die Konstruktion des
Ausgangszustandes in Zeile 91 bis 107. Wir könnten diesen Ausgangszustand
zwar auch direkt aus der – entsprechend modifizierten – Eingabedatei einlesen; wir werden uns aber an dieser Stelle auf eine kleine Klasse von Ausgangszuständen beschränken, die durch lediglich zwei Parameter festgelegt
werden. Dabei handelt es sich um Gauß-förmige Wellenpakete, die sich zum
Zeitpunkt t = 0 am Ursprung befinden und sich mit einem mittleren Impuls p̄ bewegen. Außer diesem mittleren Impuls braucht man nur noch die
anfängliche Breite α des Wellenpakets im Ortsraum.
Zunächst aber wollen wir uns die Frage stellen, ob unser Programm vom
Standpunkt der Performance und der Stabilität eine gute Implementation des
gegebenen Problems darstellt. Die Antwort auf diese Frage muss – zumindest
in der gegenwärtigen Form – eindeutig nein lauten; wir wollen jedoch hier
schon anmerken, dass wir auf das Programm frei1.cpp noch zurückgreifen werden. Der Grund dafür, dass frei1.cpp in puncto Performance nicht
zufriedenstellen kann, ist die Tatsache, dass wir keinerlei Gebrauch davon
gemacht haben, dass uns im Impulsraum die analytische Lösung bekannt
ist (5.44), weswegen wir eine hohe Zahl von Teilschritten ∆t in Kauf nehmen, in die die Bibliotheksroutine zur Lösung der Differentialgleichung die
5.8 Das freie Teilchen
205
Zeit unterteilt. Ein auf der analytischen Lösung im Impulsraum aufbauendes
Programm könnte folgendermaßen vorgehen:
–
Transformation des Ausgangszustandes im Ortsraum ψ(x, t = 0) in den
Impulsraum ψ(p, t = 0) durch Fouriertransformation.
– Berechnung des Zustandes ψ(p, tn ) zu den Zeiten tn , zu denen die Lösung
erwünscht ist mittels (5.44).
– Rücktransformation dieser Zustände in den Ortsraum durch inverse Fouriertransformation.
Diese Vorgehensweise ist in dem folgenden Programm realisiert:
1
2
3
4
5
6
/**************************************************************************
* Name:
frei2.cpp
*
* Zweck:
Simuliert ein quantenmechanisches freies Teilchen
*
* Gleichung: Schroedingergleichung ohne Potential
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_fft_complex.h>
"tools.h"
13
14
using namespace std;
15
16
17
//-- Zahl der Stuetzstellen
const int nmax = 8192;
18
19
//-------------------------------------------------------------------------
20
21
main( int argc, char *argv[] )
22
23
24
25
26
27
28
29
30
31
32
{
//-- Definition der Variablen
int
n, n1, n_start, n_out, max_fct, status;
double psi[2*nmax], psi_p[2*nmax], t, t1, t2, tend, dt, alpha;
double x, dx, p, dp, p_0, arg, sum;
double mu = 10;
ifstream in_stream;
ofstream out_stream;
gsl_fft_complex_wavetable * wavetable;
gsl_fft_complex_workspace * workspace;
33
34
35
36
37
38
39
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: frei2 infile outfile\n";
exit(1);
}
40
41
42
43
44
45
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von frei2.cpp\n";
inout(tend,"tend");
inout(dx,"dx");
206
46
47
48
5 Quantenmechanik
inout(n_out,"n_out");
inout(p_0,"p_0");
in_stream.close();
inout(alpha,"alpha");
49
50
51
52
//-- Berechnung einiger benoetigter Parameter -dt = tend / n_out;
dp = 2 * pi / dx / nmax;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//-- Anfangsbedingungen
t = 0;
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
psi[2*n] = exp(-sqr(x/alpha)/2);
sum = sum + sqr(psi[2*n]);
}
sum = 1 / sqrt(sum);
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
psi[2*n+1] = -psi[2*n] * sum * sin(p_0*x);
psi[2*n]
= psi[2*n] * sum * cos(p_0*x);
}
// Imaginaerteil
// Realteil
69
70
71
72
//--Anfangszustand im Impulsraum
for (n=0; n<=2*nmax; n++) psi_p[n] = psi[n];
gsl_fft_complex_radix2_forward (psi_p, 1, nmax);
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//-- Zeitschleife -t = 0;
for (n=1; n<=n_out; n++)
{
t1 = n * dt;
cout << n << " " << t1 << " " << t1*dp*nmax/2 << "\n";
for (n1=0; n1<nmax/2; n1++)
{
p = n1 * dp;
arg = sqr(p)*t1/2;
psi[2*n1]
= psi_p[2*n1] * cos(arg) - psi_p[2*n1+1] * sin(arg);
psi[2*n1+1] = psi_p[2*n1] * sin(arg) + psi_p[2*n1+1] * cos(arg);
}
for (n1=nmax/2; n1<nmax; n1++)
{
p = (n1-nmax) * dp;
arg = sqr(p)*t1/2;
psi[2*n1]
= psi_p[2*n1] * cos(arg) - psi_p[2*n1+1] * sin(arg);
psi[2*n1+1] = psi_p[2*n1] * sin(arg) + psi_p[2*n1+1] * cos(arg);
}
gsl_fft_complex_radix2_inverse (psi, 1, nmax);
out_stream << t << "\n";
for (n1=0; n1<nmax; n1++)
out_stream << (n1-nmax/2)*dx << " " << psi[2*n1] << " " << psi[2*n1+1]
<< " " << sqr(psi[2*n1])+sqr(psi[2*n1+1]) << "\n";
}
100
101
102
out_stream.close();
}
5.8 Das freie Teilchen
207
Betrachten wir nun die Lösungen, die uns dieses oder das vorangegangene
Programm liefert, so scheint sich das Wellenpaket zunächst wie erwartet zu
verhalten:
2
| |
0.003
(a)
2
| |
0.002
0.001
0.0
-100 -50
0.003
(b)
2
| |
0.002
0.001
0
50 100
x
0.0
-100 -50
0.003
(c)
0.002
0.001
0
50 100
0.0
-100 -50
x
0
50 100
x
Abb. 5.1. Zeitentwicklung eines Wellenpakets berechnet mit frei2.cpp. Zunächst
wandert das Paket nach rechts, wenn es den Rand des abgedeckten Ortsbereichs
erreicht, verschwindet es dort und taucht am entgegengesetzten Ende wieder auf
Das Wellenpaket wandert aufgrund seines positiv gewählten Anfangsimpulses p0 nach rechts. Gleichzeit läuft das Wellenpaket auseinander, d.h. es
verliert an Höhe und wird breiter. Interessant wird es, wenn es den Rand des
betrachteten Ortsbereichs erreicht: Während ein Teil des Pakets scheinbar
nach rechts das Intervall verlässt, kommt ein Double unseres Wellenpakets
am anderen Ende des Intervalls ins Spiel. Diese implizite Periodizität können
wir schon am Programmcode an einigen Punkten festmachen:
–
Im Programm frei1.cpp werden die Randbedingungen in den Zeilen
35/36 sowie in 42/43 explizit festgelegt. Indem wir z.B. das eigentlich
benötigte psi[-1] (links vom x-Bereich) durch psi[nmax-1] (am rechten Rand des x-Bereichs) ersetzt haben, haben wir uns für periodische
Randbedingungen entschieden. An dieser Stelle lassen sich also die Randbedingungen verhältnismäßig einfach ändern.
– Im Programm frei2.cpp sehen wir, dass die Zeitentwicklung sich in Impulsdarstellung lediglich in einer Phase exp(−ip2 t/2) äußert (Zeile 84/85
und 91/92). Es ist also offensichtlich, dass die Norm der Wellenfunktion erhalten bleibt und ein Wellenpaket nicht einfach an den Enden des
Ortsbereichs verschwinden kann.
– Die Periodizität im Ortsbereich liegt im Programm frei2.cpp in der Verwendung der FFT, die eigentlich keine Fouriertransformation berechnet,
sondern eine Fourierreihenentwicklung (siehe Anhang F).
Versuchen wir es zunächst mit einer Änderung der Randbedingungen in
den Zeilen 35/36 und 42/43 von frei1.cpp, indem wir diese durch
dpsi_dt[0]
= - (psi[nmax+1]-2*pis[nmax]) / sqr(dx);
dpsi_dt[nmax-1] = - (psi[2*nmax-2]-2*psi[2*nmax-1]) / sqr(dx)
bzw. durch
dpsi_dt[nmax]
= + (psi[1]-2*psi[0]) / sqr(dx);
dpsi_dt[2*nmax-1] = + (psi[nmax-2]-2*psi[nmax-1]) / sqr(dx)
208
5 Quantenmechanik
ersetzen. Dadurch dass wir ψ jenseits des Intervalls ersatzlos haben wegfallen
lassen, implizieren wir, dass die Wellenfunktion außerhalb des betrachteten
Ortsbereichs null ist – was bei einem unendlich tiefen Potentialtopf der Fall
ist. Entsprechend bekommen wir durch diese Änderung ein Verhalten analog
zu dem beim Potentialtopf: das Wellenpaket wird beim Erreichen des Randes
des Potentialtopfs an demselben reflektiert – auch nicht das Verhalten, das
wir im Normalfall haben wollen. Vielmehr wünschen wir in der Mehrheit der
Fälle, dass das Teilchen nach dem Erreichen des Randes den betrachteten
Ortsbereich verlässt ohne irgendwo anders wiederaufzutauchen.
Um zu verstehen, wie wir ein solches Verhalten realisieren können, müssen
wir uns zunächst Gedanken über die Normerhaltung machen – denn genau
diese müssen wir aushebeln, wenn wir wollen, dass das Teilchen irgendwo
verschwindet. Betrachten wir hierzu die Dynamik eines Teilchens in einem
Potential V (x). Die Schrödingergleichung in Ortsdarstellung lautet in skalierten Einheiten
i
1 d2
d
ψ(x, t) = −
ψ(x, t) + V (x)ψ(x) .
dt
2 dx2
Damit können wir bestimmen, ob sich die Norm der Wellenfunktion
d
d
N =
dx ψ (x)ψ(x)
dt
dt
d2
i
dx
ψ (x) 2 ψ(x) − c.c.
=
2
dx
− (ψ (x)(V (x) − V (x))ψ(x)) .
(5.48)
ändert:
(5.49)
(5.50)
(5.51)
Hierbei bedeutet c.c. den zum Vorausgegangenen konjugierten Ausdruck.
Durch partielle Integration sieht man, dass der erste Term zu keiner Änderung
der Norm führen kann. Bei normalen, also reellen Potentialen verschwindet
auch der zweite Term – wie es auch sein muss, da in Wirklichkeit kein Teilchen einfach verschwinden kann. Gleichzeitig liefert der Ausdruck (5.51) aber
einen Hinweis, was man tun muss, um absorbierende Randbedingungen zu
implementieren: Man muss in diesem Bereich ein imaginäres Potential V (x)
hinzufügen. Da solche Potentiale zuerst in der klassischen Optik verwendet
wurden, werden diese auch als optische Potentiale bezeichnet.
Ein solches – zwangsweise ortsabhängiges Potential – führt jedoch dazu, dass das Problem nicht mehr durch Transformation in den Impulsraum
gelöst werden kann. Das Programm frei2.cpp kann also so nicht mehr verwendet werden. Andererseits haben wir festgestellt, dass frei1.cpp sehr viele
Teilschritte und damit enorme Rechenzeiten benötigt. Wir müssen also nach
einem Weg suchen, die analytische Lösung für das Ausgangsproblem (ohne
Potential) mit einem ortsabhängigen Potential zu verknüpfen. Eine Möglichkeit besteht in der Näherung
exp(i(Ĥ1 + Ĥ2 )∆t) ≈ exp(iĤ1 dt) exp(iĤ2 ∆t) ,
(5.52)
5.8 Das freie Teilchen
209
die für beliebige Operatoren H1 und H2 möglich ist. In unserem Fall wählen
wir
1
H1 = − d2 dx2
2
H2 = V (x)
(5.53)
(5.54)
und trennen die Dynamik damit in zwei Abschnitte, die diagonal im Impulsraum bzw. im Ortsraum sind. Den Fehler, den wir dabei machen, ist von der
Ordnung
[Ĥ1 , Ĥ2 ](∆t)2 ,
(5.55)
d.h. im Gegensatz zu der Situation ohne Potential müssen wir die Zeitentwicklung in viele kleine Zeitschritte unterteilen, um den Fehler gering zu halten,
was zunächst keine Verbesserung gegenüber frei1.cpp verspricht. Außerdem
stellen wir mit etwas Enttäuschung fest, dass der Fehler wesentlich größer ist
als bei den Verfahren zur Integration der Schrödingergleichung im Ortsraum,
die wir im Programm frei1.cpp eingesetzt haben (dort war der Fehler in
der Größenordnung (∆t)5 ). Zunächst ist also nicht einzusehen, warum ein
auf dieser Näherung basierendes Programm eine Verbesserung darstellen sollte. Einen Punkt wollen wir jedoch noch erwähnen, bevor wir daran gehen,
die Näherung (5.52) in ein konkretes Computerprogramm umzusetzen: Eine
wichtige Eigenschaft bleibt dieser Näherung nämlich exakt erhalten: wenn
Ĥ1 und Ĥ2 hermitesch sind, bleibt die Norm der Wellenfunktion erhalten.
1
2
3
4
5
6
/**************************************************************************
* Name:
frei3.cpp
*
* Zweck:
Simuliert ein quantenmechanisches freies Teilchen
*
* Gleichung: Schroedingergleichung mit optischem Potential
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_fft_complex.h>
"tools.h"
13
14
using namespace std;
15
16
17
//-- Zahl der Stuetzstellen
const int nmax = 8192;
18
19
20
21
22
23
//-- globale Variablen
double psi[2*nmax], V[nmax];
double dp;
gsl_fft_complex_wavetable * wavetable;
gsl_fft_complex_workspace * workspace;
24
25
//-------------------------------------------------------------------------
26
27
int timestep(double dt)
28
29
{
210
30
31
5 Quantenmechanik
int
n, n1;
double psi_p[2*nmax], arg, p;
32
33
34
35
//-- Zustand im Impulsraum
for (n=0; n<=2*nmax; n++) psi_p[n] = psi[n];
gsl_fft_complex_radix2_forward (psi_p, 1, nmax);
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//-- Zeitentwicklung (freie Dynamik)
for (n1=0; n1<nmax/2; n1++)
{
p = n1 * dp;
arg = sqr(p)/2 * dt;
psi[2*n1]
= psi_p[2*n1] * cos(arg)
psi[2*n1+1] = psi_p[2*n1] * sin(arg)
}
for (n1=nmax/2; n1<nmax; n1++)
{
p = (n1-nmax) * dp;
arg = sqr(p)/2 * dt;
psi[2*n1]
= psi_p[2*n1] * cos(arg)
psi[2*n1+1] = psi_p[2*n1] * sin(arg)
}
- psi_p[2*n1+1] * sin(arg);
+ psi_p[2*n1+1] * cos(arg);
- psi_p[2*n1+1] * sin(arg);
+ psi_p[2*n1+1] * cos(arg);
52
53
54
//-- Ruecktransformation in den Ortsraum
gsl_fft_complex_radix2_inverse (psi, 1, nmax);
55
56
57
58
59
60
61
62
//-- Zeitentwicklung (optisches Potential)
for (n1=0; n1<nmax; n1++)
{
arg = - V[n1] * dt;
psi[2*n1]
= psi[2*n1] * exp(arg);
psi[2*n1+1] = psi[2*n1+1] * exp(arg);
}
63
64
65
return 0;
}
66
67
//--------------------------------------------------------------------
68
69
main( int argc, char *argv[] )
70
71
72
73
74
75
76
77
78
{
//-- Definition der Variablen
int
n, n1, n_start, n_out, n_interm, max_fct, status;
double t, t1, t2, tend, dt, rtol, atol;
double alpha, x, dx, p, p_0, a_opt, b_opt, arg, sum;
double mu = 10;
ifstream in_stream;
ofstream out_stream;
79
80
81
82
83
84
85
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: frei3 infile outfile\n";
exit(1);
}
86
87
88
//-- Einlesen der Parameter -in_stream.open(argv[1]);
5.8 Das freie Teilchen
89
90
91
92
93
94
95
96
211
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von frei3.cpp\n";
inout(tend,"tend");
inout(dx,"dx");
inout(n_out,"n_out");
inout(n_interm,"n_interm");
inout(alpha,"alpha");
inout(p_0,"p_0");
inout(rtol,"rtol");
inout(atol,"atol");
inout(a_opt,"a_opt");
inout(b_opt,"b_opt");
in_stream.close();
97
98
99
100
//-- Berechnung einiger benoetigter Parameter -dt = tend / n_out / n_interm;
dp = 2 * pi / dx / nmax;
101
102
103
104
105
106
107
108
//-- Berechnung des optischen Potentials
for (n=0; n<nmax; n++)
{
if (n < nmax/2) x = n * dx;
else x = (nmax-n) * dx;
V[n] = a_opt*exp( -sqr(b_opt*x) );
}
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//-- Anfangsbedingungen
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
psi[2*n] = exp(-sqr(x/alpha)/2);
sum = sum + sqr(psi[2*n]);
}
sum = 1 / sqrt(sum);
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
psi[2*n+1] = -psi[2*n] * sum * sin(p_0*x);
psi[2*n]
= psi[2*n] * sum * cos(p_0*x);
}
// Imaginaerteil
// Realteil
124
125
126
127
128
t = 0;
out_stream << n*dt << "\n";
for (n1=0; n1<nmax; n1++)
out_stream << psi[2*n1] << " " << psi[2*n1+1] << "\n";
129
130
131
132
133
134
135
136
137
138
139
//-- Zeitschleife -t = 0;
for (n=1; n<=n_out; n++)
{
for (n1=0; n1<n_interm; n1++) status = timestep(dt);
cout << n << " " << n_out << "\n";
out_stream << n*dt << "\n";
for (n1=0; n1<nmax; n1++)
out_stream << psi[2*n1] << " " << psi[2*n1+1] << "\n";
}
140
141
142
out_stream.close();
}
Zu den bisherigen Parametern kommen noch die Höhe a opt und die
inverse Breite b opt des (Gaußschen) optischen Potentials hinzu, sowie
n interm, das angibt, in wieviele intermediäre Zeitschritte jedes Zeitinter-
212
5 Quantenmechanik
vall zwischen zwei Zeiten, zu denen der Zustand abgespeichert wird, eingeteilt wird. Bei der Wahl der beiden Parameter a opt und b opt ist ein wenig
Fingerspitzengefühl gefragt:
– Ein zu kleines b opt schränkt den Bereich ein, in dem sich das Teilchen
tatsächlich wie ein freies Teilchen verhält.
– Ein zu großes b opt hingegen wirkt wie eine Wand: das Teilchen dringt
nur zu einem Teil in den Potentialwall ein, wo es absorbiert wird, während
der Rest reflektiert wird.
– Ein zu großes a opt hat ebenfalls den Effekt, dass ein nennenswerter
Teil der Wellenfunktion am optischen Potential reflektiert wird, anstatt
einzudringen.
– Ein zu kleines a opt schließlich erlaubt dem Teilchen, das Potential zu
durchdringen, und so auf der anderen Seite des Ortsbereichs wieder aufzutauchen.
Zur Verdeutlichung illustrieren wir die Zeitentwicklung für einige Parameter
von a opt in Abb. 5.2. Sie sehen, dass in der oberen Reihe der Wert von
2
| |
0.1
(a)
2
| |
0.05
0.0
-100 -50
0
0.1
(b)
| |
0.05
0.0
-100 -50
50 100
0
x
2
| |
0.1
2
| |
0.0
-100 -50
0
0.1
| |
0.1
2
| |
0
50 100
x
0
| |
0
0.1
2
0.1
(f)
0.05
0.0
-100 -50
50 100
0
0.0
-100 -50
| |
0
50 100
x
50 100
x
(h)
0.05
50 100
x
x
(g)
0.0
-100 -50
0.0
-100 -50
50 100
0.05
0.0
-100 -50
50 100
0.05
(c)
0.05
(e)
x
2
0.1
x
(d)
0.05
2
2
0.1
(i)
0.05
0.0
-100 -50
0
50 100
x
Abb. 5.2. Zeitentwicklung eines Wellenpakets und dessen Absorption sowie Reflektion an einem optischen Potential, das an den beiden Enden des dargestellten
Ortsbereichs lokalisiert ist. In der oberen Bildreihe ist a opt = 0.1, in der mittleren
Reihe ist a opt = 0.8 und in der unteren Reihe ist a opt = 3. Gezeigt wird jeweils
der Ausgangszustand des Wellenpakets (linkes Bild), das Wellenpaket im Bereich
des optischen Potentials (mittleres Bild) und das Wellenpaket nach Durchlaufen
des optischen Potentials (rechtes Bild)
5.9 Eigenzustände des Hamiltonoperators
213
a opt zu klein ist – das Teilchen durchdringt rechts den Bereich des optischen
Potentials, erreicht den Rand und taucht auf der linken Seite auf. In der
mittleren Reihe ist der Parameter a opt so gewählt, dass das Teilchen das
optische Potential nicht durchdringen kann, aber auch nicht an demselben
reflektiert wird. In der unteren Reihe schließlich ist a opt zu groß gewählt,
so dass das Wellenpaket nur zu einem Teil absorbiert wird, während der
verbleibende Teil reflektiert wird.
Ein vorteilhafter Aspekt des nun entwickelten Programms frei3.cpp ist,
dass es durch minimale Modifikationen auch die Entwicklung einer Wellenfunktion in einem beliebigen und sogar zeitabhängigen Potential berechnen
kann. Dazu müssen lediglich die Zeilen 56–62 auf ein reelles Potential angepasst und die Zeilen 102–105 entsprechend geändert werden. Dies werden
wir uns im übernächsten Abschnitt zu Nutze machen, vorher jedoch müssen
wir noch lernen, wie wir zu einem vorgegebenen Potential die zugehörigen
Eigenzustände und deren Energieniveaus berechnen können.
5.9 Eigenzustände des Hamiltonoperators
Die zeitabhängige Schrödingergleichung (5.38) ist besonders leicht zu lösen,
wenn wir als Anfangszustand einen Eigenzustand |ψ(t = 0) = |E des Hamiltonoperators H wählen, d.h. einen Zustand für den
H|E = E|E
(5.56)
gilt. In diesem Fall ist der Zustandsvektor zu einem beliebigen Zeitpunkt t
durch
|ψ(t) = exp(−iEt)|E
(5.57)
gegeben. Die Zeitentwicklung äußert sich also lediglich in einem skalaren Faktor vom Betrag 1. Das bedeutet auch, dass sich der Erwartungswert eines
beliebigen, nicht explizit zeitabhängigen Operators nicht mit der Zeit ändert,
sondern konstant bleibt:
ψ(t)|Ô|ψ(t) = exp(iEt)E|Ô|E exp(iEt) = E|Ô|E .
(5.58)
Sie sehen also, dass den Eigenzuständen des Hamiltonoperators eine besondere Bedeutung zukommt. In diesem Abschnitt wollen wir uns deshalb mit
der numerischen Berechnung dieser Eigenzustände bei beliebig vorgegebenem
äußeren Potential beschäftigen.
Die Grundlage dafür haben wir bereits in (5.47) gelegt, denn wenn wir
die Dreipunktsformel für die zweite Ableitung in Ĥ einsetzen, erhalten wir
einen diskretsieren Hamiltonoperator in Matrixform:
1
+ V (x)
(∆x)2
1 1
= Hn,n+1 = −
.
2 (∆x)2
Hn,n =
Hn,n−1
(5.59)
(5.60)
214
5 Quantenmechanik
Um die Eigenzustände des Hamiltonoperators zu erhalten, müssen wir also
die Eigenvektoren dieser Matrix berechnen, wofür zum Glück in jeder Standardbibliothek fertige Routinen zur Verfügung stehen. Betrachten wir das
Programm eigen1.cpp:
1
2
3
4
5
6
/**************************************************************************
* Name:
eigen1.cpp
*
* Zweck:
Berechnet Eigenzustaende zu vorgegebenem Potential
*
* Gleichung: Schroedingergleichung
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_math.h>
<gsl/gsl_eigen.h>
"tools.h"
14
15
using namespace std;
16
17
18
//-- Zahl der Stuetzstellen
const int nmax = 512;
19
20
//-------------------------------------------------------------------------
21
22
main( int argc, char *argv[] )
23
24
25
26
27
28
29
{
//-- Definition der Variablen
int
n, n1, n2;
double x, dx, x2[nmax], psi[nmax], V[nmax];
ifstream in_stream;
ofstream out_stream;
30
31
32
33
34
35
36
37
38
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<4)
{
cout << " Aufruf: eigen1 infile outfile1 outfile2\n";
cout << "
outfile1 enthaelt das Potential\n";
cout << "
outfile2 enthaelt die Eigenwerte und Eigenzustaende\n";
return 0;
}
39
40
41
42
43
44
45
46
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von eigen1.cpp\n";
inout(dx,"dx");
in_stream.close();
out_stream << "! Spalte 1: n
Spalte 2: x_n
Spalte 3: V(x_n)\n";
47
48
49
50
51
52
53
54
//-- Berechnung des reellen Potentials
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
V[n] = 0.5 * sqr(x);
out_stream << n << " " << x << " " << V[n] << "\n";
}
5.9 Eigenzustände des Hamiltonoperators
55
out_stream.close();
56
57
58
59
60
61
62
63
64
out_stream.open(argv[3]);
out_stream << "! Ergebnis-Datei generiert von eigen1.cpp\n";
out_stream << "! nmax = " << nmax << "\n";
out_stream << "! dx = " << dx << "\n";
out_stream << "! Erster Teil: Spalte 1: n Spalte 2: E_n"
<< " Spalte 3: <xˆ2>_n\n";
out_stream << "! Zweiter Teil: Spalte 1: n Spalte 2: x"
<< "
Spalte 3: psi_n(x)\n";
65
66
67
68
69
70
//-- Speicherplatz allokieren
gsl_vector *eval = gsl_vector_alloc(nmax);
gsl_matrix *evec = gsl_matrix_alloc(nmax, nmax);
gsl_matrix *H
= gsl_matrix_alloc(nmax, nmax);
gsl_eigen_symmv_workspace * w = gsl_eigen_symmv_alloc(nmax);
71
72
73
74
75
//-- Hamiltonoperator konstruieren
for (n1=0; n1<nmax; n1++)
for (n2=0; n2<nmax; n2++)
gsl_matrix_set(H, n1, n2, 0);
76
77
78
79
80
81
82
83
84
85
86
87
88
for (n1=0; n1<nmax; n1++)
gsl_matrix_set(H, n1, n1, 1/sqr(dx) + V[n1]);
for (n1=1; n1<nmax; n1++)
{
gsl_matrix_set(H, n1-1, n1, -0.5/sqr(dx));
gsl_matrix_set(H, n1, n1-1, -0.5/sqr(dx));
}
for (n1=0; n1<nmax-1; n1++)
{
gsl_matrix_set(H, n1+1, n1, -0.5/sqr(dx));
gsl_matrix_set(H, n1, n1+1, -0.5/sqr(dx));
}
89
90
91
92
93
//-- Eigenwerte und Eigenvektoren berechnen und sortieren
gsl_eigen_symmv(H, eval, evec, w);
gsl_eigen_symmv_free(w);
gsl_eigen_symmv_sort (eval, evec, GSL_EIGEN_SORT_VAL_ASC);
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//-- Berechnung von <x*x>
for (n1=0; n1<nmax; n1++)
{
x2[n1] = 0;
for (n2=0; n2<nmax; n2++)
{
x = (n2-nmax/2) * dx;
x2[n1] = x2[n1] + sqr(gsl_matrix_get(evec,n2,n1)*x);
}
}
//-- Ausgabe der Eigenwerte und Eigenvektoren
for (n1=0; n1<nmax; n1++)
out_stream << n1 << " " << gsl_vector_get(eval, n1) << " "
<< x2[n1] << "\n";
for (n1=0; n1<nmax; n1++)
for (n2=0; n2<nmax; n2++)
{
x = (n2-nmax/2) * dx;
out_stream << n1 << " " << x << " "
215
216
5 Quantenmechanik
<< gsl_matrix_get(evec, n2, n1) << "\n";
114
115
}
116
117
118
119
gsl_vector_free(eval);
gsl_matrix_free(evec);
gsl_matrix_free(H);
120
121
122
out_stream.close();
}
Nach dem Einlesen der Parameter wird das Potential V (x) an den Stützstellen berechnet (Zeile 48–54) – das ist die einzige Stelle, die wir modifizieren
müssen, wenn wir die Eigenzustände zu anderen Potentialtypen berechnen
wollen. Danach wird Speicherplatz für die Matrix des Hamiltonoperators, für
deren Eigenwerte und Eigenvektoren, sowie für die Berechnung dieser Eigenvektoren alloziert. Dann erfolgt die Aufstellung des Hamiltonoperators nach
(5.59), die Berechnung der Eigenwerte und -vektoren, sowie die Ausgabe der
Ergebnisse.
Zur Überprüfung des Programms eignen sich besonders Potentiale, bei
denen die Eigenzustände analytisch berechnet werden können, z.B. das Potential des harmonischen Oszillators:
V (x) =
1
mω 2 x2 .
2
(5.61)
Dessen Eigenzustände finden sich in jedem Lehrbuch zur Quantenmechanik
und lauten:
1 2 2
ψn (x) = Nn exp − k x Hn (k 2 x)
(5.62)
2
mit
k2 =
Nn =
mω
h̄
(5.63)
k2
√
2n n!
π
.
(5.64)
Die dabei auftretenden Hermiteschen Polynome Hn geben wir für die niedrigsten Ordnungen n explizit an:
H0 (ξ) = 1
H1 (ξ) = 2ξ
(5.65)
(5.66)
H2 (ξ) = 4ξ − 2 .
2
Die zu diesen Eigenzuständen gehörenden Energieniveaus sind:
1
.
En = h̄ω n +
2
(5.67)
(5.68)
5.9 Eigenzustände des Hamiltonoperators
217
Tabelle 5.1. Numerisch bestimmte Energieeigenwerte des harmonischen Oszillators
n
E
n
E
0
0.49999
7
7.49985
1
1.49994
8
8.50359
2
2.49984
9
9.51658
3
3.49969
10
10.5515
4
4.49949
11
11.6277
5
5.49927
12
12.7666
6
6.49918
13
13.9861
Dieses analytische Ergebnis können wir nun mit dem numerischen Resultat
von eigen1.cpp vergleichen. Für nmax=512 und dx = 0.02 erhalten wir die
Eigenwerte:
Die Energieniveaus werden also für diese Parameter bis etwa n = 10
ganz gut wiedergegeben und weichen dann immer mehr von ihrem Sollwert
n+1/2 ab. Was bei diesem Wert geschieht sehen wir, wenn wir die zugehörigen
Eigenfunktionen betrachten (Abb. 5.3):
Wir sehen, dass die Wellenfunktion ψ für kleine n gut beim Ursprung
lokalisiert ist, ihre Ausdehnung aber mit wachsendem n zunimmt. Für die-
1.0
(a)
(b)
1 0.5
0
0.0
0.5
-0.5
0.0
-4
-2
0
2
4
-4
-2
0
2
x
(c)
(d)
2 0.5
10 0.5
0.0
0.0
-0.5
-0.5
-4
4
x
-2
0
2
4
x
-4
-2
0
2
4
x
Abb. 5.3. Eigenzustände des harmonischen Oszillators: Vergleich des numerisch
gewonnenen Ergebnisses (durchgezogene Linie) mit dem analytischen Ergebnis (gestrichelt) für n = 0, n = 1, n = 2 und n = 10. Nur bei n = 10 kann ein kleiner
Unterschied zwischen diesen beiden Kurven ausgemacht werden
218
5 Quantenmechanik
se n ist in Abb. 5.3 das numerische vom analytischen Ergebnis nicht mehr
zu trennen. Bei etwa n = 10 erreicht die Wellenfunktion den Rand des abgedeckten Ortsbereichs, so dass verständlich ist, dass für höhere n unsere
numerische Berechnung nicht mehr richtig sein kann. Trotzdem ist die Übereinstimmung der gewonnenen Eigenfunktion (in Abb. 5.3 rechts unten) mit
dem analyitschen Resultat (5.62) erstaunlich.
Nachdem wir nun verifiziert haben, dass das Programm korrekt funktioniert, ist es verhältnismäßig einfach, das Potential beliebig zu variieren und
jeweils Energieniveaus und die zugehörigen Eigenfunktionen zu berechnen.
Da wir für den übernächsten Abschnitt die Eigenzustände zum Potential
(5.69)
V (x) = −a exp −b(x − x0 )2
benötigen, werden wir die Eigenzustände dieses Potentials kurz diskutieren
– darüber hinaus kann dieses Potential auch gut als eindimensionales Modell
eines Atoms herangezogen werden.
Das Programm zur Berechnung der Eigenzustände finden Sie auf der CD
als eigen2.cpp. Wie schon oben erwähnt müssen nur die Zeilen geändert
werden, in denen das Potential V berechnet wird:
...
55
56
57
58
59
60
//-- Berechnung des reellen Potentials
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
V[n] = -a*exp(-b*sqr(x-x0));
}
...
Wenn wir die Eigenzustände und zugehörigen Eigenwerte zu diesem Potential für
a = 1.5
b = 0.5
x0 = 0
(5.70)
berechnen, stellen wir zunächst fest, dass das Spektrum der Eigenwerte aus
zwei negativen und sehr vielen positiven Werten besteht. Die negativen Energieeigenwerte gehören zu gebundenen Zuständen – d.h. die Energie des Elektrons reicht nicht aus, den Atomkern zu verlassen – während die positiven
Energien zu ungebundenen Zuständen gehören. Diese Aussage können wir
auf zwei Weisen untermauern: Zum einen können wir die Eigenzustände direkt anschauen und werden sehen, dass tatsächlich die Eigenzustände zu den
negativen Energien am Potential lokalisiert sind, was bei den übrigen Eigenzuständen nicht zutrifft. Da wir dies aber nicht für alle Eigenzustände (in
unserem Beispiel immerhin 512 Stück) durchführen können, können wir alternativ für jeden Eigenzustand den Mittelwert x2 berechnen und ausgeben
lassen. Die beiden gebundenen Zustände haben einen kleinen Wert von x2 ,
der sich zudem bei einer Vergrößerung des betrachteten Ortsbereichs nur wenig ändert, während diese Größe bei den freien Zuständen sehr groß ist und
mit anwachsendem Ortsbereich divergiert.
5.10 Variationsmethoden
219
5.10 Variationsmethoden
Alternativ zur vorangegangenen Bestimmung der Eigenwerte über die Aufstellung des Hamiltonoperators im Ortsraum kann jede beliebige Darstellung
des Hilbertraumes verwendet werden. Dabei ist es auch nicht notwendig erforderlich, dass der Basissatz |φi , nach dem man einen beliebigen Zustand
entwickelt, aus lauter zueinander orthogonalen Funktionen besteht – wichtig
für analytische Berechnungen ist lediglich die Vollständigkeit des Funktionensatzes, d.h. jeder beliebige Zustand |ψ muss sich als Linearkombination
ψi |φi (5.71)
|ψ =
i
darstellen lassen. Bei den meisten Problemen ist diese Forderung nur durch
einen unendlich großen Satz an Entwicklungsfunktionen erfüllbar. Für numerische Berechnungen ist es dann leider notwendig, sich auf einen endlichen
Teilsatz zu beschränken. Diese Notwendigkeit und der damit verbundene Fehler lässt die Auswahl geeigneter Entwicklungsfunktionen unter einem ganz
neuen Licht erscheinen: eine günstige Wahl kann eine sehr genaue Berechnung der Energieniveaus und Eigenzustände mit einem sehr kleinen Satz an
Entwicklungsfunktionen erlauben. In diesem Zusammenhang ist natürlich jede Kenntnis über die erwarteten Eigenfunktionen (z.B. deren Verhalten in
der Nähe kritischer Punkte oder für große x bzw. p) hilfreich.
Beginnen wir mit einer Betrachtung des Grundzustandes |ψ0 , von dem
wir voraussetzen wollen, dass er nicht entartet ist. Unter allen normierten
Zuständen ist er derjenige mit dem niedrigsten Energieerwartungswert
ψ0 |H|ψ0 ,
(5.72)
da alle anderen Zustände Anteile mit höherer Energie haben – dies ist übrigens auch der Grundgedanke des Ritzschen Variationsprinzips. Wir können
den Grundzustand also als eine Lösung des Variationsproblems
δ (ψ0 |H|ψ0 ) = 0
(5.73)
erhalten, wobei die Variation allerdings auf normierte Zustände beschränkt
ist. Diese Beschränkung können wir fallenlassen, wenn wir statt des Ausdruckes (5.72) die leicht modifizierte Größe
E=
ψ0 |H|ψ0 ψ0 |ψ0 (5.74)
variieren. Wir können den Grundzustand also anstatt als Lösung der zeitunabhängigen Schrödingergleichung auch als Lösung des Variationsproblems
δE =
1
2
(ψ|ψ)
(ψ|ψδψ|H|ψ − ψ|H|ψδψ|ψ
+ψ|ψψ|H|δψ − ψ|H|ψψ|δψ)
=0
(5.75)
(5.76)
220
5 Quantenmechanik
erhalten. Auch die anderen Energieniveaus erhält man als Lösung dieses Variationsproblems.
Beschränkt man sich nun auf einen endlichen Satz an Funktionen, nach denen man die gesuchten Zustände entwickelt, erstreckt sich die Suche nach dem
Zustand minimaler Energie nur auf einen Teilraum des tatsächlichen Hilbertraums. Aus diesem Grund ist die gefundene Grundzustandsenergie entweder
zu hoch oder korrekt, kann aber niemals zu niedrig sein. Die Variationsaufgabe (5.75) kann auf die Form einer verallgemeinerten Eigenwertgleichung
HA = EDA
(5.77)
gebracht werden. Dabei enthält die Matrix H die Elemente
Hnm = φn |E|φm (5.78)
und die Überlappmatrix D setzt sich aus
Dnm = φn |φm (5.79)
zusammen.
Als konkretes Beispiel zur Berechnung von Eigenzuständen und Energieniveaus mittels (5.75) betrachten wir das Modellatom mit dem Potential
V (x) = − √
β
.
1 + αx2
(5.80)
Die Symmetrie des Potentials gewährleistet, dass alle Eigenfunktionen entweder symmetrisch oder antisymmetrisch zu x = 0 sind. Aus diesem Grund
ist es sinnvoll, zwei Sätze von Testfunktionen aufzustellen, wobei der eine nur
symmetrische und der andere nur antisymmetrische Entwicklungsfunktionen
enthält. Der Übersichtlichkeit halber beschränken wir uns hier auf symmetrischen Entwicklungsfunktionen. Bei der Auswahl des Funktionensatzes fn ist
es von Vorteil, wenn möglichst viele der für Dnm und Hnm benötigten Erwartungswerte analytisch berechnet werden können. In unserem Fall wählen
wir
1
(5.81)
φn = √ x2n exp −µx2 /2 .
n!
√
Der Faktor 1/ n! soll bewirken, dass die Matrixelemente ähnliche Größenordnung haben – andernfalls handelt man sich an dieser Stelle vermeidbare
numerische Fehler ein. Der Übersichtlichkeit spalten wir die Matrix H in den
kinetischen Anteil T und den vom Potential herrührenden Teil V auf.
Zunächst benötigen wir die Überlappintegrale
Dnm = φn |φm ∞
1
dx x2N exp −µx2 ,
= √
n!m!
−∞
(5.82)
(5.83)
5.10 Variationsmethoden
221
die Matrixelemente
Tnm = φn |T̂ |φm 1
=− √
2 n!m!
(5.84)
∞
dx 2N (2N − 1)x2N −2 − (4N + 1)µx2N
−∞
2 2N +2
+α x
exp −µx2 ,
(5.85)
sowie
Vnm = φn |V̂ |φm ∞
x2N
= −β
dx √
exp −µx2 .
2
1 + αx
(5.86)
(5.87)
−∞
Hierbei haben wir die Abkürzung N = n + m eingeführt. Bis auf den letzten
Integraltyp (5.87) können wir alle anderen Integrale auf Ausdrücke der Form
∞
In =
dx x2n exp −µx2
−∞
1 · 3 · . . . · (2n − 1)
=
µn
π
µ
(5.88)
(5.89)
zurückführen:
IN
(5.90)
n!m!
1
2N (2N − 1)IN −1 − (4N + 1)µIN + µ2 IN +1 .(5.91)
= −√
2n!m!
Dnm = √
Tnm
Entsprechend definieren wir
∞
dx √
Jn =
−∞
x2n
exp −µx2 ,
1 + αx2
(5.92)
welches wir jedoch numerisch bestimmen müssen. Aus dieser Hilfsgröße erhält
man die fehlenden Matrixelemente für die potentielle Energie
Vnm = −βJN .
(5.93)
Wenn wir nun zur Umsetzung des Gesagten in einem Programm kommen,
stoßen wir auf ein weiteres Hindernis: die Numerik-Bibliothek GSL verfügt
über keine Routine, die die verallgemeinerte Eigenwertgleichung (5.77) löst.
Wir könnten nun eine weitere Bibliothek zu Hilfe nehmen (z.B. CLAPACK),
222
5 Quantenmechanik
was jedoch wegen der verschiedenen Realisierung von Matrizen in GSL und
CLAPACK etwas unschön ist. Alternativ können die Eigenvektoren der – offensichtlich symmetrischen – Matrix D dazu verwendet werden, (5.77) auf die
Form einer gewöhnlichen Eigenwertgleichung zu bringen. Hierzu multiplizieren wir (5.77) von rechts mit der Matrix T, deren Spalten die Eigenvektoren
von D sind, und von links mit der adjungierten Matrix Tt . Verwendet man
√
hierbei statt auf eins normierter Eigenvektoren, solche, die auf 1/ an (an ist
der jeweilige Eigenwert) normiert sind, so erhält man:
H̃ = Tt HT.
(5.94)
Die Eigenwerte dieser Matrix H̃ sind identisch mit den Lösungen der verallgemeinerten Eigenwertgleichung (5.77).
1
2
3
4
5
6
/**************************************************************************
* Name:
var1.cpp
*
* Zweck:
Berechnet Eigenzustaende zu vorgegebenem Potential
*
* Methode : Variationsmethode
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
14
15
16
17
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_math.h>
<gsl/gsl_vector.h>
<gsl/gsl_matrix.h>
<gsl/gsl_linalg.h>
<gsl/gsl_eigen.h>
<gsl/gsl_integration.h>
"tools.h"
18
19
using namespace std;
20
21
22
23
//-- globale Variablen
int
n;
double alpha, beta, mu;
24
25
//-------------------------------------------------------------------------
26
27
double integr1(double x, void *params)
28
29
30
31
{
return 2. * pow(x,n) / sqrt(1.+alpha*sqr(x)) * exp(-mu*sqr(x));
}
32
33
//-------------------------------------------------------------------------
34
35
main( int argc, char *argv[] )
36
37
38
39
40
41
42
{
//-- Definition der Variablen
int
n1, n2, n3, nmax, limit=10000, N, m, signum;
double x, dx, a1, b1, x1, res, abserr, sum, sum1, sum2, sum3;
double nu = 10;
double epsabs = 0;
5.10 Variationsmethoden
43
44
45
46
47
223
double epsrel = 1.e-6;
ifstream in_stream;
ofstream out_stream;
gsl_function F;
gsl_integration_workspace *w1;
48
49
50
51
52
53
54
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: var1 infile outfile\n";
return 0;
}
55
56
57
58
59
60
61
62
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von var1.cpp\n";
inout(alpha,"alpha");
inout(beta,"beta");
inout(nmax,"nmax");
inout(mu,"mu");
in_stream.close();
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//-- Speicherplatz allokieren
gsl_vector *eval = gsl_vector_alloc(nmax);
gsl_vector *eval_D = gsl_vector_alloc(nmax);
//gsl_vector *evec1 = gsl_vector_alloc(nmax);
gsl_vector *I
= gsl_vector_alloc(2*nmax+2);
gsl_vector *J
= gsl_vector_alloc(2*nmax+2);
gsl_vector *fac = gsl_vector_alloc(2*nmax+2);
gsl_matrix *evec = gsl_matrix_alloc(nmax, nmax);
gsl_matrix *evec_D = gsl_matrix_alloc(nmax, nmax);
gsl_matrix *D
= gsl_matrix_alloc(nmax, nmax);
gsl_matrix *D_copy = gsl_matrix_alloc(nmax, nmax);
gsl_matrix *H
= gsl_matrix_alloc(nmax, nmax);
gsl_matrix *M
= gsl_matrix_alloc(nmax, nmax);
gsl_permutation *p = gsl_permutation_alloc(nmax);
78
79
80
81
82
83
84
85
86
87
//-- Hilfsintegrale I und J berechnen
gsl_vector_set_zero(I);
gsl_vector_set_zero(J);
gsl_vector_set(I,0,sqrt(pi/mu));
gsl_vector_set(fac,0,1);
for (n1=1; n1<2*nmax+2; n1++)
{
gsl_vector_set(I,n1,gsl_vector_get(I,n1-1)*(2.*n1-1)/2./mu);
gsl_vector_set(fac,n1,n1*gsl_vector_get(I,n1-1));
}
88
89
90
91
92
93
94
95
96
97
98
for (n1=0; n1<2*nmax+2; n1++)
{
n = 2*n1;
F.function = &integr1;
F.params
= ν
w1 = gsl_integration_workspace_alloc(10000);
gsl_integration_qagiu(&F, 0, epsabs, epsrel, limit, w1, &res, &abserr);
gsl_vector_set(J,n1,res);
gsl_integration_workspace_free(w1);
}
99
100
101
//-- Ueberlappmatrix konstruieren
gsl_eigen_symmv_workspace *w2 = gsl_eigen_symmv_alloc(nmax);
224
102
103
104
105
106
107
108
109
5 Quantenmechanik
for (n1=0; n1<nmax; n1++)
for (n2=0; n2<nmax; n2++)
gsl_matrix_set(D, n1, n2, gsl_vector_get(I,n1+n2)
/ gsl_vector_get(fac,n1) / gsl_vector_get(fac,n2));
gsl_matrix_memcpy(D_copy,D);
gsl_eigen_symmv(D_copy, eval_D, evec_D, w2);
gsl_eigen_symmv_free(w2);
gsl_matrix_free(D_copy);
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//-- Energiematrix konstruieren
for (n1=0; n1<nmax; n1++)
for (n2=0; n2<nmax; n2++)
{
N = n1+n2;
res = -beta*gsl_vector_get(J,N)
+ 0.5*mu*(4.*n2+1.)*gsl_vector_get(I,N)
- 0.5*sqr(mu)*gsl_vector_get(I,N+1);
if (n2>=1) res = res - n2*(2.*n2-1.)*gsl_vector_get(I,N-1);
res = res / gsl_vector_get(fac,n1) / gsl_vector_get(fac,n2);
gsl_matrix_set(H, n1, n2, res);
}
gsl_vector_free(I);
gsl_vector_free(J); gsl_vector_free(fac);
124
125
126
127
128
129
// H durch Tˆ{-1} H T ersetzen
for (n1=0; n1<nmax; n1++)
for (n2=0; n2<nmax; n2++)
gsl_matrix_set(evec_D,n1,n2,gsl_matrix_get(evec_D,n1,n2)
/ sqrt(gsl_vector_get(eval_D,n2)));
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
for (n1=0; n1<nmax; n1++)
for (n2=0; n2<nmax; n2++)
{
sum = 0;
for (n3=0; n3<nmax; n3++)
sum = sum + gsl_matrix_get(H,n2,n3) * gsl_matrix_get(evec_D,n3,n1);
gsl_matrix_set(M,n1,n2,sum);
}
for (n1=0; n1<nmax; n1++)
for (n2=0; n2<nmax; n2++)
{
sum = 0;
for (n3=0; n3<nmax; n3++)
sum = sum + gsl_matrix_get(M,n2,n3) * gsl_matrix_get(evec_D,n3,n1);
gsl_matrix_set(H,n1,n2,sum);
}
147
148
149
gsl_matrix_free(D);
gsl_vector_free(eval_D);
gsl_matrix_free(evec_D);
gsl_matrix_free(M);
150
151
152
153
154
155
//-- Eigenwerte und Eigenvektoren berechnen und sortieren
gsl_eigen_symmv_workspace *w = gsl_eigen_symmv_alloc(nmax);
gsl_eigen_symmv(H, eval, evec, w);
gsl_eigen_symmv_free(w);
gsl_eigen_symmv_sort (eval, evec, GSL_EIGEN_SORT_VAL_ASC);
156
157
158
159
160
//-- Ausgabe der Eigenwerte und Eigenvektoren
for (n1=0; n1<nmax; n1++)
out_stream << n1 << " " << gsl_vector_get(eval, n1) << "\n";
5.10 Variationsmethoden
161
162
gsl_matrix_free(H);
gsl_vector_free(eval);
225
gsl_matrix_free(evec);
163
164
165
out_stream.close();
}
Im Programm erkennen wir zunächst die Definition des Integranden aus
(5.92) in Zeile 27–31. Im Hauptprogramm wird dieses Integral in den Zeilen
89–98 für verschiedene Werte von n berechnet. Für die Bestimmung der In
wird zunächst in Zeile 81 I0 berechnet und dann in den Zeilen 83–87 auf die
Rekursionsformel
2n − 1
In =
In−1
(5.95)
2µ
zurückgegriffen. Anschließend konstruieren wir aus diesen Hilfsgrößen die
Überlappmatrix D und die Energiematrix H. Nun müssen wir die Transformation (5.94) durchführen, wofür wir zunächst die Eigenwerte und Eigenvektoren von D benötigen (Zeile 106–109). Beachten Sie bitte, dass bei der
Eigenwert- und Eigenvektorberechnung die jeweilige Matrix überschrieben
wird, so dass Sie vorher eine Kopie erzeugen müssen, falls Sie die ursprüngliche Matrix noch benötigen. Die Transformation auf H̃ (das in unserem Programm einfach die ursprüngliche Matrix H ersetzt) geschieht dann in den
Zeilen 125–146. Nach all dieser Vorarbeit ist die Berechnung der gesuchten
Energieeigenwerte (Zeile 151–155) verhältnismäßig einfach.
Die Effizienz unseres Programms überprüfen wir für α = 1 und β = 2,
indem wir die Eigenenergien E0 und E2 des Grundzustands und des zweiten
angeregten Zustands mit verschieden großen Sätzen an Eigenfunktionen (jeweils mit µ = 1) bestimmen (beachten Sie, dass der erste angeregte Zustand
antisymmetrisch sein muss und deswegen hier nicht auftaucht):
Tabelle 5.2. Energieeigenwerte eines Modellatoms, bestimmt mit der Variationsmethode
n
E0
E2
2
-1.476462496
0.0388704779
3
-1.482897851
-0.1979509319
4
-1.483099040
-0.3236953490
6
-1.483407203
-0.4120002016
8
-1.483432501
-0.4424870560
10
-1.483435439
-0.4548390169
12
-1.483435876
-0.4603066238
16
-1.483435970
-0.4640635221
226
5 Quantenmechanik
Sie erkennen, dass die gewonnenen Energien mit wachsendem n Grenzwerten zustreben. Wenn unser Funktionensatz nicht so gewählt wurde, dass
selbst im Grenzfall n → ∞ noch relevante Bereiche des Hilbertraums nicht
abgedeckt werden, sind diese Grenzwerte gleich den gesuchten Eigenenergien Für die Grundzustandsenegie ist der gefundene Wert auch für verhältnismäßig kleine n außerordentlich genau. Vergleichen Sie die Größe des hierbei verwendeten Funktionensatzes mit der Zahl an Ortspunkten, die wir im
vorangegangen Abschnitt benötigt haben (oder noch besser mit der entsprechenden Zahl bei Übungsaufgabe 5.2)! Bei dem Energieeigenwert für den
zweiten angeregten Zustand stellen wir fest, dass wir hierfür mehr Testfunktionen benötigen – ein Sachverhalt, der ganz analog auch in Tabelle 5.1 zutage
trat.
5.11 Quantentunneln
Das Problem, das wir in diesem Abschnitt untersuchen wollen, ist eines jener Phänomene, die so erstaunlich sind, dass sie immer wieder diskutiert und
auch experimentell untersucht werden, da sich an ihnen sowohl die Richtigkeit
als auch die Unanschaulichkeit der Quantenmechanik eindrucksvoll dokumentieren lässt. Beim sogenannten Quantentunneln ist ein Teilchen zunächst in
einem Bereich, den es klassisch nicht verlassen kann, weil seine Energie nicht
ausreicht, die es umgebenden Potentialbarrieren zu überwinden. Die Quantenmechanik erlaubt es dem Teilchen aber trotzdem zu entkommen – als ob
es einen Tunnel durch die Barriere finden würde, daher der Name des Effekts.
Um dieses Phänomen zu diskutieren, konstruieren wir uns zunächst ein
Potential, das über zwei Minima verfügt, z.B.
V (x) = −a1 exp(−b1 (x + x0 )2 ) − a2 exp(−b2 (x − x0 )2 ) .
(5.96)
Dieses Potential verfügt über zwei Minima in der Nähe von ±x0 , deren Tiefe
und Breite wir mit a1,2 und b1,2 festlegen können. Im folgenden interessieren
wir uns für zwei nur geringfügig verschiedene Parametersätze zur Festlegung
des Potentials:
a1 = a2 = 1.5
b1 = b2 = 4
x0 = 2
(5.97)
x0 = 2 .
(5.98)
bzw.
a1 = 1.5
a2 = 1.6
b 1 = b2 = 4
Während im ersten Fall die beiden Potentialmulden identisch sind, ist die
rechte Potentialmulde im zweiten Fall ein bisschen tiefer (siehe Abb. 5.4).
Das Programm qt1.cpp setzt nun den im vorigen Abschnitt 5.9 berechneten Grundzustand für eine einzelne Potentialmulde in das linke Minmum
des Potential ein und löst die zeitabhängige Schrödingergleichung – beachten
Sie, dass der Ausgangszustand durch die Hinzunahme der rechten Potentialmulde kein Eigenzustand mehr ist, so dass die Dynamik nicht einfach durch
(5.57) gegeben ist:
5.11 Quantentunneln
227
Abb. 5.4. Doppeltopfpotential mit zwei Minima.
1
2
3
4
5
6
/**************************************************************************
* Name:
qt1.cpp
*
* Zweck:
Simuliert quantenmechanisches Tunneln
*
* Gleichung: Schroedingergleichung mit optischem und reellem Potential
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
7
8
9
10
11
12
13
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<math.h>
<gsl/gsl_errno.h>
<gsl/gsl_fft_complex.h>
"tools.h"
14
15
using namespace std;
16
17
18
//-- Zahl der Stuetzstellen
const int nmax = 8192;
19
20
21
22
23
24
25
//-- globale Variablen
double psi[2*nmax];
double V[nmax], V_opt[nmax];
double t, dt, dp;
gsl_fft_complex_wavetable * wavetable;
gsl_fft_complex_workspace * workspace;
26
27
//-------------------------------------------------------------------------
28
29
int timestep(double dt)
30
31
32
33
{
int
n, n1;
double psi_p[2*nmax], arg, p, a, b;
34
35
36
37
//-- Zustand im Impulsraum
for (n=0; n<=2*nmax; n++) psi_p[n] = psi[n];
gsl_fft_complex_radix2_forward (psi_p, 1, nmax);
38
39
40
41
42
43
44
45
46
//-- Zeitentwicklung (freie Dynamik)
for (n1=0; n1<nmax/2; n1++)
{
p = n1 * dp;
arg = sqr(p)/2 * dt;
psi[2*n1]
= psi_p[2*n1] * cos(arg) - psi_p[2*n1+1] * sin(arg);
psi[2*n1+1] = psi_p[2*n1] * sin(arg) + psi_p[2*n1+1] * cos(arg);
}
228
47
48
49
50
51
52
53
5 Quantenmechanik
for (n1=nmax/2; n1<nmax; n1++)
{
p = (n1-nmax) * dp;
arg = sqr(p)/2 * dt;
psi[2*n1]
= psi_p[2*n1] * cos(arg) - psi_p[2*n1+1] * sin(arg);
psi[2*n1+1] = psi_p[2*n1] * sin(arg) + psi_p[2*n1+1] * cos(arg);
}
54
55
56
//-- Ruecktransformation in den Ortsraum
gsl_fft_complex_radix2_inverse (psi, 1, nmax);
57
58
59
60
61
62
63
64
//-- Zeitentwicklung (optisches Potential)
for (n1=0; n1<nmax; n1++)
{
arg = - V_opt[n1] * dt;
psi[2*n1]
= psi[2*n1] * exp(arg);
psi[2*n1+1] = psi[2*n1+1] * exp(arg);
}
65
66
67
68
69
70
71
72
73
74
75
//-- Zeitentwicklung (reelles Potential)
for (n1=0; n1<nmax; n1++)
{
arg = V[n1] * dt;
a = psi[2*n1] * cos(arg) - psi[2*n1+1] * sin(arg);
b = psi[2*n1] * sin(arg) + psi[2*n1+1] * cos(arg);
psi[2*n1]
= a;
psi[2*n1+1] = b;
}
t = t+dt;
76
77
78
return 0;
}
79
80
//-------------------------------------------------------------------------
81
82
main( int argc, char *argv[] )
83
84
85
86
87
88
89
90
91
{
//-- Definition der Variablen
int
n, n1, n2, n_start, n_out, n_interm, max_fct, status, n_offset;
double tend, x, dx, p, a, a1, b1, x1, a2, b2, x2, E, x_min, alpha;
double arg, sum, a_opt, b_opt, prob_links, prob_rechts, rtol, atol;
double mu = 10;
ifstream in_stream;
ofstream out_stream;
92
93
94
95
96
97
98
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: qt1 infile outfile\n";
return 0;
}
99
100
101
102
103
104
105
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von qt1.cpp\n";
out_stream << "! nmax = " << nmax << "\n";
inout(tend,"tend");
inout(dx,"dx");
5.11 Quantentunneln
106
107
108
109
110
111
112
inout(n_out,"n_out");
inout(rtol,"rtol");
inout(a_opt,"a_opt");
inout(a1,"a1");
inout(x1,"x1");
inout(b2,"b2");
in_stream.close();
inout(n_interm,"n_interm");
inout(atol,"atol");
inout(b_opt,"b_opt");
inout(b1,"b1");
inout(a2,"a2");
inout(x2,"x2");
113
114
115
116
//-- Berechnung einiger benoetigter Parameter -dt = tend / n_out / n_interm;
dp = 2 * pi / dx / nmax;
117
118
119
120
121
122
123
124
125
126
//-- Berechnung des optischen Potentials
for (n=0; n<nmax; n++)
{
if (x < nmax/2)
x = n * dx;
else
x = (nmax-n) * dx;
V_opt[n] = a_opt*exp( -sqr(b_opt*x) );
}
127
128
129
130
131
132
133
//-- Berechnung des reellen Potentials
for (n=0; n<nmax; n++)
{
x = (n-nmax/2) * dx;
V[n] = -a1*exp(-b1*sqr(x-x1)) -a2*exp(-b2*sqr(x-x2));
}
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//-- Anfangsbedingungen
for (n=0; n<nmax; n++) psi[2*n] = 0;
sum = 0;
in_stream.open("eigen1.res");
n_offset = nmax/2 - 512/2 + x2 / dx;
cout << " n_offset : " << n_offset << "\n";
for (n=0; n<512; n++) {in_stream >> n1; in_stream >> E;}
for (n=0; n<512; n++)
{
in_stream >> n1;
in_stream >> n2;
in_stream >> a;
psi[2*(n+n_offset)] = a;
sum = sum + sqr(psi[2*(n+n_offset)]);
}
in_stream.close();
sum = 1 / sqrt(sum);
for (n=0; n<nmax; n++)
{
psi[2*n]
= psi[2*n] * sum;
// Realteil
psi[2*n+1] = 0;
// Imaginaerteil
}
prob_links = 0; prob_rechts = 0;
for (n1=0; n1<nmax; n1++) prob_links = prob_links + sqr(psi[n1]);
for (n1=nmax; n1<2*nmax; n1++)
prob_rechts = prob_rechts + sqr(psi[n1]);
cout << "0 0 " << " " << prob_links << " " << prob_rechts << "\n";
162
163
164
t = 0;
out_stream << n*dt << "\n";
229
230
165
166
5 Quantenmechanik
for (n1=0; n1<nmax; n1++)
out_stream << psi[2*n1] << " " << psi[2*n1+1] << "\n";
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//-- Zeitschleife -t = 0;
for (n=1; n<=n_out; n++)
{
for (n1=0; n1<n_interm; n1++) timestep(dt);
//-- Wahrscheinlichkeit, dass das Teilchen in der linken/rechten
//
Haelfte ist
prob_links = 0; prob_rechts = 0;
for (n1=0; n1<nmax; n1++) prob_links = prob_links + sqr(psi[n1]);
for (n1=nmax; n1<2*nmax; n1++) prob_rechts = prob_rechts + sqr(psi[n1]);
cout << n << " " << t << " " << prob_links << " " << prob_rechts << "\n";
out_stream << n*dt << "\n";
for (n1=0; n1<nmax; n1++)
out_stream << psi[2*n1] << " " << psi[2*n1+1] << "\n";
}
183
184
185
out_stream.close();
}
Als Ergebnis liefert das Programm zum einen die Zustände zu den Zeitpunkten 0, dt, 2dt, . . . N dt und zum anderen die Wahrscheinlichkeit, das
Teilchen zu diesen Zeitpunkten in der linken oder in der rechten Hälfte des
Ortsbereiches zu finden. Diese beiden Wahrscheinlichkeiten – im Programmtext prob links und prob rechts genannt – werden auf dem Bildschirm
ausgegeben, können jedoch auch in eine gesonderte Datei umgelenkt werden.
Wie wir feststellen, ist dieser Zeitverlauf, der das Wechseln des Teilchens
vom einen in das andere Potentialminimum wiedergibt, für die beiden sehr
ähnlichen Potentialverläufe sehr unterschiedlich. Beginnen wir mit dem symmetrischen Potentialverlauf, bei dem a1 = a2 = 1.5 ist (Abb. 5.5):
Abb. 5.5. Aufenthaltswahrscheinlichkeit des Teilchens in der linken (durchgezogene Linie) bzw. rechten Potentialmulde (gestrichelt dargestell) beim symmetrischen
Doppelmuldenpotential
Das Teilchen pendelt also zwischen den beiden Potentialminima hin und
her. Dieses Verhalten steht in krassem Gegensatz zu dem, was wir vom thermischen Tunneln her kennen, bei dem die Aufenthaltswahrscheinlichkeit sich
monoton dem thermischen Gleichgewicht (für zwei gleich tiefe Minima also
der Gleichverteilung) nähert.
5.11 Quantentunneln
231
Die bisher besprochene Situation von zwei gleich tiefen Potentialmulden,
die gut voneinander getrennt sind, lässt sich in guter Näherung auch analytisch beschreiben. Dazu nehmen wir zunächst an, wir würden die Eigenfunk(0)
tionen ψi (x) einer einzelnen Potentialmulde V0 (x) kennen. Nun suchen wir
die Eigenfunktionen des Doppelmuldenpotentials
V (x) = V0 (x − x0 ) + V0 (x + x0 ) .
(5.99)
Da dieses Potential symmetrisch ist, müssen alle Eigenfunktionen entweder
symmetrisch (ψ(x) = ψ(−x)) oder antisymmetrisch (ψ(x) = −ψ(−x)) sein.
In niedrigster Ordnung Störungstheorie mit Entartung müssen wir diese Eigenfunktionen aus den Eigenfunktionen gleicher Energie zum linken bzw.
rechten Potential zusammensetzen (siehe z.B. [29] oder [30]):
ψ0,1 (x) = αi (ψ0 (x − x0 ) ± ψ0 (x + x0 )) .
(5.100)
Der Normierungsfaktor α bestimmt sich für gut separierte Potentialmulden
durch
N = dx ψ0,1
(x)ψ0,1 (x)
(5.101)
= αi2 2 ± 2Re
dx ψ0 (x + x0 )ψ(x − x0 )
(5.102)
≈ 2αi2 ,
woraus folgt, dass
(5.103)
√
αi = 1/ 2
(5.104)
ist.
Näherungen für die zugehörigen Energieniveaus erhalten wir durch Berechnung der entsprechenden Energieerwartungswerte:
E0,1 = αi2 dx ψ0,1
(x)
d2
(5.105)
× − 2 + V0 (x − x0 ) + V (x − x0 ) ψ0,1 (x)
dx
1
(0)
dx ψ0 ψ0
.
(5.106)
=
2E0 ± 2Re
2
Wenn wir nun den entsprechenden Zeitverlauf bei dem links etwas vertieften Potential (a1 = 1.6, a2 = 1.5) gegenüberstellen (Abb. 5.6), so stellen wir
fest, dass der Zeitabhnagigkeit beider Aufenthaltswahrscheinlichkeiten zwar
wieder sinusoidal ist, die Wahrscheinlichkeit, das Teilchen rechts anzutreffen,
allerdings nie größer als etwa 52% ist.
Abschließend möchten wir in diesem Abschnitt noch die Situation ansprechen, dass das Teilchen in einen ungebundenen Zustand tunneln kann.
232
5 Quantenmechanik
Abb. 5.6. Aufenthaltswahrscheinlichkeit des Teilchens in der linken (durchgezogene Linie) bzw. rechten Potentialmulde (gestrichelt dargestellt) beim leicht asymmetrischen Doppelmuldenpotential
Dies erreichen wir, indem wir das Potential so wählen, dass zu gebundenen
Zuständen auf der rechten Seite des Potentialwalls freie Zustände vergleichbarer Energie auf der linken Seite existieren. Dies ist z.B. der Fall, wenn wir
das Potentialminimum nach links beliebig verlängern:
f ür x ≤ x0
−a1 exp b1 (x + x0 )2 − a2 exp b2 (x − x0 )2
.
V (x) =
−a1 exp b1 (2x0 )2 − a2
f ür x > x0
(5.107)
Abb. 5.7. Aufenthaltswahrscheinlichkeit des Teilchens in der linken (durchgezogene Linie) bzw. rechten (gestrichelt dargestellt) Hälfte des Ortsbereichs
Zunächst fällt in Abb. 5.7 auf, dass für t → ∞ beide Wahrscheinlichkeiten gegen null gehen, was daran liegt, dass die ungebundenen Teilchen
den betrachteten Ortsbereich verlassen und dabei vom optischen Potential
absorbiert werden. Da dies ausschließlich links passiert, müssen diese fehlenden Teilchen eigentlich zu P2 hinzugerechnet werden. Trotzdem verbleibt die
Tatsache, dass das Tunneln nicht mehr periodisch ist wie in den vorangegangen Beispielen, sondern irreversiblen Charakter hat: Ist das Teilchen erst
einmal auf der linken Seite, ist die Wahrscheinlichkeit für ein Zurücktunneln
vernachlässigbar.
5.12 Einführung in die Quantenstatistik
233
5.12 Einführung in die Quantenstatistik
In den folgenden Abschnitten wollen wir etwas expliziter auf die Dynamik von
Atomen eingehen. Wir haben bereits an unserem eindimensionalen Modellatom aus Abschn. 5.9 gesehen, dass ein Atom über diskrete Energieniveaus
im negativen Energiebereich und ein darüberliegendes Kontinuum verfügt.
Die diskreten Energieniveaus gehören zu gebundenen Zuständen, während
bei den Zuständen aus dem Kontinuum das Elektron nicht an den Atomkern
gebunden ist. Einen Aspekt, den wir jedoch in unserem bisherigen Modell
überhaupt nicht verstehen können, ist wie ein Atom spontan von einem Energieniveau auf ein anderes (niedrigeres) springt und die überschüssige Energie
in Form eines Photons abgibt. Denn wenn sich das Elektron in einem Eigenzustand des vom Atomkern erzeugten Potentials befindet, sollte es für immer
und ewig in diesem Eigenzustand bleiben.
Dieser spontane Zerfall wurde erst im Rahmen der Quantenfeldtheorie
verständlich, als neben dem Atom auch das Vakuum mit seinen Nullpunktsfluktuationen in das Modell einbezogen wurde. Zum Glück ist es in vielen
Fällen nicht nötig, dieses Vakuum explizit mitzubetrachten, sondern man befindet sich in einer Situation, die ganz ähnlich der in Abschn. 4.6 ist: die
Vakuumflukationen sind eine Störung, die die ansonsten Hamiltonsche Dynamik des Atoms stört, und wie dort kann man dieser Störung durch eine
statistische Beschreibung gerecht werden, ohne diese explizit miteinzubeziehen. Dieser Grundgedanke ist die Basis der Quantenstatistik.
Auf eine detaillierte Herleitung des Formalismusses müssen wir im Rahmen dieses Buches zwar verzichten, wir möchten allerdings die wichtigsten
Punkte wiedergeben, so dass Sie die nachfolgenden Abschnitte verstehen
können, ohne zu einem anderen Buch greifen zu müssen. Letzteres können
wir Ihnen allerdings nicht ersparen, wenn Sie wissen möchten, wie man zu
den dargestellten Gleichungen kommt.
Beginnen wir mit der Beschreibung eines Zustandes in der Quantenstatistik. Während dieser Zustand bisher durch einen Zustandsvektor |ψ beschrieben wurde, tritt nun an dessen Stelle der Dichteoperator ˆ – statt eines
Vektors haben wir es nun also mit einer Matrix zu tun. Die bisher betrachteten Zustandsvektoren |ψ sind als sogenannte reine Zustände in dieser erweiterten Beschreibung enthalten, da Sie durch die Dichtematrix
ˆ = |ψψ| ,
(5.108)
dargestellt werden Können. Umgekehrt kann nicht jeder zulässige Dichteoperator in dieser Weise durch einen Zustandsvektor ausgedrückt werden, doch
darauf gehen wir später noch genauer ein. Diese nicht reinen Zustände heißen
gemischt.
Mittelwerte erhält man aus der Dichtematrix durch Spurbildung
(5.109)
Ô = Tr Ôˆ ,
234
5 Quantenmechanik
dabei ist die Spur Tr(M̂ ) einer Matrix M̂ definiert durch die Summe ihrer
Diagonalelemente:
Mii .
(5.110)
Tr(M̂ ) =
i
Diese Spur ist im Gegensatz zu den einzelnen Diagonalelementen unabhängig
von der Wahl der Basis, nach der man den Operator M̂ entwickelt. Sie können
sich davon überzeugen, dass wir im Fall von reinen Zuständen wieder zu
Gleichung (5.37) aus Abschn. 5.5 zurückkommen:
(5.111)
Ô = Tr Ôˆ
(5.112)
= Tr Ô|ψψ|
=
ψn On,m ψm
(5.113)
n,m
= ψ|Ô|ψ .
(5.114)
Schließlich benötigen wir noch die Bewegungsgleichung für die Dichtematrix , also den Ersatz für die Schrödingergleichung, die ja die Bewegungsgleichung für die Zustandsvektoren |ψ ist. Diese Bewegungsgleichung heißt
in Anlehnung an ihr Analogon aus der klassischen Statistischen Physik ebenfalls Mastergleichung und spaltet sich in einen Hamiltonschen (reversiblen)
Anteil und einen nicht-Hamiltonschen (irreversiblen) Anteil auf:
D
d
d
(t) =
(t)
(t)
+
.
(5.115)
dt
dt
dt
rev
irrev
Ohne Spezifizierung auf ein konkretes Problem lässt sich allerdings nur der
Hamiltonsche Anteil explizit angeben:
d
i
= − [H, ] .
(t)
(5.116)
dt
h̄
rev
Auch hier können wir uns wieder davon überzeugen, dass die Beschreibung der Quantenstatistik für reine Zustände und für eine rein Hamiltonsche
Dynamik wieder in die gewöhnliche Quantenmechanik übergeht:
1
∂
|ψ = H|ψ ⇒
∂t
ih̄
1
∂
|ψψ| =
((H|ψ) ψ| − |ψ (Hψ|))
∂t
ih̄
i
= − [H, |ψψ|] .
h̄
(5.117)
(5.118)
(5.119)
Als letzten Punkt wollen wir das quantenmechanische Analogon zur
Entropie einführen:
5.13 Ein Zwei-Niveau-System mit äußerer Anregung
S = −Tr ln .
235
(5.120)
Dabei ist der Logarithmus einer Matrix im Sinne von Abschn. 5.2 zu verstehen, d.h. der Logarithmus wird über seine Potenzreihe [31] definiert:
ln(1 + ) =
∞
(−1)n
n=1
n
.
n
(5.121)
In der Praxis wird man den Logarithmus von jedoch berechnen, indem man
in Diagonaldarstellung
(5.122)
= TDT−1
bringt. Hierbei ist D eine Diagonalmatrix und die Zeilenvektoren von T sind
die Eigenvektoren von . Dann ist
(ln)nm =
Tnl lnDll Tml .
(5.123)
l
Dabei haben wir schon davon Gebrauch gemacht, dass die Eigenvektoren von
senkrecht aufeinander stehen und folglich
T−1 = Tt
(5.124)
gilt.
Im Fall reiner Zustände = |ψψ| erhalten wir
S=0,
(5.125)
wovon man sich am Leichtesten überzeugt, indem man eine Basis wählt, in
der |ψ einer der Basisvektoren ist. Dann reduziert sich die Spurbildung in
(5.120) auf diesen Vektor:
S = −1 ln 1 = 0 .
(5.126)
Aus der Tatsache, dass es Dichtematrizen gibt, bei denen der Wert der
Entropie (5.120) von null verschieden ist, ergibt sich automatisch, dass diese
Zustände nicht in der Form (5.108) geschrieben werden können und daher
nicht rein sind.
5.13 Ein Zwei-Niveau-System mit äußerer Anregung
Das System, dem wir uns zunächst zuwenden wollen, hat einen denkbar einfachen, nämlich nur zweidimensionalen Hilbertraum. Die naheliegendste Realisierung eines solchen Systems ist ein Atom, bei dem nur zwei gebundene
Zustände betrachtet werden müssen – aber auch der innere Freiheitsgrad des
Spins führt bei Spin 1/2 und einem einzelnen, freien Teilchen auf zwei diskrete
Niveaus mit den Spineinstellungen ±1/2.
236
5 Quantenmechanik
Wenn wir die äußere Anregung für einen Moment ausser Acht lassen, wird
der Hamiltonsche Anteil der Dynamik durch den Hamiltonoperator
E1 0
H=
(5.127)
0 E2
mit den Energieniveaus E1 und E2 beschrieben. Eine äußere Anregung, z.B.
durch Einstrahlen von Licht, führt nun zu einem Zusatzterm im Hamiltonoperator:
∆E1 α
.
(5.128)
Hext =
α ∆E2
Interessant sind hierbei insbesondere die Nichtdiagonalelemente, die zu einer
Kopplung der beiden Eigenzustände des ungestörten Systems führen. Der
Einfachheit halber setzen wir die beiden Diagonalelemente ∆E1 und ∆E2
null, und haben zusammen den Hamiltonoperator
E1 α
Hges =
.
(5.129)
α E2
Ohne irreversible Terme in der Bewegungsgleichung würde dieses Problem auf
die Diagonalisierung des Hamiltonoperators (5.129), also einer 2×2-Matrix
hinauslaufen, und die Lösung wäre eine Überlagerung der beiden so gewonnenen Eigenzustände. Dass dies nicht die ganze Wahrheit sein kann, sehen
wir bereits an dem Problem ohne äussere Anregung: Bei dem Hamiltonoperator (5.127) müsste ein Atom immer im angeregten Zustand verbleiben – die
Wirklichkeit lehrt uns jedoch, dass jedes angeregte Atom mit einer für diesen
Anregungszustand typischen Zerfallszeit τ in den Grundzustand zurückkehrt.
Diese Diskrepanz mit der Realität wird behoben, wenn wir das Atom
an ein Wärmebad ankoppeln, d.h. ein System mit vielen Freiheitsgraden,
dessen Dynamik wir nicht kennen und uns auch gar nicht interessiert. Eine
solche Ankopplung an ein Wärmebad führt zu einem nicht-Hamiltonschen
Zusatzterm in der Bewegungsgleichung:
∂
i
γ21 = − [Ĥ, ] +
[Ŝ− , Ŝ+ ] + [Ŝ− , Ŝ+ ]
∂t
h̄
2
γ12 +
[Ŝ+ , Ŝ− ] + [Ŝ+ , Ŝ− ] ,
2
wobei die sogenannten Umklappoperatoren
00
01
S+ =
sowie S− =
10
00
(5.130)
(5.131)
das System aus dem Grundzustand in den angeregten Zustand befördern
(S+ ) bzw. den umgekehrten Übergang bewirken (S− ).
Neu sind in (5.130) die beiden Terme proportional zu γ21 bzw. γ12 . Der erste beschreibt den Übergang vom angeregten in den Grundzustand, während
5.13 Ein Zwei-Niveau-System mit äußerer Anregung
237
der Term in der zweiten Zeile den umgekehrten Vorgang beschreibt, der bei
einem Wärmebad endlicher Temperatur auch möglich ist – schließlich muss
sich im thermischen Gleichgewicht ein Zustand einstellen, bei dem die Besetzung der beiden Niveaus durch den Boltzmannfaktor wiedergegeben wird:
N1
= exp {−(E1 − E2 )/kT } .
N2
(5.132)
Daraus lässt sich herleiten, dass
γ12
= exp {−(E2 − E1 )/kT } .
γ21
(5.133)
gelten muss. Hierbei ist k die Boltzmann-Konstante, die Sie in der Literatur
auch als kB finden.
Der Vollständigkeit halber müssen wir noch anmerken, dass die quantenmechanische Mastergleichung (5.130) nur gilt, wenn
– der Grenzfall schwacher Dämpfung vorliegt: γ12 , γ21 (E2 − E1 )/h̄ ,
– die Markov-Approximation gerechtfertigt ist, was bedeutet, dass kT /h̄ >
γ12 , γ21 sein muss.
Abschließend wollen wir noch darauf hinweisen, dass im Grenzfall niedriger
Temperaturen kT E2 −E1 der letzte Term in (5.130) vernachlässigt werden
kann.
Ausgeschrieben lautet die Differentialgleichung (5.130)
∂ 11 12
i
α(21 − 12 )
−∆E12 + α(22 − 11 )
=−
α(12 − 21 )
∂t 21 22
h̄ ∆E21 + α(11 + 22 )
γ21 222 −12
γ12 −211 −12
+
+
(5.134)
−21 −222
−21 211
2
2
Die Implementation dieser Bewegungsgleichung in einem Programm stellt
uns vor keine besonderen Schwierigkeiten:
1
2
3
4
5
6
7
/**************************************************************************
* Name:
zweiniveau.cpp
*
* Zweck:
Dynamik eines Zweiniveau-Atoms mit aeusserer Anregung und
*
*
Daempfung
*
* Gleichung: Quanten-Mastergleichung
*
* verwendete Bibliothekl: GSL
*
**************************************************************************/
8
9
10
11
12
13
14
#include
#include
#include
#include
#include
#include
<iostream>
<fstream>
<gsl/gsl_errno.h>
<gsl/gsl_odeiv.h>
<math.h>
"tools.h"
15
16
17
using namespace std;
238
18
19
5 Quantenmechanik
//-- Definition von Konstanten
const int imax_max = 100;
20
21
22
//-- Definition globaler Variablen
double omega_12, alpha, gamma_down;
23
24
//-------------------------------------------------------------------------
25
26
int f(double t, const double x[], double dxdt[], void *params)
27
28
29
30
31
32
33
34
{
int
double
double
double
double
double
n, m, i, j;
rho_11_real, rho_12_real, rho_21_real, rho_22_real;
rho_11_imag, rho_12_imag, rho_21_imag, rho_22_imag;
drhodt_11_real, drhodt_12_real, drhodt_21_real, drhodt_22_real;
drhodt_11_imag, drhodt_12_imag, drhodt_21_imag, drhodt_22_imag;
mu = *(double *)params;
35
36
37
38
39
rho_11_real
rho_21_real
rho_12_real
rho_22_real
=
=
=
=
x[0];
x[2];
x[4];
x[6];
rho_11_imag
rho_21_imag
rho_12_imag
rho_22_imag
=
=
=
=
x[1];
x[3];
x[5];
x[7];
40
41
42
43
44
45
46
47
//-Terme durch
drhodt_11_real
drhodt_21_real
drhodt_21_imag
drhodt_12_real
drhodt_12_imag
drhodt_22_real
Eigenenergien
= 0;
drhodt_11_imag = 0;
= - omega_12 * rho_21_imag;
=
omega_12 * rho_21_real;
=
omega_12 * rho_12_imag;
= - omega_12 * rho_12_real;
= 0;
drhodt_22_imag = 0;
//-Terme durch
drhodt_11_real
drhodt_11_imag
drhodt_21_real
drhodt_21_imag
drhodt_12_real
drhodt_12_imag
drhodt_22_real
drhodt_22_imag
Daempfung
= drhodt_11_real
= drhodt_11_imag
= drhodt_21_real
= drhodt_21_imag
= drhodt_12_real
= drhodt_12_imag
= drhodt_22_real
= drhodt_22_imag
//-Terme durch
drhodt_11_real
drhodt_11_imag
drhodt_12_real
drhodt_12_imag
drhodt_21_real
drhodt_21_imag
drhodt_22_real
drhodt_22_imag
Kopplung (z.B. durch Pumpen)
= drhodt_11_real + alpha * (rho_21_imag-rho_12_imag);
= drhodt_11_imag - alpha * (rho_21_real-rho_12_real);
= drhodt_12_real + alpha * (rho_22_imag-rho_11_imag);
= drhodt_12_imag - alpha * (rho_22_real-rho_11_real);
= drhodt_21_real + alpha * (rho_11_imag-rho_22_imag);
= drhodt_21_imag - alpha * (rho_11_real-rho_22_real);
= drhodt_22_real + alpha * (rho_12_imag-rho_21_imag);
= drhodt_22_imag - alpha * (rho_12_real-rho_21_real);
48
49
50
51
52
53
54
55
56
57
+
gamma_down
+
gamma_down
-0.5*gamma_down
-0.5*gamma_down
-0.5*gamma_down
-0.5*gamma_down
gamma_down
gamma_down
*
*
*
*
*
*
*
*
rho_22_real;
rho_22_imag;
rho_21_real;
rho_21_imag;
rho_12_real;
rho_12_imag;
rho_22_real;
rho_22_imag;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
dxdt[0]
dxdt[2]
dxdt[4]
dxdt[6]
=
=
=
=
drhodt_11_real;
drhodt_21_real;
drhodt_12_real;
drhodt_22_real;
73
74
75
76
return GSL_SUCCESS;
}
dxdt[1]
dxdt[3]
dxdt[5]
dxdt[7]
=
=
=
=
drhodt_11_imag;
drhodt_21_imag;
drhodt_12_imag;
drhodt_22_imag;
5.13 Ein Zwei-Niveau-System mit äußerer Anregung
77
239
//------------------------------------------------------------------------
78
79
80
int jac(double t, const double x[], double *dfdx, double dxdt[],
void *params)
81
82
83
84
{
return GSL_SUCCESS;
}
85
86
//------------------------------------------------------------------------
87
88
int main( int argc, char *argv[] )
89
90
{
91
92
93
94
95
96
97
98
//-- Definition der Variablen
int
n, n1, n_out, neq;
double t, t_end, t1, t2, dt, E_1, E_2, rtol, atol, h, hmax, daux;
double x[6*imax_max];
double mu = 10;
ifstream in_stream;
ofstream out_stream;
99
100
101
102
103
104
105
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: zweiniveau infile outfile\n";
return 0;
}
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei generiert von zweiniveau.cpp\n";
inout(t_end,"t_end");
inout(n_out,"n_out");
inout(atol,"atol");
inout(rtol,"rtol");
inout(E_1,"E_1");
inout(E_2,"E_2");
inout(alpha,"alpha");
inout(gamma_down,"gamma_down");
inout(x[0],"x[0]");
inout(x[1],"x[1]");
inout(x[2],"x[2]");
inout(x[3],"x[3]");
inout(x[4],"x[4]");
inout(x[5],"x[5]");
inout(x[6],"x[6]");
inout(x[7],"x[7]");
in_stream.close();
120
121
122
123
124
//-- Berechnung einiger benoetigter Parameter -dt = t_end / n_out;
neq = 8;
omega_12 = E_2-E_1;
125
126
127
//-- Anfangsbedingungen
t
= 0;
128
129
130
131
132
133
134
135
//-- Initialisierung der GSL-Routine
const gsl_odeiv_step_type *T = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, neq);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(neq);
gsl_odeiv_system sys = {f, jac, neq, &mu};
240
136
137
138
139
140
141
142
143
144
145
146
147
148
5 Quantenmechanik
for (n=1; n<=n_out; n++)
{
t1 = n * dt;
while (t<t1)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t1, &h, x);
if (status != GSL_SUCCESS) break;
}
cout << t << "\n";
out_stream << t;
for (n1=0; n1<8; n1++) out_stream << " " << x[n1];
out_stream << "\n";
}
149
150
151
152
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
153
154
155
out_stream.close();
}
Wir haben die Real- und Imaginärteile der vier Komponenten der Dichtematrix zu einem Vektor x der Länge 8 zusammengefasst, wobei wir nicht
davon Gebrauch gemacht haben, dass hermitesch sein muss. Damit ließe
sich die Zahl der wirklich unabhängigen Variablen auf vier halbieren – andererseits ist die Hermitizität von auch eine gute Möglichkeit, das Programm
zu verifizieren, wenn der Programmcode von dieser Eigenschaft nicht explizit
Gebrauch macht.
In der Funktion f ab Zeile 26 schreiben wir und dessen zeitliche Ableitung in einer Matrix-ähnlichen Form, um die Übereinstimmung mit der
Bewegungsgleichung (5.134) deutlich zu machen.
Abb. 5.8. Aufenthaltswahrscheinlichkeit des Teilchens im Grund- (durchgezogene
Kurve) bzw. im angeregten Zustand (gestrichelte Kurve) eines 2-Niveau-Systems
mit äußerer Anregung und Dämpfung
Wir sehen in Abb. 5.8, dass die Aufenthaltswahrscheinlichkeit im angeregten Zustand abnimmt, während die Besetzung des Grundzustands entsprechend zunimmt – dies ist der Effekt der Dämpfung. Überlagert sehen wir die
Oszillationen, die wir schon aus den Abb. 5.5 und 5.6 kennen und die von der
Kopplung der beiden Niveaus, also der Anregung herrührt. Diese hat auch
zur Folge, dass für t → ∞ die Besetzung des angeregten Niveaus nicht gegen
null, sondern gegen einen endlichen Wert strebt.
5.14 Messprozess
241
5.14 Messprozess
An dem Beispiel des Zweiniveausystems können wir nun den Einfluss des
Messprozesses auf die Dynamik untersuchen. Zu diesem Zweck stellen wir uns
vor, dass wir zu beliebigen Zeitpunkten eine Messung vornehmen, ob sich das
System im Grund- oder angeregten Zustand befindet. Eine solche Messung
hat also nur zwei mögliche Ergebnisse, deren Wahrscheinlichkeiten aufgrund
der Kopenhagener Interpretation der Quantenmechanik (siehe Abschn. 5.5
bzw. 5.12) durch die Diagonalelemente 11 bzw. 22 gegeben sind. Auch den
Zustand nach der Messung liefert uns die Kopenhagener Interpretation: Je
nach Ausgang der Messung liegt der Zustand
10
00
oder
(5.135)
00
01
vor. Wir implementieren diesen Vorgang im Programm zeno1.cpp mit Hilfe
eines Zufallszahlengenerators, der entscheidet, welchen Ausgang die Messung
nimmt:
...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
for (n=1; n<=n_out; n++)
{
t1 = n * dt;
while (t<t1)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t1, &h, x);
if (status != GSL_SUCCESS) break;
}
rand = gsl_rng_uniform (r1);
if (rand < x[0])
{
x[0] = 1;
x[1] = 0;
x[2] = 0;
x[3] = 0;
x[4] = 0;
x[5] = 0;
x[6] = 0;
x[7] = 0;
}
else
{
x[0] = 0;
x[1] = 0;
x[2] = 0;
x[3] = 0;
x[4] = 0;
x[5] = 0;
x[6] = 1;
x[7] = 0;
}
cout << t << " " << x[0] << "\n";
out_stream << t << " " << x[0] << " " << x[6] << "\n";
}
166
167
168
169
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
170
171
172
out_stream.close();
}
Es überrascht Sie hoffentlich nicht, dass das Ergebnis dieses Programms
(Abb. 5.9) völlig anders aussieht als das des Programms zweiniveau.cpp.
242
5 Quantenmechanik
Abb. 5.9. Typischer Ausgang von aufeinanderfolgenden Messungen im zeitlichen
Abstand ∆t = 0.1 an einem Zweiniveausystem. Null bedeutet, dass das System im
Grundzustand ist – eins bedeutet, dass es sich im angeregten Zustand befindet
5.15 Der Zeno-Effekt
Wir müssen uns auch klar darüber sein, dass das dargestellte Ergebnis zwar
typisch ist, aber aufgrund des statistischen Charakters des Messvorgangs
nicht das einzig mögliche. Um zu – zumindest im Prinzip – vergleichbaren Ergebnissen zu kommen, müssen wir viele solcher typischen Kurven sammeln
und überlagern – ganz ähnlich zu unserer Vorgehensweise in Kap. 4. Das
Hauptprogramm des so entstandenen zeno2.cpp sieht dann so aus:
...
86
//-------------------------------------------------------------------------
87
88
int main( int argc, char *argv[] )
89
90
{
91
92
93
94
95
96
97
98
99
100
//-- Definition der Variablen
int
m, n, n1, n_out, neq, N_ensemble;
double t, t_end, t1, t2, dt, E_1, E_2, rand, rtol, atol, h, hmax, daux;
double x[8], x_0[8];
double mu = 10;
ifstream in_stream;
ofstream out_stream;
const gsl_rng_type * T1;
gsl_rng * r1;
101
102
103
104
105
106
107
//-- Fehlermeldung, wenn Input- und Outputfilename nicht uebergeben wurden
if (argc<3)
{
cout << " Aufruf: zeno2 infile outfile\n";
return 0;
}
108
109
110
111
112
113
114
115
116
117
118
//-- Einlesen der Parameter -in_stream.open(argv[1]);
out_stream.open(argv[2]);
out_stream << "! Ergebnis-Datei
inout(t_end,"t_end");
inout(atol,"atol");
inout(E_1,"E_1");
inout(alpha,"alpha");
inout(N_ensemble,"N_ensemble");
inout(x[0],"x_0[0]");
generiert von zeno2.cpp\n";
inout(n_out,"n_out");
inout(rtol,"rtol");
inout(E_2,"E_2");
inout(gamma_down,"gamma_down");
inout(x_0[1],"x[1]");
5.15 Der Zeno-Effekt
119
120
121
inout(x[2],"x_0[2]");
inout(x[4],"x_0[4]");
inout(x[6],"x_0[6]");
inout(x_0[3],"x[3]");
inout(x_0[5],"x[5]");
inout(x_0[7],"x[7]");
122
123
124
125
126
//-- Berechnung einiger benoetigter Parameter -dt = t_end / n_out;
neq = 8;
omega_12 = E_2-E_1;
127
128
129
gsl_vector *results = gsl_vector_alloc(n_out);
for (n=1; n<=n_out; n++) gsl_vector_set(results,n-1,0);
130
131
132
133
134
//-- Initialisierung des Zufallszahlengenerators
gsl_rng_env_setup();
T1 = gsl_rng_default;
r1 = gsl_rng_alloc (T1);
135
136
137
138
139
140
141
//-- Initialisierung der GSL-Routine
const gsl_odeiv_step_type *T = gsl_odeiv_step_rkf45;
gsl_odeiv_step
*s = gsl_odeiv_step_alloc(T, neq);
gsl_odeiv_control *c = gsl_odeiv_control_y_new(atol, rtol);
gsl_odeiv_evolve *e = gsl_odeiv_evolve_alloc(neq);
gsl_odeiv_system sys = {f, jac, neq, &mu};
142
143
144
for (m=0; m<N_ensemble; m++)
{
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//-- Anfangsbedingungen
t
= 0;
x[0] = x_0[0];
x[1] = x_0[1];
x[2] = x_0[2];
x[3] = x_0[3];
x[4] = x_0[4];
x[5] = x_0[5];
x[6] = x_0[6];
x[7] = x_0[7];
for (n=1; n<=n_out; n++)
{
t1 = n * dt;
while (t<t1)
{
int status = gsl_odeiv_evolve_apply(e, c, s, &sys, &t, t1, &h, x);
if (status != GSL_SUCCESS) break;
}
rand = gsl_rng_uniform (r1);
if (rand < x[0])
// Messergebnis: Niveau 1
{ x[0] = 1;
x[1] = 0;
x[2] = 0;
x[3] = 0;
x[4] = 0;
x[5] = 0;
x[6] = 0;
x[7] = 0; }
else
// Messergebnis: Niveau 2
{ x[0] = 0;
x[1] = 0;
x[2] = 0;
x[3] = 0;
x[4] = 0;
x[5] = 0;
x[6] = 1;
x[7] = 0; }
gsl_vector_set(results,n-1,gsl_vector_get(results,n-1)+x[0]);
}
if (m % 100 == 0) cout << m << "\n";
}
169
170
171
172
for (n=0; n<n_out; n++)
out_stream << n*dt+dt << " "
<< gsl_vector_get(results,n) / N_ensemble << "\n";
173
174
175
176
177
gsl_odeiv_evolve_free(e);
gsl_odeiv_control_free(c);
gsl_odeiv_step_free(s);
243
244
178
179
5 Quantenmechanik
out_stream.close();
}
Das Ergebnis dieses Programms (die durchgezogene Linie in Abb. 5.10)
sieht nun dem Resultat von Abschn. 5.13 schon etwas ähnlicher, es fällt jedoch
sofort auf, dass die Wahrscheinlichkeit im Vergleich zu Abb. 5.8 viel langsamer
und auch ohne die charakteristischen Oszillationen abnimmt:
Abb. 5.10. Wahrscheinlichkeit, ein Zweiniveausystem bei fortlaufenden Messungen
im Grundzustand zu finden. Bei der durchgezogenen Kurve erfolgten die Messungen
im zeitlichen Abstand von 1, bei der gestrichelten Kurve von 10−1 und bei der
gepunkteten Kurve im Abstand von 10−2
Der Grund ist, dass wir durch die Messungen die Dynamik des Systems beeinflussen – ein Effekt, den wir im vorangegangenen Abschnitt nicht berücksichtigt haben, da wir dort lediglich die Mastergleichung des Systems gelöst
haben. Diese Lösung lieferte zwar die Wahrscheinlichkeit, mit der man das
System nach einer Zeit t in einem gegebenen Zustand findet – jedoch unter
der Voraussetzung, dass bis zu diesem Zeitpunkt keine Messungen am System
stattgefunden haben.
Besonders drastisch wird dieser Unterschied, wenn wir sehr viele, kurz
aufeinander folgende Messungen am System vornehmen. In diesem Fall hat
das System fast keine Zeit, aus dem Zustand, in den es durch die vorhergehende Messung projiziert wurde, in den anderen überzugehen und das System
wird – lediglich durch die Messungen – quasi eingefroren. Diesen Sachverhalt spiegeln die beiden weiteren Kurven in Abb. 5.10 wieder, bei denen die
Frequenz der Messungen gegenüber der durchgezogenen Kurve verzehnfacht
(gestrichelte Kurve) bzw. verhundertfacht (gepunktet dargestellt) wurde.
Wir wollen diesen Effekt – den sogenannten Zeno-Effekt – etwas näher
beleuchten, und dabei eine analytische Lösung für den Grenzfall sehr kleiner
Zeitabstände zwischen zwei aufeinander folgender Messungen herleiten. Wir
bezeichnen mit n den Zustand nach und mit ˜n den Zustand unmittelbar vor
der n-ten Messung. Unser erstes Ziel ist eine stroboskopische Beschreibung,
die den Zusammenhang von n und n+1 angibt.
Beginnen wir mit dem etwas langwierigeren Teil: dem Zusammenhang
zwischen n und ˜n+1 . Dazu müssen wir die Mastergleichung über das –
kurze – Zeitintervall ∆t lösen. Dabei berücksichtigen wir zunächst jedoch nur
den Hamiltonschen Anteil der Bewegung – aus Gründen, die im Folgenden
5.15 Der Zeno-Effekt
245
sichtbar werden.
⇒
i
∂
= − [H, ]
∂t
h̄
∂
i
11 = + α (21 − 12 )
∂t
h̄
i
∂
12 = − (∆E21 − α (22 − 11 ))
∂t
h̄
i
∂
21 = − (−∆E12 + α (22 − 11 )) .
∂t
h̄
(5.136)
(5.137)
(5.138)
(5.139)
Wir differenzieren (5.137) nach der Zeit und setzen (5.138) und (5.139) ein:
∂2
2α2
α∆E
=
(12 + 21 ) .
11
2 (22 − 11 ) −
∂t2
h̄
h̄2
(5.140)
Unter der Voraussetzung, dass die Nichtdiagonalelemente von nicht besetzt
sind (was wir weiter unten klären werden), ist die Lösung dieses Differentialgleichungssystems für kleine Zeiten δt gegeben durch:
α2
(n,22 + n,11 ) (δt)2
h̄2
α2
˜n+1,22 (δt) = n,22 + 2 (n,22 + n,11 ) (δt)2
h̄
˜n+1,11 (δt) = n,11 −
(5.141)
(5.142)
Der entscheidende Punkt an dieser Stelle ist, dass die Änderung der Dichtematrixelemente von der Größenordnung (δt)2 ist.
Der Einfluss der Messung liefert uns schließlich den Zusammenhang zwischen ˜n+1 und n+1 . Dieser ist zum Glück sehr einfach: n+1 setzt sich aus
den Dichtematrizen
10
00
und
(5.143)
00
01
zusammen, wobei wir deren statistische Gewichte ˜n+1,11 bzw. ˜n+1,22 berücksichtigen müssen. Wir erhalten also
10
00
˜n+1,11
0
n+1 = ˜n+1,11
+ ˜n+1,22
=
(5.144)
00
01
0
˜n+1,22
Die Messung vernichtet also quasi die Nichtdiagonalelemente, während die
Diagonalelemente unangetastet bleiben. An dieser Stelle wird klar, warum
wir bei der Zeitentwicklung davon ausgehen konnten, dass 12 und 21 beide
anfänglich null sind, und warum wir uns bei der Lösung nicht für deren Werte
nach der kurzen Zeit δt interessiert haben: ersteres garantiert die vorangegange Messung und die nachfolgende Messung macht diese beiden Matrixelemente ohnehin zu null. Fassen wir nun die beiden Teile – die Entwicklung über
kurze Zeiten δt ((5.142) und (5.142)) und den Effekt der Messung (5.144)
zusammen, erhalten wir:
246
5 Quantenmechanik
n+1 =
n,11 + ξ(22 − 11 )
0
0
n,22 − ξ(22 − 11 )
.
(5.145)
Dabei haben wir die Abkürzung
ξ=
2α2
2
(δt)
h̄2
(5.146)
eingeführt. Was passiert nun, wenn wir im Grundzustand starten und – wie
in Abb. 5.10 – ein endliches Zeitintervall ∆t in sehr viele (N ) kleine Einheiten
δt = ∆t/N zerteilen, nach denen jeweils eine Messung erfolgt? Nach der ersten Messung ist die Besetzung des angeregten Zustands ξ, nach der zweiten
Messung 2ξ und so weiter. Entsprechend nimmt die Besetzung des Grundzustands ab. Nach N Messungen sind die beiden Diagonalmatrixelemente
also
2α2 (∆t)2
h̄2 N
2
2α (∆t)2
=
.
h̄2 N
11 (∆t) = 1 − N ξ = 1 −
(5.147)
22 (∆t)
(5.148)
= Nξ
Wir sehen, dass in der Tat die Dynamik im Grenzfall N → ∞ eingefroren
wird. Kommen wir nun noch zu der Frage, warum wir uns bei dieser Berechnung auf eine rein Hamiltonsche Zeitentwicklung beschränkt habe und
eventuelle dissipative Terme in der Mastergleichung ausgeschlossen haben.
Der Grund ist einfach: wenn Dämpfung vorliegt, gibt es im allgemeinen den
Zeno-Effekt nicht. Berechnet man nämlich wie vorhin die Änderung von zwischen zwei Messungen, erhält man Terme, die proportional zu δt sind
(und nicht wie in (5.146) proportional zu (δt)2 ). Diese führen dazu, dass
die zu (5.147) und (5.148) entsprechenden Ausdrücke nicht für N → ∞ in
11 (t = 0) und 22 (t = 0) übergehen. Diese Terme proportional zu δt verschwinden jedoch, wenn der Ausgangszustand von der dissipativen Dynamik
nicht berührt wird, wenn also
∂
(t = 0)
=0
(5.149)
∂t
irrev
ist; und genau diese Situation liegt Abb. 5.10 zugrunde.
5.16 Ein Ein-Atom-Laser
In diesem Beispiel wollen wir einen einfachen Laser betrachten, wobei wir
sowohl das Lichtfeld als auch das lichtemittierende Material quantenmechanisch behandeln wollen. Um das Problem in einem handhabbaren Rahmen zu
halten, beschränken wir uns bei letzterem auf ein einziges Atom – und reißen
dabei interessanterweise ein Themengebiet an, das in den letzten Jahren ein
5.16 Ein Ein-Atom-Laser
247
aktuelles Forschungsgebiet war: der Ein-Atom-Laser [32, 33]. Wir beginnen
unsere Überlegungen, indem wir das Zwei-Niveau-System aus Abschn. 5.13
durch Hinzunahme eines weiteren Niveaus zu einem Drei-Niveau-System erweitern:
Abb. 5.11. Energieschema eines Drei-Niveau-Systems, bei dem vom Grundzustand
in den höchsten Zustand angeregt wird und das System durch Ankopplung an ein
Wärmebad von den angeregten Zuständen in niedrigere Zustände übergehen kann
Die zugehörige Mastergleichung ist zwar länglich, wird jedoch durch den
Vergleich mit ihrem Pendant bei zwei Energieniveaus (5.130) verständlich:
∂
i
= − [Ĥ, ]
∂t
h̄ γ21
+
[Ŝ21 , Ŝ12 ] + [Ŝ21 , Ŝ12 ] +
2
γ31 +
[Ŝ31 , Ŝ13 ] + [Ŝ21 , Ŝ12 ] +
2
γ23 +
[Ŝ23 , Ŝ32 ] + [Ŝ23 , Ŝ32 ] +
2
(5.150)
γ12 [Ŝ12 , Ŝ21 ] + [Ŝ12 , Ŝ21 ]
2
γ13 [Ŝ13 , Ŝ31 ] + [Ŝ12 , Ŝ21 ]
2
γ32 [Ŝ32 , Ŝ23 ] + [Ŝ32 , Ŝ23 ] ,
2
wobei der Hamiltonoperator H durch die Eigenenergien und das Pumpen
vom Grundzustand (1) in das oberste Energieniveau (2) bestimmt wird:
⎞
⎛
E1 α 0
(5.151)
Ĥ = ⎝ α E2 0 ⎠ .
0 0 E3
Die letzten drei Zeilen in (5.150) beinhalten zunächst den spontanen Übergang von Niveau 2 nach 1, dann von Niveau 3 nach 1 und schließlich von
Niveau 2 nach 3. Die Operatoren Sij definieren wir analog zu (5.131):
(Sij )nm = δim δjn .
(5.152)
Wenn Sie (5.150) ausschreiben und in einem Computerprogramm (auf der
CD dreiniveau.cpp) implementieren, erhalten Sie für die Funktion f, die
die Differentialgleichung festlegt, die folgenden Programmzeilen:
248
5 Quantenmechanik
...
23
//-------------------------------------------------------------------------
24
25
int f(double t, const double x[], double dxdt[], void *params)
26
27
28
29
30
{
int
n1, n2, n3, n, m, i, j;
double rho[3][3][2], drho_dt[3][3][2];
double sum;
31
32
33
34
35
n = 0;
for (n1=0; n1<3; n1++)
for (n2=0; n2<3; n2++)
for (n3=0; n3<2; n3++) {rho[n1][n2][n3] = x[n]; n++;}
36
37
38
39
40
41
42
43
//-Terme durch Eigenenergien
for (n1=0; n1<3; n1++)
for (n2=0; n2<3; n2++)
{
drho_dt[n1][n2][0] = -omega[n1][n2] * rho[n1][n2][1];
drho_dt[n1][n2][1] = omega[n1][n2] * rho[n1][n2][0];
}
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
for (n1=0; n1<2; n1++)
{
//-Terme durch Daempfung 2->1
drho_dt[0][0][n1] = drho_dt[0][0][n1]
drho_dt[1][0][n1] = drho_dt[1][0][n1]
drho_dt[0][1][n1] = drho_dt[0][1][n1]
drho_dt[1][1][n1] = drho_dt[1][1][n1]
drho_dt[2][1][n2] = drho_dt[2][1][n1]
drho_dt[1][2][n1] = drho_dt[1][2][n1]
//-Terme durch Daempfung 3->1
drho_dt[0][0][n1] = drho_dt[0][0][n1]
drho_dt[2][0][n1] = drho_dt[2][0][n1]
drho_dt[0][2][n1] = drho_dt[0][2][n1]
drho_dt[2][2][n1] = drho_dt[2][2][n1]
drho_dt[1][2][n1] = drho_dt[1][2][n1]
drho_dt[2][1][n1] = drho_dt[2][1][n1]
//-Terme durch Daempfung 2->3
drho_dt[2][2][n1] = drho_dt[2][2][n1]
drho_dt[1][2][n1] = drho_dt[1][2][n1]
drho_dt[2][1][n1] = drho_dt[2][1][n1]
drho_dt[1][1][n1] = drho_dt[1][1][n1]
drho_dt[0][1][n1] = drho_dt[0][1][n1]
drho_dt[1][0][n1] = drho_dt[1][0][n1]
}
+
gamma_12
-0.5*gamma_12
-0.5*gamma_12
gamma_12
-0.5*gamma_12
-0.5*gamma_12
*
*
*
*
*
*
rho[1][1][n1];
rho[1][0][n1];
rho[0][1][n1];
rho[1][1][n1];
rho[2][1][n1];
rho[1][2][n1];
+
gamma_13
-0.5*gamma_13
-0.5*gamma_13
gamma_13
-0.5*gamma_13
-0.5*gamma_13
*
*
*
*
*
*
rho[2][2][n1];
rho[2][0][n1];
rho[0][2][n1];
rho[2][2][n1];
rho[1][2][n1];
rho[2][1][n1];
+
gamma_32
-0.5*gamma_32
-0.5*gamma_32
gamma_32
-0.5*gamma_32
-0.5*gamma_32
*
*
*
*
*
*
rho[1][1][n1];
rho[1][2][n1];
rho[2][1][n1];
rho[1][1][n1];
rho[0][1][n1];
rho[1][0][n1];
69
70
71
72
73
74
75
76
77
78
79
//-Terme durch Kopplung (z.B. durch
drho_dt[0][0][0] = drho_dt[0][0][0]
drho_dt[0][0][1] = drho_dt[0][0][1]
drho_dt[0][1][0] = drho_dt[0][1][0]
drho_dt[0][1][1] = drho_dt[0][1][1]
drho_dt[1][0][0] = drho_dt[1][0][0]
drho_dt[1][0][1] = drho_dt[1][0][1]
drho_dt[1][1][0] = drho_dt[1][1][0]
drho_dt[1][1][1] = drho_dt[1][1][1]
drho_dt[0][2][0] = drho_dt[0][2][0]
Pumpen)
+ alpha
- alpha
+ alpha
- alpha
+ alpha
- alpha
+ alpha
- alpha
+ alpha
*
*
*
*
*
*
*
*
*
(rho[1][0][1]-rho[0][1][1]);
(rho[1][0][0]-rho[0][1][0]);
(rho[1][1][1]-rho[0][0][1]);
(rho[1][1][0]-rho[0][0][0]);
(rho[0][0][1]-rho[1][1][1]);
(rho[0][0][0]-rho[1][1][0]);
(rho[0][1][1]-rho[1][0][1]);
(rho[0][1][0]-rho[1][0][0]);
rho[1][2][1];
5.16 Ein Ein-Atom-Laser
80
81
82
83
84
85
86
drho_dt[0][2][1]
drho_dt[1][2][0]
drho_dt[1][2][1]
drho_dt[2][0][0]
drho_dt[2][0][1]
drho_dt[2][1][0]
drho_dt[2][1][1]
=
=
=
=
=
=
=
drho_dt[0][2][1]
drho_dt[1][2][0]
drho_dt[1][2][1]
drho_dt[2][0][0]
drho_dt[2][0][1]
drho_dt[2][1][0]
drho_dt[2][1][1]
+
+
+
alpha
alpha
alpha
alpha
alpha
alpha
alpha
*
*
*
*
*
*
*
249
rho[1][2][0];
rho[0][2][1];
rho[0][2][0];
rho[2][1][1];
rho[2][1][0];
rho[2][0][1];
rho[2][0][0];
87
88
89
90
91
n = 0;
for (n1=0; n1<3; n1++)
for (n2=0; n2<3; n2++)
for (n3=0; n3<2; n3++) {dxdt[n] = drho_dt[n1][n2][n3]; n++;}
92
93
94
return GSL_SUCCESS;
}
95
96
//-------------------------------------------------------------------------
...
Gegenüber dem Programm zweiniveau.cpp haben wir die Darstellung
der Dichtematrix geändert: der besseren Übersichtlichkeit fassen wir alle
Elemente von in einem Feld zusammen, dessen erste beiden Indizes die
Quantenzahlen angeben, während der letzte Index angibt, ob es sich um den
Realteil oder den Imaginärteil handelt. Zunächst bestimmen wir in Zeile 37–
43 die Terme in den Bewegungsgleichungen, die von den Eigenenergien der
atomaren Niveaus herrühren. Dazu definieren wir im Hauptprogramm eine
Matrix omega, die die Energiedifferenzen zweier beliebiger Niveaus bereithält:
...
150
151
for (n1=0; n1<3; n1++)
for (n2=0; n2<3; n2++) omega[n1][n2] = E[n2]-E[n1];
...
Anschließend finden Sie in den Zeilen 47 bis 68 alle Dämpfungsterme und
schließlich die Terme durch den Pumpprozess. Beachten Sie, dass die Dämpfung z.B. von Niveau 2 nach 1 auch zu Termen in den Bewegungsgleichungen
für 23 oder 32 führt (siehe Zeile 52/53) und entsprechendes gilt natürlich
auch für die Dämpfung von Niveau 3 nach 1 bzw. von Niveau 2 nach 3.
Um einen Laser zu realisieren, benötigen wir eine sogenannte Inversion,
d.h. ein höheres Niveau ist – im Gegensatz zur Besetzung im thermischen
Gleichgewicht – stärker besetzt als ein niedrigeres Niveau. In unserem Beispiel
kommen hierfür nur die Niveaus 3 und 1 in Frage:
Wir regen das System durch Pumpen vom Grundzustand in den angeregten Zustand 2 an. Bevor dieser Zustand (durch Pumpen oder durch die
Dämpfung) wieder in den Grundzustand übergeht, kann das Atom durch den
zweiten Dämpfungsterm in den Zustand 3 übergehen. Dieses Bevölkern des
Zustandes 3 auf dem Umweg über 2 ist umso effektiver, je
–
–
stärker das Pumpen von 1 nach 2 ist,
stärker die Dämpfung von 2 nach 3 ist (erlaubter Übergang)
250
–
5 Quantenmechanik
schwächer die Dämpfung von 3 nach 1 und von 2 nach 1 ist (verbotene
Übergänge).
Diese Bedingungen sind bei
γ21 = γ31 = 0.01
γ23 = 1
(5.153)
(5.154)
gut erfüllt und ergeben das folgende Resultat:
Abb. 5.12. Besetzung der drei Niveaus eines gedämpften Dreiniveau-Systems mit
äußerer Anregung. Die Parameter sind so gewählt, dass sich im stationären Zustand eine Inversion zwischen Zustand 1 (Grundzustand, durchgezogene Kurve)
und Zustand 3 (gestrichelt dargestellt) einstellt
Zu Beginn befinden sich die Atome alle im Grundzustand (durchgezogene
Linie) und werden – wie in Abschn. 5.13 durch das Pumpen in Niveau 2 (gepunktete Kurve) befördert. Neu hinzugekommen ist die Möglichkeit, von dort
zum Niveau 3 (gestrichelte Kurve) zu gelangen, wo sie – bedingt durch die
Wahl einer kleinen Dämpfungskonstante γ31 lange bleiben. Auf diese Weise
sammeln sich die Atome quasi in Niveau 3 und wir erhalten die gewünschte
Inversion relativ zum Grundzustand.
Was uns nun noch zu einem Laser fehlt, ist die Ankopplung an eine Lichtmode. Mathematisch ist das elektromagnetische Feld des Lichts äquivalent
zum harmonischen Oszillator, weswegen wir die Eigenzustände des Lichtfeldes wie beim harmonischen Oszillator in Abschn. 5.9 durch Angabe einer
Quantenzahl n beschreiben können und die zugehörigen Eigenenergien durch
1
h̄ω
(5.155)
En = n +
2
gegeben sind. Verwenden wir diese Zustände als Basis, werden die Elemente der Dichtematrix außer durch die zwei Atomniveaus noch durch zwei
Quantenzahlen für das Lichtfeld indiziert:
ij
⇒
i,n;j,m .
(5.156)
5.16 Ein Ein-Atom-Laser
251
i und j nummerieren die atomaren Niveaus und laufen bei unserem DreiNiveau-Atom von null bis drei, während n und m die Photonenzahl angeben
und deswegen jede ganze Zahl größer gleich null annehmen können.
An dieser Stelle führen wir Erzeugungs- und Vernichtungsoperatoren ein,
die einen Zustand mit n Photonen in einen mit n + 1 bzw. n − 1 Photonen
überführen (also ein Photon erzeugen bzw. vernichten):
√
b+ |n = n + 1 |n + 1
(5.157)
√
b|n = n |n − 1 .
(5.158)
Um die Bewegungsgleichung nun entsprechend zu erweitern, müssen wir
drei verschiedene Terme berücksichtigen:
–
–
–
den Term durch die Energieniveaus des Lichtfelds (5.155),
den Term durch Absorbtion / Emission eines Photons durch das Atom,
die Terme durch die Dämpfung des Lichtfeldes.
Beginnen wir mit der Dynamik durch die Energieniveaus (5.155) – wie immer
in skalierten Einheiten, wobei wir die bislang noch offene Skalierung der Zeit
so wählen, daß ω = 1 ist:
∂
i,n;j,m = i(n − m)i,n;j,m .
∂t
(5.159)
Der zweite Teil, der die Absorbtion bzw. Emission eines Photons beschreibt,
ist ebenfalls Hamiltonsch:
H = β S − b+ + S + b
(5.160)
√
∂
1,n;j,m = iβ n 3,n−1;j,m
⇒
(5.161)
∂t
√
∂
3,n;j,m = iβ n + 1 1,n+1;j,m
(5.162)
∂t
√
∂
i,n;3,m = −iβ m + 1 i,n;1,m+1
(5.163)
∂t
√
∂
i,n;1,m = −iβ m i,n;3,m−1 .
(5.164)
∂t
Der neu eingeführte Parameter β beschreibt die Kopplung des Atoms an
das Lichtfeld, d.h. je größer dieser Wert ist, umso wahrscheinlicher ist der
Übergang von Niveau 3 nach 1 (bzw. umgekehrt) unter Emission (bzw. Absorbtion) eines Photons.
Abschließend benötigen wir noch die Mastergleichung für einen gedämpften harmonischen Oszillator, die dem eines atomaren Systems sehr ähnlich
ist:
∂
(5.165)
= γphot 2b+ b − b+ b − bb+ .
∂t
Dabei haben wir die Dämpfungskonstante γphot für das Lichtfeld eingeführt.
Im Programm sieht das Ganze dann so aus:
252
5 Quantenmechanik
...
42
43
44
45
46
47
48
49
50
51
52
53
//-Terme durch Eigenenergien
for (n1=0; n1<3; n1++)
for (n2=0; n2<3; n2++)
for (n3=0; n3<nmax; n3++)
for (n4=0; n4<nmax; n4++)
{
n = n3-n4;
drho_dt[n1][n2][n3][n4][0] =
-(n + omega[n1][n2]) * rho[n1][n2][n3][n4][1];
drho_dt[n1][n2][n3][n4][1] =
(n + omega[n1][n2]) * rho[n1][n2][n3][n4][0];
}
...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//-Terme durch Emission/Absorbtion eines Photons
for (n1=0; n1<nmax; n1++)
for (n3=0; n3<3; n3++)
{
for (n2=0; n2<nmax-1; n2++)
{
drho_dt[2][n3][n2][n1][0] = drho_dt[2][n3][n2][n1][0]
- beta * sqrt(n2+1.) * rho[0][n3][n2+1][n1][1];
drho_dt[2][n3][n2][n1][1] = drho_dt[2][n3][n2][n1][1]
+ beta * sqrt(n2+1.) * rho[0][n3][n2+1][n1][0];
drho_dt[n3][2][n1][n2][0] = drho_dt[n3][2][n2][n1][0]
+ beta * sqrt(n2+1.) * rho[n3][0][n1][n2+1][1];
drho_dt[n3][2][n1][n2][1] = drho_dt[n3][2][n2][n1][1]
- beta * sqrt(n2+1.) * rho[n3][0][n1][n2+1][0];
}
for (n2=1; n2<nmax; n2++)
{
drho_dt[0][n3][n2][n1][0] = drho_dt[0][n3][n2][n1][0]
- beta * sqrt(float(n2)) * rho[2][n3][n2-1][n1][1];
drho_dt[0][n3][n2][n1][1] = drho_dt[0][n3][n2][n1][1]
+ beta * sqrt(float(n2)) * rho[2][n3][n2-1][n1][0];
drho_dt[n3][0][n1][n2][0] = drho_dt[n3][0][n2][n1][0]
+ beta * sqrt(float(n2)) * rho[n3][2][n1][n2-1][1];
drho_dt[n3][0][n1][n2][1] = drho_dt[n3][0][n2][n1][1]
- beta * sqrt(float(n2)) * rho[n3][2][n1][n2-1][0];
}
}
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//-Terme durch Daempfung der Lichtmode
for (n1=0; n1<3; n1++)
for (n2=0; n2<3; n2++)
for (n3=0; n3<nmax-1; n3++)
for (n4=0; n4<nmax-1; n4++)
for (n5=0; n5<2; n5++)
drho_dt[n1][n2][n3][n4][n5] = drho_dt[n1][n2][n3][n4][n5]
+ gamma_phot*2.*sqrt(float((n3+1)*(n4+1)))*rho[n1][n2][n3+1][n4+1][n5];
for (n1=0; n1<3; n1++)
for (n2=0; n2<3; n2++)
for (n3=0; n3<nmax; n3++)
for (n4=0; n4<nmax; n4++)
for (n5=0; n5<2; n5++)
drho_dt[n1][n2][n3][n4][n5] = drho_dt[n1][n2][n3][n4][n5]
- gamma_phot*(n3+n4)*rho[n1][n2][n3][n4][n5];
...
5.16 Ein Ein-Atom-Laser
253
Im Gegensatz zum Dreiniveau-Atom ohne Lichtfeld haben wir es nun
mit einem unendlich-dimensionalen Hilbertraum zu tun, den das Programm
laser.cpp natürlich beschneiden muss, was sich in der Beschränkung auf
ein maximales nmax für die betrachtete Photonenzahl äußert. Dies birgt die
Gefahr, dass man diesen Wert zu niedrig ansetzt und merkliche Anteile der
Dichtematrix vernachlässigt werden. Um dies auszuschließen bauen wir in den
Hauptteil zwei Kontrollen ein: Zum einen berechnen wir eine Größe n edge,
die angibt wie stark der künstlich eingeführte Rand der Dichtematrix besetzt ist; zum anderen berechnen wir die Spur der Dichtematrix. Wenn alles
korrekt ist, muss die erste Größe sehr klein bleiben, während die Spur der
Dichtematrix eins sein muss:
...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
n_phot = 0; n_edge = 0;
n_1 = 0; n_2 = 0; n_3 = 0;
for (n1=0; n1<nmax; n1++)
{
n_1 = n_1 + rho[0][0][n1][n1][0];
n_2 = n_2 + rho[1][1][n1][n1][0];
n_3 = n_3 + rho[2][2][n1][n1][0];
n_phot = n_phot + n1 * (rho[0][0][n1][n1][0]+rho[1][1][n1][n1][0]
+rho[2][2][n1][n1][0]);
}
trace = n_1 + n_2 + n_3;
out_stream << t << " " << n_1 << " " << n_2 << " " << n_3 << " "
<< n_phot << " " << n_edge << " " << trace << "\n";
}
...
Als weitere Bedingung, die wir überprüfen sollten, bevor wir einem Ergebnis Glauben schenken, bietet sich die Tatsache an, dass alle Diagonalelemente
der Dichtematrix positiv sein müssen, da sie als Wahrscheinlichkeiten interpretiert werden können.
Abschließend müssen wir noch den Parametersatz für das DreiniveauAtom um Werte für die Kopplungskonstante β und die Dämpfung γphot
ergänzen. Für
(5.166)
β=1
γphot = 0.05
(die restlichen Parameter sind gegenüber dem Drei-Niveau-Atom unverändert) erhalten wir Abb. 5.13.
Beginnend bei t = 0 pumpen wir zunächst das Atom vom Grundzustand
(dessen Besetzung in Abb. 5.13 gestrichelt dargestellt ist) über das Niveau 2
in das Niveau 3 (gepunktete Kurve in Abb. 5.13). Erst wenn dieses Niveau
besetzt ist, erhalten wir durch spontane Emission Photonen (deren mittlere
Zahl ist die durchgezogene Kurve in Abb. 5.13), was dann allerdings wieder
zu einer Entvölkerung des Niveaus 3 und zu einer stärkeren Besetzung des
Grundzustands führt. An dieser Stelle können wir auch qualitativ erklären,
warum der Ein-Atom-Laser so viel wissenschaftliches Interesse gewonnen hat:
Während in einem normalen Laser mit vielen Atomen zwei Photonen unmittelbar hintereinander in die Lichtmode eingekoppelt werden können, ist dies
254
5 Quantenmechanik
Abb. 5.13. Besetzung der Niveaus 1 und 3 (gestrichelte bzw. gepunktete Linie),
eines Drei-Niveau-Atoms, das an ein Lichtfeld gekoppelt ist, sowie die Photonenzahl
im Lichtfeld (durchgezogene Kurve)
bei einem Ein-Atom-Laser nicht möglich, da sich das einzige Atom nach der
ersten Emission im Grundzustand befindet. Man hat also guten Grund, anzunehmen, dass die Statistik der Photonen beim Ein-Atom-Laser grundlegend
anders ist als beim konventionellen Laser. Um dies genauer zu beleuchten,
müssten wir leider tief in die Quantenoptik eintauchen – den Leser, der über
entsprechende Kenntnisse verfügt, verweisen wir auf Übung 5.7.
Übungen
5.1 Eigenzustände des Delta-Potentials
Die Eigenzustände zu einem δ-förmigen Potential V (x) = aδ(x) können analytisch bestimmt werden. Gerade deswegen ist es aber ein gute Übung, diese
numerisch zu berechnen und mit dem analytischen Ergebnis zu vergleichen.
Verwenden Sie als Ausgangspunkt das Programm eigen1.cpp aus Abschnitt
5.9 und modifizieren Sie den Programmabschnitt, in dem das Potential festgelegt wird. Studieren Sie insbesondere die Abhängigkeit des Ergebnisses von
den Parametern xmax und dx.
5.2 Eindimensionales Modell eines Atoms
√
Das Potential V (x) = β/ 1 + αx2 kann als einfaches Modell für ein Atom
dienen, wobei die Parameter α und β die Breite und die Tiefe des vom Atomkern erzeugten Potentials festlegen. Berechnen Sie für einige Parametersätze
die gebundenen Zustände und deren Bindungsenergien. Letztere können Sie
mit den Ergebnissen aus Abschn. 5.10 vergleichen. Interessant ist es auch,
die freien Zustände für niedrige und für große Energien zu betrachten.
Übungen
255
5.3 Eindimensionales Modell für H+
2
Fügen Sie zwei der Einzelpotentiale aus der vorangegangenen Aufgabe zum
Potential zweier Atomkerne im Abstand a zusammen und berechnen Sie wieder die Energieniveaus der gebundenen Zustände. Variieren Sie nun den Abstand a und bestimmen Sie die Grundzustandsenergie als Funktion dieses
Parameters. Was Sie erhalten ist das effektive Potential, in dem sich (aufgrund der großen Masse der Atomkerne auf einer viel größeren Zeitskala)
die beiden Kerne bewegen. Aus dem Potentialverlauf kann also sowohl der
Abstand der beiden Kerne im Grundzustand als auch deren Schwingungsfrequenz entnommen werden.
5.4 Berechnung von Energieniveaus nach der Variationsmethode
In Abschn. 5.10 haben wir die Eigenenergien eines Modellatoms (siehe auch
Aufgabe 5.2) nach der Variationsmethode berechnet. Als Testfunktionen
dienten uns dabei Funktionen vom Typ
1
fn = √ xn exp(−µx2 /2) ,
n!
(5.167)
wobei der Parameter µ fest gewählt wurde. Untersuchen Sie, was passiert,
wenn Sie diesen Parameter verändern – insbesondere, wenn Sie ein sehr großes
oder ein sehr kleines µ nehmen. Interessant ist es auch, mehrere solche Funktionensätze zu verschiedenen µ zu kombinieren und so einen größeren Satz
an Testfunktionen zu haben, aus denen man die Eigenzustände und die dazugehörigen Eigenenergien berechnet.
5.5 Quantenmechanische Entropie
Überlegen Sie sich, wie groß die in (5.120) definierte Entropie bei einem
Zwei-Niveau- bzw. Drei-Niveau-Atom werden kann. Implementieren Sie nun
eine Berechnung der Entropie in die Programme zweiniveau.cpp sowie
dreiniveau.cpp und untersuchen Sie den Zeitverlauf der Entropie.
5.6 Zeno-Effekt
Wenn Sie bei den Parametern zum Programm zeno2.cpp (auf der CD-ROM)
die Zahl der Messungen im Zeitintervall [0 : 100] auf 20 herabsetzen, werden
Sie feststellen, dass die Abnahme der Besetzung des angeregten Zustandes
wieder langsamer erfolgt – entgegen dem allgemeinen Trend. Woran liegt
das? Betrachten Sie zum Vergleich noch einmal Abb. 5.8!
256
5 Quantenmechanik
5.7 Ein-Atom-Laser
Durch Spurbildung über die atomaren Freiheitsgrade kommen Sie von der
Dichtematrix i,n;j,m aus Abschn. 5.16 auf eine Dichtematrix nur für das
Atom:
i,n;i,m .
(5.168)
n;m =
i
Aus dieser können Leser mit Kenntnissen in Quantenoptik (siehe z.B. [34])
die Quasiwahrscheinlichkeit
Q(α) =
1
α||α
π
(5.169)
berechnen. Dabei machen wir Gebrauch von den kohärenten Zuständen
1 2 αn
√ |n .
(5.170)
|α = exp − |α|
2
n!
n
A Installation der Pakete unter Linux
Diesem Buch liegt eine CD-ROM bei, auf der Sie alle für die numerische
Physik nötigen Softwarepakete finden, so dass Sie nach deren Installation alle
Programme dieses Buches ausprobieren und weiterentwickeln können. Dabei
müssen je nach der von Ihnen getroffenen Software-Auswahl noch Anpassungen an den Programmen vorgenommen werden. Wenn Sie Linux installiert
haben – und davon geht dieser Abschnitt aus – benötigen Sie noch folgende
Programmpakete:
–
–
–
–
–
einen Compiler einer höheren Programmiersprache,
einen Editor, in dem Sie die Quellprogramme schreiben können,
eine Numerik-Bibliothek, die mathematische Standardaufgaben (z.B. die
Lösung einer Differentialgleichung) übernimmt,
ein Grafikprogramm zur Visualisierung der Ergebnisse,
und ein Programm, mit dessen Hilfe die vom Grafikprogramm erzeugten
Grafiken auf dem Bildschirm dargestellt werden können.
Für jeden dieser Punkte finden Sie auf der CD-ROM mehrere zur Auswahl
stehende Programme, z.B. entspricht jedes Verzeichnis in /linux/editors einem unter Linux lauffähigen Editor. Wenn Sie sich nun für einen Editor entschieden haben (selbstverständlich können Sie aber auch mehrere Editoren
installieren) und sich den Inhalt des entsprechenden Verzeichnisses anschauen, so finden Sie dort typischerweise:
–
ein Verzeichnis src, in dem Sie die Quelltexte des betreffenden SoftwarePaketes finden,
– ein Verzeichnis bin mit bereits kompilierten Binärversionen,
– ein Verzeichnis doc mit dazu verfügbarer Dokumentation.
Installation von rpm-Paketen
Am Einfachsten gestaltet sich die Installation, wenn Sie eine auf dem Red
Hat Package Manager (kurz rpm) aufbauende Linux-Distribution haben, wie
z.B. Red Hat oder SUSE. In diesem Fall können Sie für die meisten Pakete
auf ein passendes rpm-Paket im Unterverzeichnis bin zurückgreifen, z.B.
gsl-1.3-155.i586.rpm
Der Dateiname setzt sich aus dem Namen des Pakets (hier gsl), der Versionsund der Releasenummer (hier 1.3 und 155) und einem Zusatz zusammen, der
hier anzeigt, dass es sich um die Binärversion für die Plattform i586 (IntelProzessoren ab dem Pentium oder kompatible Prozessoren wie der AMD
Athlon) handelt.
Zunächst sollten Sie überprüfen, ob das betreffende Paket bereits installiert ist, z.B.:
258
A Installation der Pakete unter Linux
> rpm -q gsl
gsl-1.3-0
Das Kommando rpm dient allgemein der Verwaltung der gleichnamigen
Pakete. Mit der Option -q liefert es entweder eine Meldung, dass das Paket nicht installiert ist, oder es zeigt den Namen des installierten Pakets
mit Versions- und Revisionsnummer (hier 1.3 und 0). Wenn das Paket nicht
installiert oder die installierte Version veraltet ist, können Sie das Paket installieren, indem Sie als root das Kommando
> rpm --install XXX
bzw.
> rpm --freshen XXX
ausführen, wobei XXX der Dateiname des betreffenden Pakets ist. Alternativ können Sie die Installation aus dem Installationsmanager der jeweiligen
Distribution heraus ausführen, z.B. yast2 bei SUSE. Sowohl bei dieser Vorgehensweise als auch bei der oben vorgestellten Installation aus der Kommandozeile heraus, wird die Installation direkt ausgeführt, sofern dies möglich ist.
Unter Umständen kann es vorkommen, dass ein von dem zu installierenden
Programm benötigtes Paket nicht installiert ist bzw. das zu installierende
und ein bereits installiertes Paket nicht koexistieren können, was jeweils zu
einer entsprechenden Fehlermeldung führt. In diesem Fall müssen Sie vor der
Installation des gewünschten Software-Paketes das fehlende Paket installieren, bzw. ein störendes Paket deinstallieren (für das Entfernen eines Pakets
ersetzen Sie die Option --install durch --erase).
Installation von Debian-Paketen
Die Installation bei Debian-orientierten Linux-Distributionen verläuft sehr
ähnlich wie bei den eben besprochenen rpm-basierten Linux-Varianten. Allerdings kommt hier ein anderes Paket-Management zum Einsatz, so dass
sowohl die Pakete als auch die Kommandos zur Installation ein anderes Format haben. Die Debian-Pakete erkennen Sie an der Endung .deb – ansonsten
ist der Dateiname analog zu denen bei den rpm-Paketen aufgebaut, z.B. ist
gsl-bin_1.1.1-1_i386.deb
ein Debian-Paket der GSL-Library Version 1.1.1, Revision 1. Vor der Installation eines Paketes überprüfen Sie bitte, ob das betreffende Paket bereits
installiert ist:
dpkg --status gsl-bin
A Installation der Pakete unter Linux
259
Das Kommando dpkg ist das Analogon zum Befehl rpm im vorhergehenden Abschnitt. Es dient der Installation, Deinstallation und Verwaltung
der Programmpakete. Die Optionen für Installation bzw. Deinstallation lauten --install und --purge. Für Details verweisen wir auf die im pdfFormat beigelegte Einführung Dwarf ’s Guide to Debian GNU/Linux von
Dale Scheetz.
Installation durch Übersetzen der Quellprogramme
Ist kein passendes rpm- bzw. Debian-Paket vorhanden, können Sie auf die
Quelltexte im Unterverzeichnis src ausweichen. Dort finden Sie im Allgemeinen eine Datei mit einem Namen wie README, READ.ME oder LIESMICH.
Diese enthält detaillierte Anweisungen, wie die Quelltexte entpackt, kompiliert und installiert werden müssen. In vielen Fällen liegen die Quelltexte zu
einer neueren Version bereits vor, während sich die rpm-Pakete noch auf eine
Vorgängerversion beziehen.
Dokumentation
Zu jedem Programmpaket existiert Dokumentation, die Sie im Unterverzeichnis doc finden. Diese kann in verschiedenen Formaten vorliegen, die Sie jeweils
an Ihrer Endung erkennen können:
–
Bei rpm liegt die Dokumentation selbst im rpm-Format vor und muss
entsprechend installiert werden. Nach der Installation befindet sich die
Dokumentation in /usr/share/doc/packages/.
– Die Endung ps kennzeichnet eine Postscript-Datei. Sie kann auf einem
Postscript-fähigen Drucker direkt ausgedruckt oder z.B. mit ghostview
am Bildschirm betrachtet werden. Wenn Ihr System sauber installiert
ist, sollte ein Ausdruck auch dann möglich sein, wenn der Drucker nicht
Postscript-fähig ist – in diesem Fall übersetzt ghostview die PostscriptBefehle in eine für den Drucker verständliche Form.
– Ebenfalls mit ghostview oder z.B. mit dem Acrobat Reader von Adobe
können die pdf-Dateien (Portable Data Format) betrachtet werden.
– Bei htm bzw. html handelt es sich um Dokumente in der Hypertext Markup Language, die bei Internet-Seiten verwendet wird. Zur Darstellung ist
daher jeder Browser geeignet.
– dvi ist das Ausgabeformat von TEX/LATEX und kann, wenn diese Pakete installiert sind, entweder mit xdvi auf dem Bildschirm betrachtet
oder mittels dvips, dvipdf oder dvihtml in Postscript, pdf bzw. html
umgewandelt werden.
– Die Endung txt kennzeichnet eine Text-Datei, die mit jedem Editor betrachtet werden kann.
260
A Installation der Pakete unter Linux
Bei manchen Software-Paketen ist die Dokumentation nicht vom eigentlichen
Programm getrennt, in diesem Fall erfolgt die Installation der Dokumentation in /usr/share/doc/packages/ automatisch, wenn Sie das Programm
installieren.
Hilfe im Internet
Auf den Internetseiten des jeweiligen Software-Paketes finden Sie meistens
weitergehende Hilfen. Interessant sind hierbei insbesondere die FAQs (Frequently Asked Questions), die Probleme behandeln, über die schon mehrere
Anwender gestolpert sind und deswegen schon häufig gestellt wurden. Zu
vielen umfangreicheren Paketen gibt es auch eine Newsgroup, in der über email ein reger Gedankenaustausch zu allen möglichen Aspekten der betreffenden Software stattfindet. Wenn Sie an dieser Newsgroup teilnehmen wollen,
müssen Sie sich bei dieser anmelden – Hinweise dazu finden Sie im Allgemeinen auf der Homepage des Software-Paketes. An diese Newsgroup können Sie
sich auch mit einem konkreten Problem wenden, das Sie trotz Zuhilfenahme
der Dokumentation nicht lösen konnten. Es wird jedoch erwartet, dass man
zunächst die bereits erwähnten FAQs durchforstet, bevor man die Hilfe der
Newsgroup-Mitglieder in Anspruch nimmt. Da Newsgroup-Beiträge meistens
archiviert werden, ist dieses Archiv eine weitere nützliche Informtionsquelle,
die man vorab konsultieren sollte.
B Installation der Pakete unter Windows
Diesem Buch liegt eine CD-ROM bei, auf der Sie alle für die numerische
Physik nötigen Softwarepakete finden, so dass Sie nach deren Installation
alle Programme dieses Buches ausprobieren und weiterentwickeln können. In
diesem Anhang gehen wir von einer lauffähigen Windows-Version aus, so dass
Sie noch folgende Programmpakete benötigen:
–
–
–
–
–
–
einen Compiler einer höheren Programmiersprache,
einen Editor, in dem Sie die Quellprogramme schreiben können,
eine Numerik-Bibliothek, die mathematische Standardaufgaben (z.B. die
Lösung einer Differentialgleichung) übernimmt,
ein Grafikprogramm zur Visualisierung der Ergebnisse,
und ein Programm, mit dessen Hilfe die vom Grafikprogramm erzeugten
Grafiken auf dem Bildschirm dargestellt werden können.
Da viele Programmpakete in gepackter Form vorliegen, brauchen Sie
darüber hinaus ein Programm, das diese Archive entpackt. Solche Archivierprogramme finden Sie im Verzeichnis \windows\archiver. Sie benötigen eigentlich nur unzip oder pkunzip, die Archive mit der Endung .zip
entpacken können. Ich empfehle jedoch auch die Installation der entsprechenden Programme zip bzw. pkzip, die umgekehrt Dateien in ein Archiv
packen können.
Für jeden dieser Punkte finden Sie auf der CD-ROM eines oder mehrere zur Auswahl stehende Programme, z.B. entspricht jedes Verzeichnis in
\windows\editors einem unter Windows lauffähigen Editor. Wenn Sie sich
nun für einen Editor entschieden haben (selbstverständlich können Sie aber
auch mehrere Editoren installieren) und sich den Inhalt des entsprechenden
Verzeichnisses anschauen, so finden Sie dort typischerweise:
–
ein Verzeichnis src, in dem Sie die Quelltexte des betreffenden SoftwarePaketes finden,
– ein Verzeichnis bin mit bereits kompilierten Binärversionen,
– ein Verzeichnis doc mit dazu verfügbarer Dokumentation.
Installation durch Entpacken
Am Einfachsten gestaltet sich die Installation, wenn das zu installierende
Programm nur aus einer ausführbaren Datei besteht. Wenn Sie dieses in
ein Verzeichnis kopieren, das in der Umgebungsvariablen PATH enthalten
ist, ist die Installation damit bereits abgeschlossen. In vielen Fällen besteht
das Software-Paket aber aus mehreren Dateien, z.B. dem ausführbaren Programm, einer Bibliothek und zum Programm gehörenden Hilfetexten. In diesem Fall sind normalerweise alle diese Dateien in einem Archiv (meistens im
262
B Installation der Pakete unter Windows
zip-Format) enthalten, das Sie auspacken müssen. Beachten Sie dabei, dass
beim Entpacken jede Datei in das richtige Verzeichnis kommt – detaillierte Anweisungen finden Sie in der dazugehörigen Installationsanleitung, die
Sie in einer Datei mit dem Namen README, READ.ME, LIESMICH o.Ä. finden.
In diesen Anleitungen steht auch, ob und welche Umgebungsvariablen Sie
deklarieren müssen.
Besonders komfortabel gestaltet sich die Installation, wenn das Anlegen
der Verzeichnisse und das Entpacken der Dateien von einem Installationsprogramm durchgeführt wird, das Sie interaktiv durch eine Installationsprozedur führt. Ein solches Programm nimmt auch die Deklaration von benötigten
Umgebungsvariablen in der Systemkonfiguration vor.
Installation von djgpp
Am kniffligsten ist die Installation der GNU-Compiler und der dazugehörigen Numerik-Bibliotheken. Wenn Sie auf Probleme stoßen, sind die Homepage von djgpp (http://www.delorie.com/djgpp/) und die der betreffenden Bibliothek sicherlich hilfreich. Diese Compiler sind ursprünglich für Unix
entwickelt worden und erfordern die Installation der Unix-ähnlichen Laufzeitumgebung djgpp. Eine Anleitung finden Sie auf der CD in der Datei
\windows\djgpp\install.txt. Um Ihnen diese etwas langwierige Prozedur
weitgehend zu ersparen, befindet sich auf der CD eine zip-Datei mit einer
Art Komplettinstallation: djgpp komplett.zip. Diese Komplettinstallation
umfasst die Basispakete von djgpp, nützliche Filterprogramme wie grep,
Compiler für C/C++ sowie FORTRAN und die dazugehörigen NumerikBibliotheken. Legen Sie zunächst ein Verzeichnis an, in dem die kompletten
djgpp-Pakete abgelegt werden sollen, z.B. C:\djgpp. Wechseln Sie in dieses
Verzeichnis und entpacken Sie die zip-Datei mittels
> pkunzip D:\windows\djgpp\bin\djgpp_komplett
Hierbei haben wir angenommen, dass Ihrem CD-Laufwerk der Laufwerksbuchstabe D zugeordnet ist. Nun sollten Sie unter anderem im Verzeichnis
C:\djgpp\bin sehr viele EXE-Dateien und namensgleiche Dateien ohne Endung haben, z.B. ls.exe und ls. Abschließend müssen Sie nur noch eine
Umgebungsvariable setzen, bei Windows 95/98/ME können Sie das bewerkstelligen, indem Sie in der Datei autoexec.bat die Zeile
set DJGPP=C:\djgpp\djgpp.env
eintragen – wobei Sie den dort angegeben Pfad entsprechend modifizieren
müssen, wenn das Hauptverzeichnis von djgpp bei Ihnen nicht C:\djgpp ist.
Wenn djgpp erfolgreich installiert wurde, können Sie mit
> bash
B Installation der Pakete unter Windows
263
in einen Kommandomodus wechseln, der die Funktionalität der bourne-Shell
aus der Unix-Welt bietet. Wenn Sie in diesem Modus ls eingeben, wird
das oben genannte Programm C:\windows\djgpp\bin\ls ausgeführt. Dieses Programm erzeugt eine Auflistung aller Dateien im Verzeichnis, ganz
ähnlich zu dem Ihnen bekannten Programm dir. Sie können aber auch direkt in der DOS-Box ls eingeben – in diesem Fall wird das Programm
C:\windows\djgpp\bin\ls.exe aufgerufen, das die selbe Auflistung von Dateien ergibt.
Wie schon oben erwähnt, gehören zum Gesamtpaket djgpp die GNUCompiler, unter denen sich neben dem C-Compiler auch ein C++- und ein
FORTRAN-Compiler findet. Für den, der seine Programme lieber in einer
Entwicklungsumgebung als im gewöhnlichen Editor schreibt, ist RHIDE interessant.
Installation von Cygwin
Alternativ zu djgpp, bietet sich unter Windows Cygwin an, das ebenfalls die
Tools der Unix-Welt dem Windows-Anwender in einer Shell zur Verfügung
stellt. Sofern man diese bei der Installation ausgewählt hat, gehören dazu
auch die von uns benötigten GNU-Compiler, numerische Bibliotheken und
Grafikprogramme. Letztlich bietet cygwin die selbe Funktionalität wie djgpp
und offeriert zudem die Möglichkeiten einer graphischen Schnittstelle zum
Anwender, indem es das X-Protokoll in der Windows-Umgebung umsetzt.
Sehr angenehm ist die Tatsache, dass für Cygwin ein Installationsprogramm
setup.exe existiert.
Installation durch Übersetzen der Quellprogramme
Auch unter Windows liegen nicht alle Software-Pakete fertig übersetzt vor,
insbesondere für die von uns benötigten Numerik-Bibliotheken ist dies nicht
der Fall. In diesem Fall müssen Sie auf die Quelltexte im Unterverzeichnis
src ausweichen. Dort finden Sie im Allgemeinen eine Datei mit einem Namen
wie README, READ.ME oder LIESMICH. Diese enthält detaillierte Anweisungen,
wie die Quelltexte entpackt, kompiliert und installiert werden müssen.
Dokumentation
Die Dokumentation im Unterverzeichnis doc kann in verschiedenen Formaten
vorliegen, die Sie jeweils an Ihrer Endung erkennen können:
–
Die Endung .ps kennzeichnet eine Postscript-Datei. Sie kann auf einem
Postscript-fähigen Drucker direkt ausgedruckt oder z.B. mit ghostview
am Bildschirm betrachtet werden.
264
B Installation der Pakete unter Windows
–
Ebenfalls mit ghostview oder z.B. mit dem Acrobat Reader von Adobe
können die pdf-Dateien (Portable Data Format) betrachtet werden.
– Bei .htm bzw. .html handelt es sich um die Hypertext Markup Language,
die bei Internet-Seiten verwendet wird. Zur Darstellung ist daher jeder
Browser geeignet.
– Die Endung .txt kennzeichnet eine Text-Datei, die mit jedem Editor
betrachtet werden kann.
Bei manchen Software-Paketen ist die Dokumentation nicht vom eigentlichen
Programm getrennt, in diesem Fall erfolgt die Installation der Dokumentation
automatisch, wenn Sie das Programm installieren.
Hilfe im Internet
Wie bei Linux-Software finden Sie auch zu freier Windows-Software auf den
Internetseiten des jeweiligen Software-Paketes weitergehende Hilfen. Hervorzuheben sind hier die sogenannten Newsgroups, in denen über e-mail ein reger
Gedankenaustausch zu allen möglichen Aspekten der betreffenden Software
stattfindet. Zu vielen umfangreicheren Paketen gibt es auch eine Newsgroup,
in der über e-mail ein reger Gedankenaustausch zu allen möglichen Aspekten
der betreffenden Software stattfindet. Insbesondere können Sie – nachdem Sie
sich bei der betreffenden Newsgroup angemeldet haben – an andere Teilnehmer Fragen zu einem konkreten Problem stellen, das Sie trotz Zuhilfenahme
der Dokumentation nicht lösen konnten. Um zu vermeiden, dass dabei immer
wieder die selben Fragen gestellt werden, wurden die sogenannten FAQs (Frequently Asked Questions) zusammengstellt, die häufig gestellte Fragen und
kompetente Antworten dazu beinhalten. Es wird erwartet, dass man zunächst
diese FAQs durchforstet, bevor man die Hilfe der Newsgroup-Mitglieder in
Anspruch nimmt.
C Mathematische Bibliotheken
Für die Lösung mathematischer Standardprobleme auf dem Computer gibt es
— zum Glück — fertige Routinen, die in wir in unsere Programme einbinden
können. Diese Routinen werden in Bibliotheken zusammengefasst, aus denen der Compiler sich beim Linken (dem Erzeugen der ausführbaren Datei)
die jeweils benötigten Unterprogramme bzw. Funktionen holt. Dazu müssen
wir beim Aufruf des Compilers diese Bibliothek explizit mit der Option -l
angeben, z.B. verwendet
> gpp -o faden_lib faden_lib.cpp -lgsl -lgslcblas -lm
die Bibliotheken GSL, GSLCBLAS und M. Unter Unix haben die zugehörigen Dateien die Namen libgsl.a, libgslcblas.a und libm.a und befinden
sich im Verzeichnis /usr/lib. Von diesen Bibliotheken wollen wir in diesem
Anhang einige vorstellen — ohne Anspruch auf Vollständigkeit. Am Anfang
dieser Zusammenstellung stehen Bibliotheken, die mehr oder weniger alle numerischen Standardprobleme abdecken, während auf einen bestimmten Problemkreis spezialisierte Bibliotheken im Anschluss vorgestellt werden.
IMSL
IMSL steht für International Mathematics Standard Library und es gibt diese
Bibliothek für alle erdenklichen Plattformen (auch für Linux und in einer für
FORTRAN und einer für C gedachten Variante). Sie finden in IMSL praktisch
für jedes Problem eine geeignete Routine, die Vielfalt kann durchaus schon
verwirrend sein. Da IMSL ein kommerzielles Produkt von Visual Numerics
ist, kann es nicht auf der CD zur Verfügung gestellt werden.
NAG
NAG steht für Numerical Arithmetics Group, und bei deren gleichnamiger
Numerik-Bibliothek handelt es sich ebenfalls um ein kommerzielles Produkt,
das das gesamte Spektrum numerischer Probleme abdeckt. Auch diese Bibliothek gibt es in einer FORTRAN- und einer C-Variante: NAG bzw CNAG.
GSL
GSL steht für GNU Scientific Library und hat den Anspruch, eine frei
verfügbare Alternative zu IMSL zu sein. Im Gegensatz zu IMSL steht GSL
266
C Mathematische Bibliotheken
allerdings nur für C++ zur Verfügung. Konzeptionell geht GSL eigene Wege, so dass beim Umstieg von einer anderen Bibliothek eine gewisse Umgewöhnungsphase nötig ist. Danach wird man jedoch dieses modulare Konzept zu schätzen wissen. Obwohl GSL bereits sehr umfangreich ist und kaum
mehr Wünsche offen lässt, wird an dieser Bibliothek noch sehr eifrig weiterentwickelt. Schon aus diesem Grund lohnt sich ein Blick auf die Homepage
http://sources.redhat.com/gsl/, wo man auch Links zu den Mailinglisten
findet.
SLATEC
SLATEC ist eine zwar schon etwas ältere, aber sehr umfangreiche FortranBibliothek, die ebenfalls alle Gebiete abdeckt. Aufgrund seines Umfangs ist
SLATEC die erste Wahl, wenn man eine frei verfügbare Fortran-Bibliothek
sucht. Wie viele andere frei verfügbaren Bibliotheken finden Sie sie auf
http://gams.nist.gov/.
MATPACK
MATPACK ist ein sehr junges Projekt der Technischen Universität München.
Wie GSL ist MATPACK für den Einsatz in C++-Programmen vorgesehen.
Im Gegensatz zu GSL verfolgt MATPACK einen eher konventionellen Ansatz
und ist daher für Umsteiger einfacher zu handhaben. Da MATPACK erst am
Anfang seiner Entwicklung steht, ist der Umfang der Bibliothek eher gering.
Wer an dieser Bibliothek interessiert ist, sollte die Weiterentwicklung auf der
Internetseite http://www.matpack.de/ verfolgen.
CERNLIB
CERNLIB ist in dieser Liste die letzte Bibliothek, die alle numerischen
Problemstellungen abdeckt. Ursprünglich wurde CERNLIB für die numerischen Problemstellungen im Zusammenhang mit Hochenergiephysik entwickelt – das Ergebnis ist eine ausgesprochen umfangreiche Bibliothek,
die für alle physikalischen Disziplinen geeignet ist. Auf der Internetseite
http://wwwinfo.cern.ch/cernlib/overview.html. finden Sie die jeweils
aktuelle Version der CERNLIB sowie Dokumentation.
BLAS und CBLAS
BLAS steht für Basic Linear Algebra System und beinhaltet Routinen rund
um Matrizen- und Vektorrechnung. Solche Routinen werden auch in einer
C Mathematische Bibliotheken
267
ganzen Reihe anderer Probleme benötigt (z.B. zur Lösung nichtlinearer Gleichungssysteme) und da BLAS einen de-facto-Standard darstellt, greifen die
meisten Mathematikbibliotheken auf BLAS zurück.
In diesem Zusammenhang ist es interessant, dass es mehrere Implementationen von BLAS (mit einem definierten FORTRAN-Interface) und CBLAS
(mit einem definierten C-Interface) gibt, die sich in der Performance erheblich unterscheiden können. Die Verwendung einer gut optimierten BLASbzw. CBLAS-Bibliothek kann die Geschwindigkeit eines Programms deutlich erhöhen, selbst wenn man direkt keine Routinen zur Linearen Algebra aufruft. Dem Leser sei insbesondere ein Blick auf das ATLAS-Projekt
(http://math-atlas.sourceforge.net/) und – sofern ein Intel-Prozessor
eingesetzt wird – die Intel Math Kernel Library (zu finden auf der InternetSeite http://developer.intel.com/software/products/mkl/index.htm)
ans Herz gelegt.
FFTPACK, FITPACK, HOMPACK, ITPACK,
MINPACK, . . .
Hier handelt es sich um eine ganzen Reihe von Einzelbibliotheken, deren
Name fast immer mit PACK endet und die frei verfügbar sind (MATPACK gehört nicht in diese Kategorie, dieses ist ein Paket, das das gesamte Spektrum der Numerik abdecken will). Sie finden diese Pakete auf
http://www.netlib.org/XXX/index.html, wobei XXX durch den Namen des
Pakets (klein geschrieben) ersetzt werden muss. In diesem Zusammenhang
ist auch die Internetseite http://gams.nist.gov/ interessant, die eine Zusammenstellung von Numerikbibliotheken mit einer Kurzbeschreibung bietet.
Alle diese Bibliotheken liegen als eine Sammlung von Quellprogrammen vor,
die erst übersetzt und dann zu einer Bibliothek zusammengebaut werden
müssen.
LAPACK
LAPACK dient der Lösung von Aufgaben aus der Linearen Algebra. Im Gegensatz zu den vorher besprochenen Bibliotheken, die ebenfalls auf PACK
enden, gibt es von LAPACK fertige rpm- und Debian-Pakete. Neben der ursprünglichen, für FORTRAN ausgelegten Variante von LAPACK gibt es eine
C- und eine C++-Version namens CLAPACK bzw. LAPACK++.
D Fortran und C
Wie bereits in der Einleitung dieses Buches festgestellt wurde, werden in
der numerischen Physik hauptsächlich zwei Programmiersprachen eingesetzt:
C/C++ und FORTRAN. Für welche dieser beiden Sprachen man sich entscheidet, hängt von einer Vielzahl von Faktoren ab:
–
Die Betreuungssituation am jeweiligen Institut: welche Programmiersprache verwenden der Betreuer und die übrigen Institusmitglieder?
– Der Fundus an bereis vorhandenen Quellprogrammen, auf die zurückgegriffen werden kann.
– Die Bedeutung der Programmiersprache im späteren Berufsleben spricht
für C/C++.
– Die Implementierung von komplexen Zahlen ist ein klarer Vorteil von
FORTRAN.
– C/C++ hat dafür Vorteile bei systemnahen Aufgaben. Dazu gehören das
Anlegen und Löschen von Dateien und Vereichnissen ebenso wie Eingaben
über die Tastatur und Ausgabe auf den Bildschirm.
In den meisten Situationen wird es Argumente sowohl für C/C++ als auch
für FORTRAN geben. In diesem Fall ist es interessant, dass Programmteile
in C/C++ und solche in FORTRAN miteinander verbunden werden können.
Das ist nicht weiter schwierig, wenn man einige kleinere Fallstricke kennt.
Der erste Fallstrick besteht darin, dass FORTRAN-Compiler an alle
Namen von Programmen oder Funktionen einen Unterstrich anhängen,
während C das nicht macht. Wenn wir also eine C-Routine aus FORTRAN
aufrufen wollen, müssen wir im C-Programm diesen Unterstrich explizit an
den Namen der Routine anhängen, z.B. void routine1 ().
Der nächste Fallstrick liegt in der Übergabe von Parametern, wobei wir
zunächst mit einfachen Datentypen, also ganzen oder reellen Zahlen beginnen
wollen. FORTRAN übergibt solche Parameter grundsätzlich by reference, d.h.
wenn Fortran eine Routine gemäß
call unterprogramm(a)
aufruft, bekommt das Unterprogramm nicht den Wert der Variablen a, sondern dessen Adresse im Speicherbereich. Das Unterprogramm kann dann
nicht nur den Wert der Variablen vor dem Aufruf verwenden (in diesem Fall
handelt es sich um eine Eingabevariable), sondern auch deren Wert verändern
(in diesem Fall spricht man von einer Ausgabevariablen). Springt man dann
aus dem Unterprogramm wieder ins Hauptprogramm zurück, bleiben alle
Änderungen von Variablen, die im Unterprogramm vorgenommen wurden,
erhalten.
C hingegen übergibt Variablen grundsätzlich by value, d.h. die Unterprogramme erhalten nur eine lokale Kopie der Übergabeparameter. Ändert die
C-Routine nun den Wert dieser lokalen Kopie, sind solche Änderungen nach
270
D Fortran und C
der Rückkehr ins Hauptprogramm verloren. Jetzt stellt sich natürlich die
Frage, wie man es in C überhaupt realisiert, dass Übergabeparameter dauerhaft geändert werden (so etwas muss ja möglich sein!). Der Trick besteht
darin, der Routine nicht die Variable selbst zu übergeben, sondern explizit
die Speicheradresse (also das, was FORTRAN automatisch macht).
Mit diesen beiden Punkten im Hinterkopf lässt sich ein einfaches Beispiel
realisieren. Zunächst das aufrufende FORTRAN-Programm:
1
2
3
******************************************************************
* FORTRAN-Programm, das eine C-Routine aufruft
*
******************************************************************
4
5
program fc1_f
6
7
real*8
x
8
9
10
11
x = 0.d0
call fc1c(x)
write(6,*) x
12
13
14
STOP
end
Und nun die C-Routine:
1
2
3
/*------------------------------------------------------------C-Routine, die von einem FORTRAN-Programm aufgerufen wird
-------------------------------------------------------------*/
4
5
void fc1c_(double *x)
6
7
8
9
10
11
{
printf(" %f\n", *x);
*x = 1.;
return;
}
Das Hauptprogramm in FORTRAN definiert eine doppelt genaue Zahl x
und setzt diese auf null. Danach ruft es eine C-Routine namens fc1c auf.
Im C-Programm sehen wir, dass hier der Name fc1c lautet und der Übergabeparameter nicht einfach x wie im FORTRAN-Programm ist, sondern
der Zeiger auf x. Die C-Routine gibt den anfänglichen Wert von x auf den
Bildschirm aus und setzt ihn danach auf 1. Nach der Rückkehr ins Hauptprogramm wird wieder der Wert von x ausgegeben.
Um dieses Programm zu testen, übersetzen wir zunächst die C-Routine:
> gcc -c fc1_c.c
Die Option -c bewirkt, dass das Programm nur übersetzt, nicht aber gelinkt
wird (also kein ausführbarer Code erzeugt wird). Nach diesem Kompiliervorgang haben wir eine Object-Datei fc1 c.o, die wir bei der Übersetzung des
FORTRAN-Programms angeben müssen:
> g77 -o fc1_f fc1_f.f fc1_c.o
D Fortran und C
271
Dies erzeugt nun ein ausführbares Programm fc1 f, und wenn wir dieses
aufrufen, erhalten wir das erwartete Resultat:
> fc1_f
0.000000
1.
Wie sieht es nun aus, wenn wir keine einfachen Datentypen übergeben,
sondern Arrays (also Vektoren oder Matrizen)? Erstaunlicherweise ist das
sogar einfacher, weil in C ein Array ein Zeiger auf das erste Element dieses
Arrays ist. Dadurch ist das Verhalten von FORTRAN und C in diesem Fall
identisch. Aufpassen muss man lediglich bei der Indizierung der einzelnen
Elemente:
–
In FORTRAN hat das erste Element standardmäßig den Index 1, was
jedoch auf jeden beliebigen anderen Wert geändert werden kann – in C
hingegen hat das erste Element immer den Index 0.
– Bei Arrays mit mehreren Indizes ändert FORTRAN zunächst den ersten
Index, während C mit dem letzten Index anfängt. Ein Feld x mit 2 × 2
Elementen wird also bei FORTRAN in der Reihenfolge x(1,1), x(2,1),
x(1,2), x(2,2) und in C in der Reihenfolge x[0][0], x[0][1], x[1][0],
x[1][1] abgespeichert.
Auch hier sagt ein Beispielprogramm mehr als längliche Ausführungen:
1
2
3
******************************************************************
* FORTRAN-Programm, das eine C-Routine aufruft
*
******************************************************************
4
5
program fc1_f
6
7
real*8
x(2,2)
8
9
10
11
12
13
do n1 = 1, 2
do n2 = 1, 2
x(n1,n2) = -n1-2*n2
enddo
enddo
14
15
call fc2c(x)
16
17
18
19
20
21
do n1 = 1, 2
do n2 = 1, 2
write(6,*) n1, n2, x(n1,n2)
enddo
enddo
22
23
24
STOP
end
Dieses FORTRAN-Programm ruft eine in C geschriebene Routine auf:
1
2
3
/*------------------------------------------------------------C-Routine, die von einem FORTRAN-Programm aufgerufen wird
-------------------------------------------------------------*/
272
D Fortran und C
4
5
6
void fc2c_(x)
double x[2][2];
7
8
9
{
int n1, n2;
10
11
12
13
14
15
for (n1=0; n1<2; n1++)
for (n2=0; n2<2; n2++) {
printf(" %i %i %f\n", n1, n2, x[n1][n2]);
x[n1][n2] = n1+2*n2;
}
16
17
18
return;
}
Die Übersetzung der beiden Programmteile erfolgt analog zum vorhergehenden Beispiel und die Ausführung des Gesamtprogramms liefert:
> fc2_f
0 0 -3.000000
0 1 -4.000000
1 0 -5.000000
1 1 -6.000000
1 1 0.
1 2 1.
2 1 2.
2 2 3.
Die ersten vier Zeilen sind die Ausgabe der C-Routine, d.h. die Indizierung
beginnt C-gemäß jeweils bei null, die Werte sind jedoch noch diejenigen, die
der Matrix im Hauptprogramm zugewiesen wurden, also z.B. für das erste
Element −1 − 2 × 1 = −2 (die Indizes sind bei FORTRAN ja jeweils 1).
Bei den letzten vier Zeilen verhält es sich genau umgekehrt: die Indizes entsprechen dem FORTRAN-Standard (beginnen also jeweils bei 1), die
Werte sind jedoch noch aus der C-Routine, beginnen also mit 0 + 2 × 0 = 0.
Außerdem erkennen sie in den mittleren beiden Zeilen dieses Blocks die Verdrehung, die durch die bereits erwähnte bei FORTRAN und C verschiedene
Reihenfolge im Abspeichern kommt. Ansonsten würde man ja bei n1=1 und
n2=2 als Wert (1 − 1) + 2 × (2 − 1) = 2 erwarten, und entsprechend sollte in
der vorletzten Zeile (2 − 1) + 2 × (1 − 1) = 1 stehen.
Bis jetzt haben wir uns auf C-Funktionen vom Typ void beschränkt, die
aus FORTRAN-Sicht wie eine subroutine aufgerufen werden. C-Funktionen
hingegen, die einen Wert zurückgeben, entsprechen in FORTRAN einer
function und werden dementsprechend eingesetzt. Auch hierzu wieder ein
kleines Beispiel:
1
2
3
******************************************************************
* FORTRAN-Programm, das eine C-Routine aufruft
*
******************************************************************
4
5
program fc3_f
6
7
real*8
x, y, func
D Fortran und C
273
8
9
10
11
x = 1.d0
y = func(x)
write(6,*) x, y, sin(x) / x
12
13
14
STOP
end
Dieses Programm ruft eine C-Funktion func auf, sin(x)/x berechnet:
1
2
3
/*------------------------------------------------------------C-Routine, die von einem FORTRAN-Programm aufgerufen wird
-------------------------------------------------------------*/
4
5
#include <math.h>
6
7
double func_(double *x)
8
9
10
{
double y;
11
12
13
14
y = sin(*x) / (*x);
return y;
}
Zum Vergleich gibt das aufrufende FORTRAN-Programm in Zeile 11 nicht
nur den Wert von f(x) aus, sondern berechnet selbst sin(x)/x für x = 1.
Anhand der Ausgabe
> fc3_f
1. 0.841470985
0.841470985
können wir uns davon überzeugen, dass beide Werte übereinstimmen.
E Filterprogramme
Filter nennt man in der Informatik einfache, kleine Programme, die eine immer wieder auftretende Aufgabe so lösen, dass das Programm ein möglichst
breites Einsatzgebiet hat. Besondere Bedeutung haben solche Filter unter
Unix gewonnen, da hier auf der Kommandozeile die Ausgabe eines Kommandos direkt als Eingabe für ein neues Kommando verwendet werden kann,
was man als Verkettung bezeichnet. Am Verständlichsten wird das an dem
folgenden Beispiel.
Das Filterprogramm head gibt die ersten Zeilen der übergebenen Datei
an:
> head faden_lib.cpp
/**************************************************************************
* Name:
faden_lib.cpp
*
* Zweck:
Simuliert die Dynamik eines Fadenpendels
*
* Gleichung: (dˆ2/d tˆ2) phi + sin(phi) = 0
*
* verwendete Bibiliothek: GSL
*
**************************************************************************/
#include <iostream>
#include <fstream>
#include <gsl/gsl_matrix.h>
Die oben erwähnte Möglichkeit der Verkettung von Kommandos erlaubt auch,
die Ausgabe eines beliebigen Programms auf die ersten Zeilen zu beschränken,
z.B. zeigt der folgende Aufruf die zehn neuesten Dateien:
ls -t | head
Der vordere Teil ls -t liefert eine chronologische Liste der Dateien, die mittels des sogenannten Pipe-Symbols | in den zweiten Teil head umgelenkt
wird. Dieser zweite Teil filtert nun aus der Dateiliste die ersten zehn Zeilen
heraus, die – wegen deren chronologischer Ordnung – die zehn neuesten Dateien enthalten. Hinter diesen Filtern steht eine Philosophie, die konträr zur
Philosophie von DOS-Kommandos ist, nämlich dass solche Aufgaben nicht
durch Optionen für das eigentliche Kommando (in diesem Fall ls) zu lösen
sind, sondern durch universell einsetzbare Filter.
Es gibt zwei Gründe, warum wir diesen Filtern einen speziellen Abschnitt
widmen: Zum einen gibt es eine ganze Reihe von Standardfiltern, deren
Kenntnis einem auch bei der Auswertung von Ergebnisdateien helfen kann.
In diesem Zusammenhang ist es sicherlich hilfreich, dass wir diese Filter hier
– im Gegensatz zu Informatik-Lehrbüchern – speziell im Umfeld zu numerischer Physik vorstellen. Zum anderen finden Sie auf der CD einige spezielle
Filter für numerische Probleme, die ich Ihnen zur Verfügung stellen möchte.
Sollte Ihnen bei einem der Standardfilter entfallen sein, wie er verwendet
wird, erhalten Sie bei den meisten eine Hilfestellung, wenn Sie den Filter mit
der Option --help aufrufen. UNIX-Anwendern stehen darüber hinaus die
manual pages zur Verfügung, die man durch den Aufruf
276
E Filterprogramme
man head
angezeigt bekommt. Bei den von mir selbst entwickelten Filtern erfolgt eine
Kurzerklärung der Syntax, wenn Sie den Filter nicht mit der richtigen Zahl
an Argumenten, also z.B. ganz ohne Argumente, aufrufen.
Die Standardfilter more und less
Der Filter more wird sehr häufig gebraucht – nämlich immer dann, wenn
die Ausgabe eines Kommandos nicht auf eine Bildschirmseite passt und man
verhindern muss, dass die Ausgabe vorbeirauscht, bevor man Zeit hatte sie
zu lesen. Genau dies leistet nämlich dieser Filter: die Ausgabe wird angehalten, sobald eine Bildschirmseite voll ist, bis man durch Drücken der Leertaste
signalisiert, dass man den Inhalt zur Kenntnis genommen hat und man zur
nächsten Seite wechseln möchte. Das zuerst entwickelte more hatte einen
Nachteil: man konnte in der Bildschirmanzeige immer nur vorwärts blättern,
nicht aber wieder zurück. Dieses Manko behebt der Nachfolger, der ironischerweise less getauft wurde: mittels der Tasten Page Up und Page Down lässt
sich beliebig in der Ausgabe hin und herblättern. Darüber hinaus ermöglicht
less auch die Suche nach einer Zeichenkette.
Die Standardfilter head und tail
Den Filter head haben wir bereits in der Einleitung zu diesem Abschnitt
kennen gelernt. Er hat eine Reihe von Optionen, von denen aber zumindest
ich immer nur eine gebraucht habe:
> head -8
gibt nur die ersten 8 (statt standardmäßig 10) Zeilen des Arguments aus.
Alternativ hat head --lines=8 denselben Effekt.
Der Filter tail ist das Pendant zu head und konzentriert sich anstatt
auf den Anfang des Arguments auf dessen Ende. Der Aufruf ls -t | tail
liefert also die 10 ältesten Dateien. Die Optionen von tail sind zu denen von
head völlig analog, so dass z.B. tail -50 die letzten 50 Zeilen liefert.
Beide Filter, head und tail, können nicht nur dazu herangezogen werden, den Anfang bzw. das Ende großer Ergebnisdateien schnell anzuschauen
(ohne die gesamte Datei erst in einen Editor zu laden), sondern können auch
dazu verwendet werden, ohne großen Aufwand die Ausgabe eines Programms
auf deren Anfang bzw. Ende zu beschränken, was insbesondere bei der Fehlersuche hilfreich ist.
E Filterprogramme
277
Der Standardfilter grep
Der Filter grep durchsucht sein Argument auf Zeilen, die eine bestimmte
Zeichenkette enthalten, z.B. liefert
> grep "#" faden_lib.res
alle Zeilen der Ergebnisdatei faden lib.res, die ein # enthalten – also jene
Zeilen, die die Programmparameter enthalten. Dieses Programm können sie
z.B. zur Aufbereitung von Datensätzen verwenden, bei denen die Bedeutung
der einzelnen Werte durch Schlüsselworte festgelegt wird, z.B.
T=0.0000
X=0.0000
V=1.0000
A=0.0000
=============
T=1.0000
X=1.5000
V=2.0000
A=1.0000
=============
...
In diesem Fall liefert Ihnen
> grep "T=" result.dat > result_t.dat
aus der ursprünglichen Datei result.dat eine neue Datei result t.dat, die
nur noch die Zeitinformationen enthält.
Interessant ist die Option -v, die die Suchbedingung quasi invertiert.
> grep -v "#" faden_lib.res
liefert also alle Zeilen der Ergebnisdatei faden lib.res, die kein # enthalten.
Der Standardfilter cut
Kommen wir auf das Beispiel im vorigen Abschnitt zurück, in dem wir mittels
grep aus der Datei result.dat die Datei result t.dat erzeugt haben. Diese
sieht nun etwa so aus:
T=0.0000
T=1.0000
T=2.0000
...
Störend bei der Weiterverarbeitung sind für die meisten Grafikprogramme die Zeilenanfänge T=; eigentlich brauchen wir nur die Zahlen selbst. Diese
Aufgabe können wir mit dem Filter cut lösen, der aus Zeilen einen bestimmten Teil herausschneidet. Ein sogenannter Begrenzer (englisch Delimiter) legt
fest, wo eine Spalte endet und die nächste anfängt: in unserem Beispiel können
wir das Gleichheitszeichen = als Delimiter festlegen, dann steht in der ersten
Spalte immer der Buchstabe T und in der zweiten Spalte (auf der anderen
Seite des Delimiters) die numerischen Werte der Zeiten. Der Aufruf
278
E Filterprogramme
> cut -d"=" -f2 result_t.dat > result2_t.dat
liefert uns also eine Datei, die nur noch die Zeiten selbst – ohne voranstehenden Bezeichner – enthält. Die Option -d legt dabei den Delimiter fest,
während die Option -f2 besagt, dass lediglich die zweite Spalte ausgegeben
werden soll.
Der Standardfilter wc
Das Kürzel wc steht für word count, also Wortzähler. Entgegen dieser Bedeutung liefert dieser Filter jedoch nicht nur die Zahl der Wörter, sondern auch
die der Buchstaben und die der Zeilen:
> wc faden_lib.cpp
127
399
3505 faden_lib.cpp
Die erste Zahl ist die Zahl der Zeilen, die zweite die Zahl der Wörter
und schließlich folgt die Zahl der Buchstaben. Durch die Optionen -m (für
Buchstaben), -w (für Wörter) und -l (für Zeilen) lässt sich die Ausgabe auf
einen der drei Werte einschränken.
Dieser Filter ist zum einen hilfreich, wenn Sie sich schnell einen Überblick
über den Umfang eines Datenbestandes machen wollen. In dem Beispiel von
oben können wir mittels dieses Kommandos auch überprüfen, ob der Ausgangsdatensatz vollständig ist, und ob unsere Verarbeitung bis hin zur Datei
result t2.dat korrekt war. Für die Überprüfung der Vollständigkeit geben
wir die Kommandos
> grep "T="
1000
> grep "X="
1000
> grep "V="
1000
> grep "A="
999
result.dat | wc -l
result.dat | wc -l
result.dat | wc -l
result.dat | wc -l
ein. Wenn – wie hier – die Ergebnisse nicht alle übereinstimmen, ist mindestens einer der Blöcke mit Zeit, Ort, Geschwindigkeit und Beschleunigung
nicht komplett. In diesem Beispiel fehlt in einem Block die Beschleunigung.
Um zu überprüfen, ob die Weiterverarbeitung korrekt verlief, verwenden
wir wieder den Filter wc:
> wc -l result_t2.dat
1000
Das Ergebnis muss mit dem entsprechenden Ergebnis der Ausgangsdatei
result.dat übereinstimmen, sonst sind irgendwo Zeilen verlorengegangen.
E Filterprogramme
279
Der Standardfilter sort
In vielen Fällen sind Ergebnisdateien nicht so sortiert, wie dies für eine bestimmte Darstellung erforderlich wäre. In einem Beispiel, das mit dem bisher
besprochenen eng verwandt ist, haben wir eine Datei res.dat mit vier Spalten, in denen Zeit, Ort, Geschwindigkeit und Beschleunigung eines Teilchens
festgehalten sind:
0.0000 0.0000 1.0000 0.0000
1.0000 1.5000 2.0000 1.0000
...
Diese Datei hat bereits das Format, mit dem jedes Grafikprogramm zurechtkommen müsste, so dass wir ohne Probleme z.B. den Ort als Funktion
der Zeit darstellen können. Wenn wir aber die Beschleunigung als Funktion
des Ortes haben möchten (z.B. um die an diesen Orten wirkende Kraft festzulegen), müssen wir die Datei nach aufsteigenden Orten x sortieren. Dies
erledigt das Kommando sort:
sort -k2 -n res.dat > res_sorted.dat
Die Option -k2 legt fest, dass nach der zweiten Spalte sortiert werden
soll. Dabei wird standardmäßig angenommen, dass Spalten durch Leerzeichen voneinander getrennt sind, sie können diesen Delimiter jedoch durch die
Option -t ändern. Die zweite oben verwendete Option ist -n, die bewirkt,
dass die Sortierung numerisch (und nicht alphabetisch) erfolgt – andernfalls
wäre nämlich 10 vor 1 statt dahinter.
Die Filter concat und enumerate
Im Folgenden werden wir einige Filter vorstellen, die nicht Bestandteil von
Unix sind, sondern die speziell für die Anforderungen der Numerik geschrieben und auf die Struktur unserer Ergebnisdateien abgestimmt wurde. Sie
finden diese Filter im Quellcode und als lauffähige Binaries auf der CD im
Verzeichnis filter. Alle Filter dienen der Weiterverarbeitung von Ausgabedateien, von denen angenommen wird, dass sie folgendermaßen aufgebaut
sind:
–
–
Am Programmanfang stehen einige Kommentarzeilen, die mit einem !
oder mit einem # beginnen. Diese Kommentarzeilen werden von den Filtern unverändert übernommen. Um die Änderung durch den Filter selbst
nachvollziehen zu können wird eine Kommentarzeile hinzugefügt, die den
Filteraufruf mit allen Argumenten protokolliert.
Im eigentlichen Datenteil sind die Werte in Spalten angordnet. Dieser
Bereich wird durch den Filter bearbeitet und eventuell in modifizierter
Form in einer Ausgabedatei abgespeichert.
280
E Filterprogramme
Im Gegensatz zu den oben vorgestellten Standardfiltern erfolgt bei diesen numerischen Filtern die Ausgabe nicht auf dem Bildschirm sondern in eine Datei
– da die Filter jedoch im Quellcode vorliegen, können Sie das leicht ändern,
was insbesondere für Linux-Anwender interessant ist, da dann mehrere Filter
aneinander gekettet werden können. Aber auch wenn Sie die bash-Shell von
djgpp benutzen, können Sie von dieser Möglichkeit Gebrauch machen.
Bevor wir zu den spezifisch numerischen Filtern kommen, stelle ich Ihnen
an dieser Stelle zwei eigene Filter vor, die zwar eigentlich keine numerischen
Aufgaben ausführen, aber wohl nur im numerischen Umfeld benötigt werden,
und deswegen nicht zu den unter Unix standardmäßig verfügbaren Filtern
gehören.
Die Aufgabe von concat ist sehr einfach. Es liest jeweils eine Zeile aus
zwei Eingabedateien ein und gibt diese durch ein Leerzeichen getrennt in
einer einzigen Zeile wieder aus. Aus den Dateien
Fest gemauert in der Erden
Steht die Form, aus Lehm gebrannt.
Heute muss die Glocke werden.
Frisch Gesellen, seid zur Hand.
und
Sein oder nicht sein?
das ist hier die Frage...
ob’s edler im Gemuet,
die Pfeil’ und Schleudern
würde dieser Filter also
Fest gemauert in der Erden Sein oder nicht sein?
Steht die Form, aus Lehm gebrannt. das ist hier die Frage...
Heute muss die Glocke werden. ob’s edler im Gemuet,
Frisch Gesellen, seid zur Hand. die Pfeil’ und Schleudern
generieren, was ziemlichen Unsinn darstellt. Im Fall von Zahlen kann eine solche Zusammenziehung aus zwei getrennten Dateien jedoch durchaus sinnvoll
sein. Als Beispiel greifen wir wieder auf die Ergebnisdatei der vorangegangen Abschnitte zurück, aus der wir mittels verschiedener Filter eine Datei
result t2.dat generiert haben, die nur noch die Zeiten enthält. Auf demselben Weg können wir uns natürlich auch eine zweite Datei result x2.dat
erzeugen, die nur noch die Orte enthält. Unsere Grafikprogramme erwarten
jedoch eine Datei, in der Zeit und Ort jeweils paarweise enthalten sind. Dies
erzeugt uns nun der Filter concat:
> concat result_t2.dat result_x2.dat > result_t_x.dat
Es gibt unter Unix den Standardfilter paste, der praktisch die selbe Funktion wie concat hat. Allerdings behandelt er die Kommentarzeilen genauso
wie den Datenbereich und fügt nicht in die Ausgabe den Vermerk ein, dass
diese Datei aus zwei Ergebnisdateien zusammengefügt wurde.
E Filterprogramme
281
Der Filter enumerate überspringt wie concat die Kommentarzeilen und
stellt jeder nachfolgenden Zeile eine fortlaufende Nummer, beginnend bei
eins, voran.
Der numerische Filter minmax
Das kleine Programm minmax gibt einen Schnellüberblick über eine Zahlenreihe, die als Spalte in einer Datei steht. Als Argumente erwartet minmax die
Datei, in der die Daten stehen, die Zahl der Spalten und die Nummer der
Spalte, in der die zu analysierenden Daten stehen. Die Ausgabe von minmax
umfasst die Zahl der Werte, den Wertebereich (also den minimalen und maximalen Wert), den Mittelwert und die Standardabweichung:
> minmax hysterese.res 3 2
Zahl der Datensaetze : 10000
Minimum
: -2
Maximum
: 2
Mittelwert : 0.00504067
Standardabw.: 1.41576
Die numerischen Filter translate und scale
Die Filter in diesem und den beiden folgenden Abschnitten werden von
geübten awk-Programmierern nicht benötigt, da sie die entsprechenden Modifikationen ebenso schnell in awk ausgeführt haben. Mit Hilfe von translate
und awk können Sie zu allen Werten einer bestimmten Spalte eine feste Zahl
hinzuaddieren bzw. alle Werte einer Spalte mit einer Zahl multiplizieren.
Wenn z.B. in Spalte 2 einer Ausgabedatei result1.res mit insgesamt 5
Spalten die Temperatur in Grad Celsius steht, erzeugt
> translate result1.res result2.res 5 2 -273.2
eine Datei result2.res, bei der diese Temperatur in Kelvin angegeben wird.
Die anderen Spalten, sowie die Kommentarzeilen bleiben unverändert.
Ganz analog erzeugt
> scale result1.res result2.res 5 3 1/3600
eine Datei, bei der die Zeit in der dritten Spalte von Sekunden in Stunden
umgerechnet wurde. Unter DOS müssen Sie den Wert von 1/3600 zunächst
berechnen und dann dezimal angeben.
282
E Filterprogramme
Die numerischen Filter accumulate und differentiate
In vielen Fällen erhalten wir Daten, die wir vor der Auswertung noch akkumulieren bzw. differenzieren müssen. Wenn wir die Werte der auszuwertenden
Spalte mit
(E.1)
x1 , x2 , x3 , . . . , xi , . . . , xN
bezeichnen, liefert das Programm accumulate
i
xn ,
(E.2)
x2 − x1 , x3 − x2 , . . . , xi − xi−1 , . . . , xN − xN −1
(E.3)
x1 , x1 + x2 , x1 + x2 + x3 , . . . ,
n=1
xn , . . .
N
n=1
während differentiate daraus
macht. Beachten Sie, dass differentiate die Zahl der Datensätze um eins
verringert.
Als Argumente erwarten sowohl accumulate als auch differentiate die
Namen der Ein- und Ausgabedatei, die Zahl der Spalten, sowie die Nummer
der zu bearbeitenden Spalte – alle anderen Spalten bleiben unverändert.
Die numerischen Filter add, subtract, multiply und
divide
Diese vier Filter addieren, subtrahieren, multiplizieren oder dividieren die
Werte aus zwei Spalten und hängen das Ergebnis an die jeweilige Zeile an. Bei
diesen vier Filtern kommt also eine neue Spalte zu unserer Datei hinzu. Der
Aufruf ist analog zu accumulate oder differentiate, nur dass bei diesen
Filtern zwei zu bearbeitende Spalten angegeben werden müssen, z.B.
> subtract result1.res result2.res 5 2 1
Bei der so erzeugten Datei result2.res enthält die neu hinzugekommene
sechste Spalte den Wert x2 − x1 , wobei x1 und x2 die Zahlen in der ersten
bzw. zweiten Spalte sind.
Die numerischen Filter polyfit und polynom
Besonders bei der Weiterverarbeitung von Messwerten müssen diese häufig
an ein bestimmtes Modell gefittet werden, d.h. alle freien Parameter des Modells müssen so bestimmt werden, dass die Abweichung zwischen Modell und
Messwerten möglichst klein ist. Bei einem Polynomfit ist das Modell ein Polynom definierter Ordnung N
E Filterprogramme
fModell (x) =
N
an xn
283
(E.4)
n=0
und die Parameter a0 , a1 , . . . , aN sind die freien Parameter. Unser Filter
polyfit erledigt diese Aufgabe und erwartet als Argumente eine Datei mit
den Wertepaaren, den Namen der Ausgabedatei und die Ordnung N . Als
Ergebnis liefert der Filter die Koeffizienten a0 bis aN .
> poly_fit result.res mod_koeff.dat 3
Während polyfit aus Wertepaaren die (bestmöglichen) Koeffizienten berechnet, macht polynom den umgekehrten Schritt: bei gegebenen Koeffizienten a0 bis aN berechnet es die Modellkurve. Dazu braucht es neben der Datei
mit den Koeffizienten noch die Parameter xmin , xmax und die Zahl M der
Stützstellen, an denen der Polynomwert berechnet werden soll. Als Ergebnis
erhält man M Wertepaare mit x und f (x).
> polynom mod_koeff.dat result.res -10 10 101
erzeugt eine Datei result.res, in der die Werte des Polynoms im Intervall
[−10, 10] an 101 äquidistanten Punkten, also jeweils im Abstand 0.2, zu finden
sind.
Der numerische Filter mod diff
Der Filter mod diff vergleicht einen Datensatz mit einem Polynom-Modell
f (x) =
N
an xn
(E.5)
n=0
und berechnet für jedes Wertepaar (x, y) die Abweichung y−f (x) vom Modell.
Als Parameter erwartet mod diff eine Datei, die die Koeffizienten ai des
Modells beginnend bei a0 enthält, eine Datei mit den Wertepaaren (x, y),
den Namen der Ausgabedatei und die Ordnung N des Polynommodells:
> mod_diff mod_koeff.dat result.res 3
Der numerische Filter reduce
Der letzte Filter, den ich hier vorstellen möchte, dient der Reduzierung großer
Datensätze. Häufig enthalten Ergebnisdateien eine Datenfülle, die letztlich
nicht gebraucht wird. Wenn wir diese Datensätze vor der graphischen Darstellung verkleinern, sparen wir Rechenzeit und vor allem Speicherplatz, da
die entstehenden Grafiken wesentlich kleiner sind, ohne an Aussagekraft zu
verlieren. Der Filter reduce erledigt diese Aufgabe, indem er vom ursprünglichen Datensatz nur jeden n-ten übernimmt, wobei der Parameter n beim
Programmaufruf übergeben wird.
284
E Filterprogramme
> reduce result1.res result2.res 10
reduziert den Datenbestand der Datei result1.res also auf ein Zehntel und
schreibt das Ergebnis in result2.res.
F Fouriertransformation und FFT-Routinen
Eine gute mathematische Darstellung von Fourierreihen und der Fouriertransformation finden Sie in [35, 36] sowie in Teil II von [37].
Die meisten der in C vorgestellten mathematischen Bibliotheken stellen sogenannte FFT-Routinen (von Fast Fourier Transform, also schnelle
Fouriertransformation) zur Verfügung. In diesem Anhang wollen wir zeigen,
wie Sie diese Routinen einsetzen müssen, um die Fouriertransformierte einer
Funktion zu erhalten.
Als Ausgangssituation betrachten wir eine Funktion f , die wir an N äquidistanten Stützstellen xk kennen. Wie dies in vielen Anwendungen der Fall
ist, wollen wir annehmen, dass diese Stützstellen symmetrisch um null verteilt
liegen: x−N/2 , x−N/2+2 , . . . , xN/2−1 . Die Funktionswerte an diesen Stützstellen bezeichnen wir mit
fk = f (xk ) = f (k ∆x).
Was wir benötigen, ist die Fouriertransformierte
fˆ(y) = dxf (x) exp(−ixy),
(F.1)
(F.2)
oder wenigstens die Werte der Fouriertransformierten an einem endlichen
Satz von Stützstellen y−N/2 , y−N/2+1 , . . . yN/2−1 :
fˆj = fˆ(yj ) = fˆ(j∆y) .
(F.3)
Da wir die Ausgangsfunktion nur an diskreten Stützstellen kennen, müssen
wir das Integral (F.2) durch eine Summe ersetzen – eine Näherung die nur
sinnvoll ist, wenn die Stützstellen hinreichend dicht liegen, so dass sich die
Funktion f zwischen zwei Stützstellen nur wenig ändert, und wenn die Funktion f außerhalb des von den Stützstellen abgedeckten Bereichs annähernd
null ist. Unter diesen Voraussetzungen erhalten wir:
N/2−1
fˆj ≈ ∆x
fk exp(−iyj xk )
(F.4)
k =−N/2
=
N
−1
f (xk−N/2 ) exp(−ij(k − N/2)∆y∆x)
(F.5)
k=0
= (−1)j
N
−1
f (xk−N/2 ) exp(−ijk∆y∆x)
(F.6)
k=0
Dies ist im Wesentlichen das, was eine FFT-Routine liefert, nämlich
286
F Fouriertransformation und FFT-Routinen
gj =
N
−1
fk exp(−2πijk/N ) ,
(F.7)
k=0
wobei die fk die Werte sind, die wir an die Routine als Eingangsdatensatz
übergeben, während die gj das Ergebnis der FFT darstellen. Wenn wir dies
mit (F.6) vergleichen, so erhalten wir Übereinstimmung, wenn
und
fˆj = gj
2π
∆y =
N ∆x
(F.8)
(F.9)
ist und wir den Faktor (−1)j berücksichtigen.
Abschließend müssen wir uns noch überlegen, wie wir die Fouriertransformierte bei negativen y bekommen. Der Schlüssel hierzu ist die Tatsache,
dass für alle ganzen n
exp(−2πin) = 1
(F.10)
gilt. Wir können also diesen Faktor in (F.6) einfügen, wobei wir 2π mittels
(F.9) als
2π = N ∆x∆y
(F.11)
schreiben:
fˆj = (−1)j
N
−1
f (xk−N/2 ) exp(−ijk∆y∆x) exp(−inN ∆x∆x) (F.12)
k=0
= fˆj+nN .
(F.13)
Die Tatsache, dass wir die Ausgangsfunktion nur an diskreten und äquidistanten Stützstellen kennen, führt also zu einer (scheinbaren) Periodizität der
Fouriertransformierten. Wir gehen jedoch davon aus, dass letztere in Wirklichkeit nur im Intervall [−N/2∆y, N/2∆y] merklich von null verschieden ist,
und wir die Werte im negativen Teil dieses Intervalls in den fN/2 , fN/2+1 ,
. . . fN finden, wozu wir n = 1 in (F.13) einsetzen müssen.
Fouriertransformierte in mehreren Dimensionen
Liegt eine Funktion von mehreren unabhängigen Variablen vor, z.B.
f (x1 , x2 , x3 ) ,
(F.14)
so kann man die Fouriertransformation für alle Variablen durchführen:
1
ˆ
f (y1 , y2 , y3 ) =
dx1 dx2 dx3 f (x1 , x2 , x3 )
(F.15)
(2π)3/2
(F.16)
× exp (i(x1 y1 + x2 y2 + x3 y3 ) .
F Fouriertransformation und FFT-Routinen
287
Es ist offensichtlich, dass man diese Fouriertransformation nach x1 , x2 und
x3 als eine Verkettung von Transformationen auffassen kann, die jeweils nur
eine der drei Variablen betrifft:
1
f1 (y1 , x2 , x3 ) = √
dx1 f (x1 , x2 , x3 ) exp (ix1 y1 )
(F.17)
2π
1
dx2 f1 (y1 , x2 , x3 ) exp (ix2 y2 )
(F.18)
f2 (y1 , y2 , x3 ) = √
2π
1
dx3 f2 (y1 , y2 , x3 ) exp (ix3 y3 ) .
(F.19)
fˆ(y1 , y2 , y3 ) = √
2π
Diese Sichtweise hilft uns eine FFT in mehreren Dimensionen durchzuführen, auch wenn die verwendete Numerik-Bibliothek nur FFT-Routinen
für eine Dimension zur Verfügung stellt. Zunächst führen wir die Transformation (F.17), dann (F.18) und schließlich (F.19) durch, die uns die gesuchte
Fouriertransformierte fˆ liefert.
G Die GPL-Lizenz
Aus lizenzrechtlichen Gründen geben wir hier den Text der GNU General Public License in Englisch wieder. Übersetzungen in andere Sprachen finden Sie
auf der Internetseite http://www.gnu.org/licenses/translations.html;
diese haben jedoch im Zweifelsfall keinerlei rechtliche Relevanz.
The GNU General Public License
Version 2, June 1991
c 1989, 1991 Free Software Foundation, Inc.
Copyright 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Everyone is permitted to copy and distribute verbatim copies of this license document, but
changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share and change it.
By contrast, the GNU General Public License is intended to guarantee your freedom to share
and change free software—to make sure the software is free for all its users. This General Public
License applies to most of the Free Software Foundation’s software and to any other program
whose authors commit to using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public
Licenses are designed to make sure that you have the freedom to distribute copies of free software
(and charge for this service if you wish), that you receive source code or can get it if you want
it, that you can change the software or use pieces of it in new free programs; and that you know
you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights
or to ask you to surrender the rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must
give the recipients all the rights that you have. You must make sure that they, too, receive or
can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license
which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author’s protection and ours, we want to make certain that everyone understands
that there is no warranty for this free software. If the software is modified by someone else and
passed on, we want its recipients to know that what they have is not the original, so that any
problems introduced by others will not reflect on the original authors’ reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the
danger that redistributors of a free program will individually obtain patent licenses, in effect
making the program proprietary. To prevent this, we have made it clear that any patent must
be licensed for everyone’s free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
Terms and Conditions For Copying, Distribution and
Modification
0. This License applies to any program or other work which contains a notice placed by
the copyright holder saying it may be distributed under the terms of this General Public
License. The “Program”, below, refers to any such program or work, and a “work based
on the Program” means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it, either verbatim or with
modifications and/or translated into another language. (Hereinafter, translation is included
without limitation in the term “modification”.) Each licensee is addressed as “you”.
290
G Die GPL-Lizenz
Activities other than copying, distribution and modification are not covered by this License;
they are outside its scope. The act of running the Program is not restricted, and the output
from the Program is covered only if its contents constitute a work based on the Program
(independent of having been made by running the Program). Whether that is true depends
on what the Program does.
1. You may copy and distribute verbatim copies of the Program’s source code as you receive
it, in any medium, provided that you conspicuously and appropriately publish on each copy
an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that
refer to this License and to the absence of any warranty; and give any other recipients of
the Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may at your option
offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus forming a
work based on the Program, and copy and distribute such modifications or work under the
terms of Section 1 above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices stating that you changed
the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in part
contains or is derived from the Program or any part thereof, to be licensed as a whole
at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you must
cause it, when started running for such interactive use in the most ordinary way, to
print or display an announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide a warranty) and that
users may redistribute the program under these conditions, and telling the user how
to view a copy of this License. (Exception: if the Program itself is interactive but does
not normally print such an announcement, your work based on the Program is not
required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable sections of that
work are not derived from the Program, and can be reasonably considered independent
and separate works in themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you distribute the same
sections as part of a whole which is a work based on the Program, the distribution of the
whole must be on the terms of this License, whose permissions for other licensees extend
to the entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to work
written entirely by you; rather, the intent is to exercise the right to control the distribution
of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the Program
(or with a work based on the Program) on a volume of a storage or distribution medium
does not bring the other work under the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object
code or executable form under the terms of Sections 1 and 2 above provided that you also
do one of the following:
a) Accompany it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a medium customarily
used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give any third
party, for a charge no more than your cost of physically performing source distribution,
a complete machine-readable copy of the corresponding source code, to be distributed
under the terms of Sections 1 and 2 above on a medium customarily used for software
interchange; or,
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution
and only if you received the program in object code or executable form with such an
offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all
modules it contains, plus any associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a special exception, the
source code distributed need not include anything that is normally distributed (in either
source or binary form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component itself accompanies
the executable.
If distribution of executable or object code is made by offering access to copy from a
designated place, then offering equivalent access to copy the source code from the same
G Die GPL-Lizenz
4.
5.
6.
7.
8.
9.
10.
291
place counts as distribution of the source code, even though third parties are not compelled
to copy the source along with the object code.
You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the
Program is void, and will automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this License will not have their
licenses terminated so long as such parties remain in full compliance.
You are not required to accept this License, since you have not signed it. However, nothing
else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying
or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or
modifying the Program or works based on it.
Each time you redistribute the Program (or any work based on the Program), the recipient
automatically receives a license from the original licensor to copy, distribute or modify the
Program subject to these terms and conditions. You may not impose any further restrictions
on the recipients’ exercise of the rights granted herein. You are not responsible for enforcing
compliance by third parties to this License.
If, as a consequence of a court judgment or allegation of patent infringement or for any
other reason (not limited to patent issues), conditions are imposed on you (whether by
court order, agreement or otherwise) that contradict the conditions of this License, they do
not excuse you from the conditions of this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any other pertinent obligations,
then as a consequence you may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by all those who receive
copies directly or indirectly through you, then the only way you could satisfy both it and
this License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended
to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property
right claims or to contest validity of any such claims; this section has the sole purpose of
protecting the integrity of the free software distribution system, which is implemented by
public license practices. Many people have made generous contributions to the wide range
of software distributed through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing to distribute software
through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of
the rest of this License.
If the distribution and/or use of the Program is restricted in certain countries either by
patents or by copyrighted interfaces, the original copyright holder who places the Program
under this License may add an explicit geographical distribution limitation excluding those
countries, so that distribution is permitted only in or among countries not thus excluded. In
such case, this License incorporates the limitation as if written in the body of this License.
The Free Software Foundation may publish revised and/or new versions of the General
Public License from time to time. Such new versions will be similar in spirit to the present
version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies a version
number of this License which applies to it and “any later version”, you have the option of
following the terms and conditions either of that version or of any later version published
by the Free Software Foundation. If the Program does not specify a version number of this
License, you may choose any version ever published by the Free Software Foundation.
If you wish to incorporate parts of the Program into other free programs whose distribution
conditions are different, write to the author to ask for permission. For software which
is copyrighted by the Free Software Foundation, write to the Free Software Foundation;
we sometimes make exceptions for this. Our decision will be guided by the two goals of
preserving the free status of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
No Warranty
11. Because the program is licensed free of charge, there is no warranty for the program,
to the extent permitted by applicable law. Except when otherwise stated in writing the
copyright holders and/or other parties provide the program “as is” without warranty
of any kind, either expressed or implied, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose. The entire risk as
292
G Die GPL-Lizenz
to the quality and performance of the program is with you. Should the program prove
defective, you assume the cost of all necessary servicing, repair or correction.
12. In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the program as
permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of the use or inability to use the program
(including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the program to operate with any
other programs), even if such holder or other party has been advised of the possibility
of such damages.
End of Terms and Conditions
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to the public,
the best way to achieve this is to make it free software which everyone can redistribute and
change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of
each source file to most effectively convey the exclusion of warranty; and each file should have
at least the “copyright” line and a pointer to where the full notice is found.
one line to give the program’s name and a brief idea of what it does.
Copyright (C) yyyy name of author
This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
330, Boston, MA 02111-1307, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it starts in an interactive
mode:
Gnomovision version 69, Copyright (C) yyyy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’.
This is free software, and you are welcome to redistribute it under certain conditions;
type ‘show c’ for details.
The hypothetical commands show w and show c should show the appropriate parts of the General
Public License. Of course, the commands you use may be called something other than show w
and show c; they could even be mouse-clicks or menu items—whatever suits your program.
You should also get your employer (if you work as a programmer) or your school, if any, to sign
a “copyright disclaimer” for the program, if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
‘Gnomovision’ (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking
proprietary applications with the library. If this is what you want to do, use the GNU Library
General Public License instead of this License.
Literaturverzeichnis
1. M. Galassi et al: GNU Scientific Library Reference Manual, 2. Auflage (Network Theory Ltd, Bristol 2003)
2. T. Fließbach: Lehrbuch zur Theoretischen Physik, Band 1–4 3.–4. Auflage
(Spektrum Akademischer Verlag, Heidelberg 1999–2003)
3. W. Nolting: Grundkurs Theoretische Physik Band 1-7, 4.–5. Auflage (Springer,
Berlin Heidelberg New York 2001–2004)
4. R. J. Jelitto: Theoretische Physik, Band 1-6, 2.–4. Auflage (Aula Verlag, Wiesbaden 1987–1995)
5. J. D. Jackson: Klassische Elektrodynamik 3. Auflage (de Gruyter Verlag, Berlin
New York 2001)
6. M. Born, E. Wolf und A. B. Bhatia: Principles of Optics, 7. Auflage (Cambridge University Press, Cambridge 1999)
7. H. Rollnik: Quantentheorie, 2. Auflage (Springer, Berlin Heidelberg New York
2003)
8. W. Greiner et al: Theoretische Physik, Band 1–11 1. –7. Auflage (Verlag Harri
Deutsch, Frankfurt a. Main 1991–2004)
9. J. M. Thijssen: Computational Physics, 3. Auflage (Cambridge University
Press, Cambridge 2001)
10. R. H. Landau und M. J. Paez: Computational Physics: Problem Solving with
Computers, 1. Auflage (John Wiley & Sons, New York 1997)
11. P. L. DeVries: A First Course in Computational Physics, 1. Auflage (John
Wiley & Sons, New York 1994)
12. S. E. Koonin und D. C. Meredith: Computational Physics, 1. Auflage (AddisonWesley, Bonn 1997)
13. Marvin L. Dejong: Introduction to Computational Physics, 1. Auflage
(Addison-Wesley, Bonn 1991)
14. R. Peyret: Handbook of Computational Fluid Mechanics 1. Auflage (Academic
Press, Orlando 2000)
15. D. A. Anderson, J. C. Tannehill und R. H Pletcher: Computational Fluid Mechanics and Heat Transfer 1. Auflage (Taylor & Francis Press, London New
York Philadelphia Singapur 1984)
16. J. D. Anderson: Computational Fluid Dynamics 2. Auflage (McGraw-Hill,
1997)
17. T. J. Chung: Computational Fluid Dynamics 1. Auflage (Cambridge University
Press, Cambridge 2002)
18. I. Newton: Principia (1687)
19. K. H. Hoffmann, M. Schreiber (eds): Computational Statistical Physics 1. Auflage (Springer, Berlin Heidelberg New York 2002)
294
Literaturverzeichnis
20. W. Nolting: Grundkurs Theoretische Physik 2, 6. Auflage (Springer, Berlin
Heidelberg New York 2004)
21. F. Scheck: Theoretische Physik 1, 7. Auflage (Springer, Berlin Heidelberg New
York 2003)
22. W. Greiner: Mechanik, Teil 2, 6. Auflage (Verlag Harri Deutsch, Frankfurt
a. Main 1998)
23. A. Einstein: Annalen der Physik 17, 891 (1905)
24. E. Durand: Electrostatique et magnétostatique 1. Auflage (Masson, Paris 1953)
25. Ch. Kittel: Einführung in die Festkörperphysik 12. Auflage (R. Oldenbourg
Verlag, München Wien 1999)
26. B. Œksendal: Stochastic Differential Equations, 5. Auflage (Springer, Berlin
Heidelberg New York 1998)
27. G. G. Emch und C. Liu: The Logic of Thermostatistical Physics, 1. Auflage
(Springer, Berlin Heidelberg New York 2002)
28. W. Nolting: Grundkurs Theoretische Physik 5/1, 6. Auflage (Springer, Berlin
Heidelberg New York 2004)
29. W. Nolting: Grundkurs Theoretische Physik 5/2, 5. Auflage (Springer, Berlin
Heidelberg New York 2004)
30. C. Cohen-Tannoudji, B. Diu, F. Laloë: Quantenmechanik, Teil 2 2. Auflage
(Walter de Gruyter, Berlin 1999)
31. Teubner-Taschenbuch der Mathematik, 1. Auflage (B. G. Teubner Verlag,
Stuttgart Leipzig 1996)
32. C. Ginzel et al: Physical Review A 48, 732 (1993)
33. W. P. Schleich: Quantum Optics in Phase Space, 1. Auflage (Wiley-VCH Berlin
2001)
34. M. O. Scully und M. S. Zubairy: Quantum Optics 1. Auflage (Cambridge
University Press, Cambridge 1997)
35. R. V. Churchill: Fourier Series and Boundary Value Problems, 5. Auflage
(McGraw-Hill, New-York 1993)
36. F. B. Hildebrand: Advanced Calculus for Applications, 2. Auflage (PrenticeHall, Englewood Cliffs 1976)
37. W. I. Smirnow: Lehrgang der höheren Mathematik, Teil I–V, 1. Auflage (Deutscher Verlag der Wissenschaften, Berlin 1967)
Index
Aberration 111
Absorbtion 251
Astigmatismus 143
Äther 71, 99
Attraktor 54
Attraktor
seltsamer 55
Attraktordiagramm 60
Basis 196
Beschreibung
stroboskopische 244
Beugungsmuster 129
Bewegungsgleichung 3
Bifurkation 61
Bildfeldwölbung 143
BLAS 266
Boltzmann-Konstante 237
Boltzmann-Verteilung 191
Boltzmannfaktor 237
Brechung 101, 105
Brechungsgesetz
Snelliussches 102
Brownsche Bewegung 152
CBLAS 266
CERNLIB 266
Delta-Potential 254
Diamagnetismus 97
Dichtematrix 233
Dichteoperator 233
Dielektrizitätskonstante 72
Dipol 73
Dokumentation
unter Linux 259
unter Windows 263
Drei-Niveau-System 247
Dreipunktformel 204
Dynamik
integrable 32
Eigenwert 196
Eigenwertgleichung 196
Eigenwertgleichung
verallgemeinerte 220
Eigenzustände
des Hamiltonoperators
Eigenzustand 195
Ein-Atom-Laser 246
Emission 251
Entartung 196
Entropie 234
213
FAQ 260, 264
Feldlinien 76
Fernfeld 73
Freies Teilchen 200
Fresnelsche Formeln 103
Gegenkraft 1
Gesetz
Hookesches 2
von Biot-Savart 80
Gesetze
Newtonsche 1
Gnu Scientific Library 20
GNU Scientific Library 265
Gravitationsgesetz 2
Grenzzyklus 54
Hamilton-Formalismus 52
Hamiltonfunktion 200
Hamiltonoperator 200
Hamiltonsche Gleichungen 52
Hamiltonsches System 52
Heisenbergbild 200
296
Index
Mastergleichung 153
Mastergleichung
quantenmechanische 234
Mathematisches Pendel 4
MATPACK 20, 266
Maxwellsche Gleichungen 71
Mean-Field-Näherung 93
Messprozess 241
Messung 199
Metrik 40
Moment 152
Monopol 73
Helmholtz-Spule 96
Hermitesche Polynome 216
Hilbertraum 193
Huygenssches Prinzip 128
Hysterese 72, 92
Impulsdarstellung 197
Impulsoperator 197, 198
IMSL 265
Inertialsystem 2
Intensität
von Licht 125
Interferenz 127
Interferenz
destruktive 127
konstruktive 127
Interferenzterm 127
Inversion 249
NAG 265
Newsgroup 260, 264
Newtonsche Gesetze 1
Normerhaltung 208
Normierungsbedingung 194
Nullpunktsfluktuationen 233
Kohärenz 134
Kohärenzfunktion 136
Koma 143
Kommutator 195
konjugiert
kanonisch 52, 195
Koordinaten
generalisierte 26
Kopenhagener Interpretatation
Korrelation 150
Korrelationsfunktion 151
Lagrangeformalismus 26
Lagrangefunktion 26
Lagrangeparameter 26
Laser 246
Legendre-Transformation 52
Lichttheorie
Huygenssche 99
Newtonsche 99
Linse
asphärische 105
sphärische 105
Linsenfehler 111
Liouvillescher Satz 54
Lyapunov-Exponent 41
Magnetisierung 72
Markov-Approximation
Massenpunkt 1
237
199
Objekthöhe 106
Observable 199
Öffnungsfehler 143
Operator 194
Operator
Erzeugungs- 251
Hermitescher 196
Vernichtungs- 251
Ortsdarstellung 197
Ortsoperator 197, 198
Oszillator
harmonischer 216
Pendel
Doppel- 26
Faden- 3
mathematisches 4
Permeabilitätskonstante 72
Photon 100, 251
Plancksches Wirkungsquantum
Poincaré-Plot 32
Polarisation 72
Potential
optisches 208
Potentialbarriere 226
Potentialmulde 226
Potentialtopf 208
Potenzreihe 5
Projektion 199
100
Index
Umklappoperatoren
Pumpen 249
Punktladung 72
Variable
dynamische 32
Variationsmethoden
in der Quantenmechanik
Variationsprinzip
Ritzsches 219
Verbundwahrscheinlichkeit
Verzeichnung 143
Quadrupol 73
Quantenfeldtheorie 233
Quantenoptik 100
Quantenstatistik 233
Quantentunneln 226
Randbedingungen
periodische 204
Random Walk 153
Reflektion 101
Remaneszenz 97
Reservoir 166
rpm-Paket 257
Runge-Kutta-Verfahren
23
Schrödingerbild 200
Schrödingergleichung 199
Separatrix 14
Sinai-Billard 50
skleronom 52
SLATEC 20, 266
Spin 235
Spur 233
Stoßparameter 167
Strahlenoptik 100
Überlappmatrix
220
236
Wärmebad 236
Wahrscheinlichkeit
Wahrscheinlichkeit
bedingte 150
Wellen
ebene 126
Kugel- 126
Wellenpaket 204
147
Zeno-Effekt 242, 244
Zerfall
spontaner 233
Zufallskomponente 199
Zustand 193
Zustand
gemischter 233
reiner 233
Zustandsvektor 193
Zwei-Niveau-System 235
219
150
297
Herunterladen