Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

ss16:physiksimulation_doc_cpp

Hauptseite des Projekts

Physiksimulation/Dokumentation/Komponenten/

C++-Teil

INFO: Soweit nicht anders angegeben, sind die Code-Bestandteile auf dieser Seite aus den jeweiligen Header-Dateien.

Programmstruktur

vector3d.cpp

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;
};

object.cpp

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;
 
};

universe.cpp

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.

simulation.cpp

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);
    }
}

auswertung.cpp

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.

interface.cpp

Diese Klasse ist dazu da, eine Schnittstelle zum C++-Teil zur Verfügung zu stellen. Mithilfe der Methoden des Interfaces kann man alle relevanten Funktionen des C++-Teils steuern:

#include "auswertung.hpp"
 
class Interface
{
    private:
    // DEKLARATION DER ATTRIBUTE
        Universe*   u;
        Simulation* s;
        Auswertung* a;
 
    public:
    // KONSTRUKTOR
        Interface();
        Interface (double G);
 
    // METHODEN
        void add(std::string, double, double, double, double, double, double, double);
        void modify (std::string, double, double, double, double, double, double, double);
        void run (unsigned int);
        std::string step ();
        void setG (double);
        void setDT (double);
};	

Wie man sieht, sind vor allem die add()- und modify()-Methode sehr unübersichtlich. Deshalb werden wir später, im Cython-Teil, eine besser benutzbare Version davon erstellen. Hier an dieser Stelle vereinfacht es die Benutzung sogar, eine Methode zu haben, die beispielsweise ein Objekt nur im Ganzen hinzufügt.
setG() und setDT() stellen die Parameter für die Berechnung ein: G ist die Gravitationskonstante und DT die Genauigkeit bei der Berechnung.
step() führt einen Berechnungsschritt aus, run() so viele, wie angegeben.

ss16/physiksimulation_doc_cpp.txt · Zuletzt geändert: 2016/08/14 22:11 von markumnus