Dies ist eine alte Version des Dokuments!
← zurück zur Liste der Komponenten
INFO: Soweit nicht anders angegeben, sind die Code-Bestandteile auf dieser Seite aus den jeweiligen Header-Dateien.
Die Klasse Vector3D ist die Grundlage für alle Größen der Berechnungen wie Positionen, Geschwindigkeiten, Beschleunigungen und Kräfte.
Unser Vector3D bietet folgende Funktionalitäten:
class Vector3D { private: // DEKLARATION DER ATTRIBUTE double x,y,z; public: // KONSTRUKTOREN Vector3D(); Vector3D(double, double, double); // GETTER double getX() const; double getY() const; double getZ() const; // SETTER void setX(double); void setY(double); void setZ(double); // OPERATORUEBERLADUNGEN Vector3D operator+(const Vector3D &a) const; Vector3D operator-(const Vector3D &a) const; Vector3D operator*(const double) const; Vector3D operator/(double) const; bool operator== (const Vector3D) const; // SONSTIGE FUNKTIONEN double betrag(); double skalarprodukt(Vector3D &a); double winkel(Vector3D &a); double abstand (const Vector3D &a); Vector3D kreuzprodukt(Vector3D &a) const; double spatprodukt(Vector3D &a, Vector3D &b); Vector3D norm(); std::string asString() const; };
Unsere Objects dienen der Aufbewahrung der Werte, die für die Berechnungen wichtig sind sowie einen Namen zur Identifikation.
#include "vector3d.hpp" class Object { private: // DEKLARATION DER ATTRIBUTE std::string name; Vector3D pos; // Position Vector3D vel; // Velocity Vector3D acc; // Acceleration double vol; // Volume double den; // Density double mas; // Mass bool fix; // is fixed? public: // KONSTRUKTOREN Object (std::string); Object (Vector3D, Vector3D); Object (Vector3D, Vector3D, Vector3D); // GETTER std::string getName() const; Vector3D getPos() const; Vector3D getVel() const; Vector3D getAcc() const; double getVol() const; double getDen() const; double getMass() const; bool isFixed() const; // SETTER void setPos (Vector3D); void setVel (Vector3D); void setAcc (Vector3D); void setVol (double); void setDen (double); void setMass(double); void setFix (bool); // OPERATOR OVERLOADINGS bool operator== (Object*) const; };
Unser Universe dient als Container für Objects. Im Prinzip funktioniert es wie ein std::vector mit besseren Zugriffsmöglichkeiten.
#include "object.hpp" class Universe { private: // DEKLARATION DER ATTRIBUTE std::vector <Object*> objects; public: // KONSTRUKTOREN Universe (); // DESTRUKTOR ~Universe (); // GETTER std::vector <Object*> getObjects() const; Object* getObject (std::string) const; // SONSTIGE FUNKTIONEN void add (Object*); unsigned int size() const; void remove (Object*); std::string asString (); };
Die size()-Methode gibt die Anzahl der Objects zurück, die sich aktuell in diesem Universe befinden.
Die Simulation ist das Herzstück des C++-Teils. Hier laufen die Berechnungen ab, deren Ergebnisse später angezeigt werden.
Hier sind das Euler- und das Runge-Kutta-Verfahren implementiert, wobei wir aktuell nur letzteres tatsächlich nutzen.
#include "universe.hpp" class Simulation { private: // DEKLARATION DER ATTRIBUTE double dt; const double stdG = 0.0002958935421; double G; Universe* unvrs; public: // KONSTRUKTOR Simulation (Universe*); Simulation (Universe*, double); // FUNKTIONEN void setG (double); void setDT (double); void simulate (); void simulate (unsigned int); // simuliert n Schritte void simulate_rk4 (); void simulate_rk4 (unsigned int); // simuliert n Schritte // Euler-Verfahren Vector3D updateForce (unsigned int) const; void updateAcc (); void updateVel (); void updatePos (); // Runge-Kutta-Verfahren void updateVel_rk4 (); void updatePos_rk4 (); };
Die Hauptmethode ist simulate_rk4(). Hier werden die Beschleunigungen (und Kräfte) berechnet, daraus die Geschwindigkeiten und daraus die Positionen.
Dazu ein paar Ausschnitte aus der simulation.cpp:
void Simulation::simulate_rk4 () { updateAcc(); updateVel_rk4(); updatePos_rk4(); }
Bei der Berechnung der Beschleunigungen ist die Berechnung der Kräfte mit eingebunden, denn aus
F=m*a
folgt
a = F/m
void Simulation::updateAcc () { Vector3D newAcc = Vector3D(0.,0.,0.); for (unsigned int i = 0; i < unvrs->size(); i++) { newAcc = (updateForce(i)/unvrs->getObjects().at(i)->getMass()); unvrs->getObjects().at(i)->setAcc(newAcc); } }
Die Kräfte werden nach dieser Formel in updateForce() berechnet.
Vector3D Simulation::updateForce (unsigned int a) const // a ist der Index des Object im Universe { std::vector<Object*> objects = unvrs->getObjects(); std::vector<Vector3D> forces (objects.size(), Vector3D (0.,0.,0.)); for (unsigned int i = 0; i < objects.size(); i++) { if (i != a) // Object wirkt keine Kraft auf sich selbst aus { Vector3D abstand_vec (objects.at(a)->getPos() - objects.at(i)->getPos()); double abstand = objects.at(a)->getPos().abstand(objects.at(i)->getPos()); forces.at(a) = forces.at(a) + (abstand_vec * ((objects.at(a)->getMass() * objects.at(i)->getMass())/(abstand*abstand*abstand))* -G); } } return forces.at(a); }
Mit den berechneten Beschleunigungen kann man nun mithilfe des Runge-Kutta-Verfahrens die Geschwindigkeiten und analog dazu die Positionen berechnen:
void Simulation::updateVel_rk4 () { std::vector<Object*> objects = unvrs->getObjects(); Vector3D newVel = Vector3D(0.,0.,0.); for (unsigned int i = 0; i < objects.size(); i++) { Vector3D k1 = (objects.at(i)->getAcc()) * dt; Vector3D k2 = (objects.at(i)->getAcc() + (k1*0.5)) * dt; Vector3D k3 = (objects.at(i)->getAcc() + (k2*0.5)) * dt; Vector3D k4 = (objects.at(i)->getAcc() + (k3)) * dt; newVel = objects.at(i)->getVel() + ((k1 + k2*2 + k3*2 + k4)/6); objects.at(i)->setVel(newVel); } }
Die Auswertung-Klasse dient dem Sammeln von Daten über das Universum. Hier werden zum Beispiel auch die Positionsdaten der Objects in die Ergebnis-Textdatei geschrieben. Dazu dienen die folgenden Funktionen:
#include "simulation.hpp" class Auswertung { private: // DEKLARATION DER ATTRIBUTE Universe* unvrs; std::vector <std::string> results; public: // KONSTRUKTOR Auswertung(Universe*); // ANDERE METHODEN void clear(); std::string currentState() const; void writeToBuffer(); void writeToFile(); };
Die Funktionalität ist einfach: In jedem Zeitschritt werden die Positionsdaten in den Buffer (hier ein std::vector<std::string>) geschrieben, um das Textdokument nicht permanent öffnen und wieder schließen zu müssen. Sobald eine bestimmte Anzahl an Elementen im Buffer sind (bei uns 50), wird der Inhalt in die Textdatei geschrieben und der Buffer geleert.