Solving Error Accumulation in Physics Simulation Program

AI Thread Summary
The discussion revolves around a program designed to simulate gravity among point particles, facing significant issues with error accumulation that leads to unrealistic particle behavior and erratic center of mass movement. The user employs the gravitational force equation and an iterative method for updating particle velocities and positions but experiences rapid divergence from expected results.Key points include the use of the gravitational force formula to calculate acceleration for each particle pair, followed by updating velocities and positions using a trapezoidal integration method. Despite the refined equations, the simulation fails to maintain stability, prompting inquiries into potential solutions.Suggestions for improvement focus on implementing a predictor-corrector algorithm to enhance numerical integration accuracy, emphasizing the need for smaller time steps (Δt) to mitigate errors. Additionally, periodic checks on the system's total energy are recommended to ensure conservation principles are upheld, allowing for adjustments if energy levels fluctuate. Overall, the discourse highlights the challenges of simulating gravitational interactions and the importance of numerical methods in achieving stable results.
Kynnath
Messages
17
Reaction score
0
I'm trying to write a program that will simulate basic gravity on a set of point particles. The problem I'm having is the program accumulates a lot of error very quickly, the particles start gaining speed and soon they're off to weird places. I can also track how the center of mass of the system starts moving all over the palce. Which shouldn't happen.

Not sure why error grows so quickly, though. I'm using the same equations I've seen in other simulators (actually a bit more refined, which should have led to less error I thought). But still they fly off in every direction.

The equations I'm using are:

Fg = G * ( mi * mj ) / ( d(i,j)^2 )

for the force of gravity, from which I get acceleration by dividing Fg by mi for the particle i and by mj for particle j. I run this for every pair of particles.

I add up the acceleration from each interaction, and use that to modify the velocity of the particle. I also store the acceleration from the previous iteration.

v(t) = v(t-1) + ( a(t) + a(t-1) ) / 2

and from this I get the new position for the particle

p(t) = p(t-1) + ( v(t) + v(t-1) ) / 2

The code itself is

Code:
void nSistema::updateSistema1 ( )
{
	long double G = 0.0000000001 ;
	nLista<nVector<long double> > ainicial ; //Lista para almacenar las aceleraciones de la vuelta previa para cada objeto

	centrodemasa_.setAceleracion( nVector<long double> () ) ;

	for ( unsigned int i = 0 ; i < cuerpos_ ; ++i )
	{
		nCuerpo * cuerpoi ( sistema_.getObjeto( i+1 ) ) ;
		if ( i == 0 ) // en la primer pasada tiene que guardarse la aceleracion previa y borrarse para calcular la nueva
		{
			ainicial.addObjetoEnd( cuerpoi->getAceleracion() ) ;  //aceleracion previa
			cuerpoi->setAceleracion( nVector<long double> () ) ;  //setear a a 0 para el nuevo ciclo
		}

		for ( unsigned int j = i+1 ; j < cuerpos_ ; ++j )
		{
			nCuerpo * cuerpoj ( sistema_.getObjeto( j+1 ) ) ;
			if ( i == 0 )// en la primer pasada tiene que guardarse la aceleracion previa y borrarse para calcular la nueva
			{
				ainicial.addObjetoEnd( cuerpoj->getAceleracion() ) ;
				cuerpoj->setAceleracion( nVector<long double> () ) ;
			}
			nVector<long double> ij ( cuerpoj->getPosicion() - cuerpoi->getPosicion() ) ;
			long double d2 ( ij.getNorma2() ) ;
			long double d ( sqrt( d2 ) ) ; std::cout<< "d ( " << i+1 << " a " << j+1 << " ) = " << d << std::endl ;

			nVector<long double> Fg ;
			if ( d > 0.1 ) Fg = ij * G * ( cuerpoj->getMasa() * cuerpoi->getMasa() ) / ( d2 * d ) ;
			else Fg = nVector<long double> ( ) ;

			cuerpoi->setAceleracion( cuerpoi->getAceleracion() + ( Fg / cuerpoi->getMasa() ) ) ;
			cuerpoj->setAceleracion( cuerpoj->getAceleracion() - ( Fg / cuerpoj->getMasa() ) ) ;

		}
		nVector<long double> a0 ( * ainicial.getObjeto( i+1 ) ) ;
		//updatear v de cuerpoi ;
		//nVector<long double> test ( cuerpoi->getAceleracion() ) ;
		//std::cout << "test[" << i+1 << "] : a = ( " << test.getX() << " , " << test.getY() << " , " << test.getZ() << " )" << std::endl ;
		nVector<long double> v0 ( cuerpoi->getVelocidad() ) ;
		cuerpoi->setVelocidad( v0 + ( cuerpoi->getAceleracion() + a0 ) / 2 ) ;

		//updatear p de cuerpoi ;
		cuerpoi->setPosicion( cuerpoi->getPosicion() + ( cuerpoi->getVelocidad() + v0 ) / 2 ) ;

		centrodemasa_.setPosicion( cuerpoi->getPosicion() ) ;
	}
	centrodemasa_.setPosicion( centrodemasa_.getPosicion() / cuerpos_ ) ;

}

