Nächste Seite: Die Qt-Benutzeroberfläche Aufwärts: Die praktische Realisierung in Vorherige Seite: Generische Programmierung und Templates   Inhalt


Klassenstrukturen zur Simulation

Im Folgenden werden einige Klassen des Programms genauer betrachtet. Den entsprechenden Quellcode findet man in Anhang D.

Im ersten Teil des Programms wurde ausgiebig von den Templates Gebrauch gemacht; so können z. B. die drei Integrationsalgorithmen sowohl zur Integration von normalen Zahlen als auch, wie dann in anderen Programmteilen verwendet, zur Integration von Vektoren herangezogen werden. Auch der Datentyp der den Vektoren und Tensoren zugrunde liegenden Skalare wird nicht von vornherein festgelegt; im Programm wird später zwar double verwendet, es würde sich aber ohne große Änderungen auch ein benutzerdefinierter Datentyp verwenden lassen, der z. B. eine größere Genauigkeit bietet.

Gehen wir zunächst noch etwas genauer auf die Integrationsalgorithmen ein. Sie sind alle von der im Header grav_numerical.h (siehe Abschnitt D.2) definierten Klasse numerical abgeleitet, die wie alle Klassen des ersten Programmteils im Namensraum grav steht. Die Klasse numerical definiert die grundlegende Schnittstelle der Integrationsalgorithmen: Es wird ein Konstruktor festgelegt, dem man die Anfangswerte für Position und Geschwindigkeit sowie ein Funktionsobjekt übergibt, das die Beschleunigung in Abhängigkeit von Position und Geschwindigkeit berechnet. Weiterhin werden Funktionen get_x() und get_v() zum Auslesen der momentanen Werte von Position und Geschwindigkeit sowie eine Funktion iterate() definiert. Diese Funktion ist virtuell, da sie erst von den abgeleiteten Klassen mit Inhalt versorgt wird; ihr wird eine Schrittweite übergeben, sie führt dann einen Integrationsschritt mit dem betreffenden Algorithmus aus. Je nach Algorithmus ist die Implementation dieser Funktion natürlich unterschiedlich; die Klassen euler_cauchy (siehe Abschnitt D.3), runge_kutta (siehe Abschnitt D.4) und adams_stoermer (siehe Abschnitt D.5) setzen jeweils die Verfahren aus Abschnitt 7.1 um. Dabei ist für die Klasse adams_stoermer noch auf eine Besonderheit hinzuweisen: Da das Verfahren nicht selbststartend ist, werden zunächst vier Iterationen mit dem Runge-Kutta-Algorithmus durchgeführt, um die nötigen Startwerte zu erhalten (siehe auch Abschnitt 7.1.3).

Kommen wir nun zu den Datenstrukturen, die spezieller auf unser Problem zugeschnitten sind; beginnen wir mit der Klasse vector (siehe Abschnitt D.6). Sie heißt genauso wie eine Klasse der Standardbibliothek [21, S. 471-488]; dies stellt aber kein Problem dar, da die Standardbibliothek den Namensraum std benutzt während unsere Klasse, wie oben schon angesprochen, auf den Namensraum grav zurückgreift. Neben der Bereitstellung von Speicherplatz für die Komponenten des Vektors kümmert sich die Klasse vector vor allem auch um die Überladung wichtiger Operatoren. Dies ist dafür wichtig, dass man die Schreibweise für Vektoroperationen genauso wählen kann wie bei den Operationen mit den eingebauten Datentypen. Nur so können wir unsere Integrationsalgorithmen ohne Änderung für beides verwenden.

Aufbauend auf dem Vektor wurde die Klasse metric (siehe Abschnitt D.7) geschrieben. Sie ist nichts weiter als ein Vektor der Dimension $n^2$, auf dem zusätzlich ein ()-Operator definiert wurde, der zwei Vektoren als Argument nimmt und den Wert der Anwendung der Metrik auf die beiden Vektoren zurückliefert.