I use a few classes I made myself, since the basis of the project is to get a bit of practice. In case they help, I have the definitions right here. I'd appreciate anyone who could offer some insight into why I'm having this problem, or how to fix it.

Code:
#ifndef _nCuerpo_h_
#define _nCuerpo_h_

#include "nVector.h"

class nCuerpo
{
	public :
		nCuerpo ( ) ;
		nCuerpo ( const long double & , const nVector<long double> & , const nVector<long double> & , const nVector<long double> & ) ;
		nCuerpo ( const nCuerpo & ) ;
		~nCuerpo ( ) ;
		const long double getMasa ( ) const ;
		const nVector<long double> getVelocidad ( ) const ;
		const nVector<long double> getPosicion ( ) const ;
		const nVector<long double> getAceleracion ( ) const ;
		void setMasa ( const long double & ) ;
		void setVelocidad ( const nVector<long double> & ) ;
		void setPosicion ( const nVector<long double> & ) ;
		void setAceleracion ( const nVector<long double> & ) ;

		const bool operator == ( const nCuerpo & ) const ;
		const bool operator != ( const nCuerpo & ) const ;

	protected :

	private :
		long double masa_ ;
		nVector<long double> velocidad_ ;
		nVector<long double> posicion_ ;
		nVector<long double> aceleracion_ ;
};


#endif // _nCuerpo_h_

Code:
#ifndef _nLista_h_
#define _nLista_h_

#include "nNodoLista.h"

template < typename T >
class nLista
{
	public :
		nLista ( ) ;
		nLista ( const nLista<T> & ) ;
		~nLista ( ) ;

		void addObjeto ( const T & ) ;
		void addObjetoEnd ( const T & ) ;
		void removeObjeto ( ) ;
		// void removeObjetoAtras ( ) ;
		const bool findObjeto ( const T & ) const ;
		// const bool removeObjeto ( const T & ) ;
		T * getObjeto ( const unsigned int = 1 ) const ;
		const unsigned int getCantidad ( ) const ;

	protected:

	private:
		nNodoLista<T> * primero_ ;
		unsigned int cantidad_ ;

		void deleteNodo ( nNodoLista<T> * ) ;

};


#endif // _nLista_h_

Code:
#ifndef _nNodoLista_h_
#define _nNodoLista_h_

template < typename T >
class nNodoLista
{
	public :
		nNodoLista ( ) ;
		nNodoLista ( const T & ) ;
		nNodoLista ( const nNodoLista<T> & ) ;
		~nNodoLista ( ) ;
		T * getObjeto ( ) ;
		void setSiguiente ( nNodoLista<T> * ) ;
		nNodoLista<T> * getSiguiente ( ) ;

	protected :

	private :
		T dato_ ;
		nNodoLista<T> * siguiente_ ;
};


#endif // _nNodoLista_h_

Code:
#ifndef _nSistema_h_
#define _nSistema_h_

#include "nCuerpo.h"
#include "nLista.h"

class nSistema
{
	public :
		nSistema ( ) ;
		nSistema ( const nSistema & ) ;
		~nSistema ( ) ;

		void addCuerpo ( const nCuerpo & ) ;
		void updateSistema1 ( ) ;
		//unsigned int cuerpos ( ) { return cuerpos_ ; } ;
		void drawSistema1 ( ) const ;

	protected :

	private :
		nLista<nCuerpo> sistema_ ;
		nCuerpo centrodemasa_ ;
		unsigned int cuerpos_ ;
};

#endif // _nSistema_h_

Code:
#ifndef _nVector_h_
#define _nVector_h_

template < typename T >
class nVector
{
	public :
		const static int NVECTORSIZE = 3 ;
		nVector ( ) ;
		nVector ( const T & , const T & , const T & ) ;
		nVector ( const nVector<T> & ) ;
		~nVector ( ) ;

		nVector<T> & operator = ( const nVector<T> & ) ;
		nVector<T> & operator += ( const nVector<T> & ) ;
		nVector<T> & operator -= ( const nVector<T> & ) ;
		nVector<T> & operator *= ( const T & ) ;
		nVector<T> & operator /= ( const T & ) ;
		const nVector<T> operator + ( const nVector<T> & ) const ;
		const nVector<T> operator - ( const nVector<T> & ) const ;
		const nVector<T> operator * ( const T & ) const ;
		const nVector<T> operator / ( const T & ) const ;
		const T operator * ( const nVector<T> & ) const ;
		const nVector<T> Xprod ( const nVector<T> & ) const ;

		const bool operator != ( const nVector<T> & ) const ;
		const bool operator == ( const nVector<T> & ) const ;

		const T getNorma2 ( ) const ;
		const T getX ( ) const ;
		const T getY ( ) const ;
		const T getZ ( ) const ;

	protected :

	private :
		T data_ [ NVECTORSIZE ] ;
};


#endif // _nVector_h_
 
Technology news on Phys.org
The key here is the numerical integration used to calculate velocities and positions, which in turn are used to calculate acclerations for each time step. An iterative corrector method will improve the results. In the algorithm shown below, an, vn, and pn, are successive "guesses" that should converge quickly. F(...) calculates the acceleration based on pn(t), and Δt is the elapsed time per step. You may want to do 6 to 8 interations instead of the 4 shown in this example. The first step is essentially Euler (since a1(t) is set = F(p0(t)) (= a(t-1)), the remaining steps are trapezoidal. Even though each step of this algorithm will converge to a specific set of values, the algorithm is based on trapezoidal rule, a linear approximation, so you need to use small time steps (Δt).

v0(t) = v(t-1)
p0(t) = p(t-1)

a1(t) = F(p0(t)) (= a(t-1))
v1(t) = v(t-1) + 1/2 (a(t-1) + a1(t)) Δt
p1(t) = p(t-1) + 1/2 (v(t-1) + v1(t)) Δt

a2 = F(p1(t))
v2(t) = v(t-1) + 1/2 (a(t-1) + a2(t)) Δt
p2(t) = p(t-1) + 1/2 (v(t-1) + v2(t)) Δt

a3 = F(p2(t))
v3(t) = v(t-1) + 1/2 (a(t-1) + a3(t)) Δt
p3(t) = p(t-1) + 1/2 (v(t-1) + v3(t)) Δt

a4 = F(p3(t))
v4(t) = v(t-1) + 1/2 (a(t-1) + a4(t)) Δt
p4(t) = p(t-1) + 1/2 (v(t-1) + v4(t)) Δt

...

v(t) = vn(t)
p(t) = pn(t)
a(t) = F(pn(t))

time += Δt
t += 1

This is a predictor-corrector type algorithm:

http://en.wikipedia.org/wiki/Predictor-corrector_method#Euler_trapezoidal_example
 
Last edited:
Thanks! I'll give that a try, see how the program reacts and how many steps I'd need. I'd tried to figure decent ways to correct the error, such as trying to calculate the particle's energy at a given point and cut speed accordingly to keep energy constant, but hadn't gotten to figuring out how that would work.
 
http://en.wikipedia.org/wiki/Gravity_assist"
 
Last edited by a moderator:
Right. Energy of the system doesn't change, not energy of each individual particle. As I said, was just an idea, didn't think it through.
 
Kynnath said:
ways to correct the error
If you use the integration method I described in my previous post and keep the time step (Δt) small enough, you shouldn't have an issue with errors with a reasonable number of orbital cycles (100 to 1000). As mentioned you could calculate the total energy of the system every 10 to 100 cycles, and if it's increasing or decreasing, apply a small correction factor to the process.
 
Dear Peeps I have posted a few questions about programing on this sectio of the PF forum. I want to ask you veterans how you folks learn program in assembly and about computer architecture for the x86 family. In addition to finish learning C, I am also reading the book From bits to Gates to C and Beyond. In the book, it uses the mini LC3 assembly language. I also have books on assembly programming and computer architecture. The few famous ones i have are Computer Organization and...
I have a quick questions. I am going through a book on C programming on my own. Afterwards, I plan to go through something call data structures and algorithms on my own also in C. I also need to learn C++, Matlab and for personal interest Haskell. For the two topic of data structures and algorithms, I understand there are standard ones across all programming languages. After learning it through C, what would be the biggest issue when trying to implement the same data...

Similar threads

Replies
36
Views
3K
Replies
23
Views
2K
Replies
3
Views
2K
Replies
17
Views
2K
Replies
31
Views
3K
Replies
35
Views
4K
Replies
1
Views
1K
Replies
89
Views
5K
Back
Top