Weiterhin ist zur Berechnung von Geodäten eine kovariante Ableitung nötig. Diese könnte zwar prinzipiell aus der Metrik berechnet werden; da diese Berechnung aber ziemlich aufwändig wäre und ohne einen Programmteil für symbolische Differentiation ohnehin nur näherungsweise (numerisch) ausgeführt werden könnte, muss die kovariante Ableitung hier unabhängig von der Metrik definiert werden. Dies geschieht durch die Christoffel-Symbole, die mit der Klasse christoffel (siehe Abschnitt D.8) eine C++-Repräsentation erhalten. Ähnlich wie bei der Metrik wird auch hier wieder der Funktionsoperator () überladen. Dieses Mal liefert er zu zwei Vektoren einen Beschleunigungsvektor zurück (siehe Gleichung 3.28).

Als nächstes betrachten wir die Klasse space_time (siehe Abschnitt D.9). Diese Klasse stellt keine eigene Funktionalität bereit, sondern trifft Vorkehrungen für Funktionen, die zu jedem Punkt einer Raumzeit die entsprechenden Werte von Metrik und Christoffel-Symbolen zurückliefern. Die Klasse ist als Basisklasse zum Ableiten anderer Klassen gedacht. Das Programm macht hiervon auch Gebrauch: space_time ist Basis-Klasse der Klassen rotating3 (siehe Abschnitt D.12), schwarzschild3 (siehe Abschnitt D.13), schwarzschild4 (siehe Abschnitt D.14), kerr3 (siehe Abschnitt D.15) und kerr4 (siehe Abschnitt D.16).

Eine interessante Aufgaben erfüllen die von xv_field abgeleiteten Klassen (siehe Abschnitt D.10: Sie nehmen ein Objekt der space_time-Klasse und stellen einen Funktionsoperator bereit, der aus einer Position und einer Geschwindigkeit mit Hilfe der Christoffel-Symbole der gegebenen space_time-Klasse einen Beschleunigungsvektor berechnet. Dies hat zweierlei Nutzen: Zum einen kann eine xv_field-Klasse als Funktionsobjekt für eine numerical-Klasse genutzt werden, zum anderen kann die xv_field-Klasse Modifikationen am Beschleunigungswert vornehmen, wie sie z. B. bei der Parametrisierung nach der Koordinatenzeit (siehe Abschnitt 7.2.2) nötig sind. Damit ergeben sich zwei Kindklassen der xv_field-Klasse: Die Klasse xv_time_field übernimmt die Parametrisierung nach der Koordinatenzeit, während die Klasse xv_affine_field für affine Parameter wie z. B. die Eigenzeit geeignet ist.

Eine Klasse, die keine eigentliche Funktionalität bereitstellt, jedoch die Übersichtlichkeit des Programms beträchtlich erhöht, ist die Klasse falling_body (siehe Abschnitt D.11). Sie stellt eine einheitliche Schnittstelle zu den numerical-Algorithmen, den space_time-Raumzeiten und den xv_field-Klassen her. Dem Konstruktor eines Objekts der falling_body-Klasse wird ein xv_field, Anfangswerte für Position und Geschwindigkeit sowie ein Algorithmentyp übergeben. Über die Methode iterate() kann dann ein Schritt des entsprechenden Algorithmus ausgeführt werden. Zusätzlich stellt falling_body Methoden zur Abfrage der momentanen Bewegungsdaten des Körpers zur Verfügung.

Die restlichen Klassen sind nur Ableitungen von space_time, die aus programmiertechnischer Sicht wenig interessant sind. Interessant ist jedoch die mathematische Seite, die wir jedoch bereits an anderer Stelle behandelt haben.




Nächste Seite: Die Qt-Benutzeroberfläche Aufwärts: Die praktische Realisierung in Vorherige Seite: Generische Programmierung und Templates   Inhalt
FAQ Homepage