C# Particle-to-Particle Gravity

Started by
5 comments, last by neoaikon 16 years, 2 months ago
I have been working on creating a particle gravity simulation using C#.. Its just simple Particle2Particle interaction, no optimizations. I’m trying to make it as realistic as possible as possible in terms of simulating gravity, and thus I’m using realistic constants and formulas. G = 6.673e-11f Or G = .00000000006673 I am creating particles of random radius; I use that to calculate the mass by using the following calculation. M = (radius^2)^2 * 1.9e-22f Or M = (radius^2)^2 * 190,000,000,000,000,000,000,000 This multiplies the squared square of the mass with the mass of the moon, giving me my mass. I use the following formula to get the force F=M1A A=G * ((M1*M2) / DISTANCE) Combining the two, we can cancel out the M1, giving us F= A = G * (M2 / DISTANCE) I multiply that with a normalized vector pointing toward the particle in question, and add that into a vector, then after going through all the particles, I add that vector to the position vector to get the new position. I believe I am doing this correctly, or at least believe I am, but the system never really stabilizes and forms orbits and such. My goal is to create small systems that orbit realistically, but what happens is after the particles are spawned (providing no initial velocities have been given), they all migrate towards the center of mass for the simulation, which is how it should be, but the problem is a large portion of the particles will form tight orbits around this center. This causes any particles not part of that mass to be bound to it, and thus no orbiting pairs form, because of this mass. Is there anything else I need to be doing? or am I calculating the gravitational forces correctly? Or is their other forces I need to be taking into account here besides the acceleration due to gravity? Once I have it well commented, I will post the class file I am using, Thanks in advance.
Advertisement
Here and here is a good start for calculating the stability of a 3-body system.

As far as I know, while there are stable solutions to n-body systems, the study of such systems was the basis for chaos theory.
Wow! if I hadn't done half the math I've been learning as of late, seeing that kinda stuff would have killed me. While I've never thought of this as an n-body system, it kind of is. But my concern isn't with calculating the stability of an N body system, but to refine my system so that it tends to fall into stable orbits.

My system isn't very particle intensive, thus why I'm using particle-to-particle interactions. My system can run it up to 500 particles smoothly, but its pretty much chaos at that point, lowering the number of particles increase stability, with 2 particles almost always falling into some sort of stable orbit.

Anything above 10 particles will usually become so mass intensive at the center particles orbiting around this heavier concentration tend to ignore other nearby particles.
Three bodies of near-equal mass are known to be chaotic in their co-gravitation, so anything above 2 bodies in your simulation seems to already be producing realistic behaviour.

Your force calculation seems to be close to correct, but I will rewrite it just to make sure:

F = GMm/r^2
a = F/m = GM/r^2

Multiplying the unit vector pointing from the gravitated body (the one with mass m) to the gravitating body (the one with mass M) by the scalar value a gives the final acceleration vector. This seems to be what you're doing already.

The sum of all gravitation is the sum of all these acceleration vectors. This seems to be what you're doing already.

You then integrate by:
new position vector = old position vector + old velocity vector
new velocity vector = old velocity vector + acceleration vector

Make sure you do not perform the integration backward by updating velocity before position. Things will break.

I'm not sure what behaviour you are expecting.

Why not start with 2 particles, and model a simplified version of the Sun-Earth system:

M = 1.9891e30 kg
m = 5.9736e24 kg (or 0 kg if you want to ignore Earth's gravitational field)
r = 149597887500 m (Earth's semi-major axis -- its average orbit distance)
v = 29789 m/s (Earth's average orbit speed)

This can be begun by placing the Sun at location {0, 0}, and the Earth at {0, 149597887500}. Then, give Earth an initial velocity vector of {-29789, 0}. You should end up with a circular orbit. If you do, then your calculations are correct.

The initial velocity above was gathered by the equation:

v = sqrt(GM/r)

You can pretty much assume that gravitational interaction is the only one worth worrying about at this large scale. Considering that electromagnetic repulsion really only comes into play when you're in direct contact with a body such as Earth or the Moon, you don't have to worry about it until the bodies are touching. Calculating contact points and collision response vectors would be things to look into.

The Sun itself emits a lot more electromagnetic energy, but the pressure is still very slight, and can most likely be ignored until the point of contact. Solar sailing runs on this principle of light pressure.

[Edited by - taby on January 31, 2008 2:23:15 PM]
Quote:Original post by neoaikon
Wow! if I hadn't done half the math I've been learning as of late, seeing that kinda stuff would have killed me. While I've never thought of this as an n-body system, it kind of is. But my concern isn't with calculating the stability of an N body system, but to refine my system so that it tends to fall into stable orbits.


Looking around I found this. There appear to be some number of many-body stable orbit families, but while mathematically stable, they eventually become more chaotic as floating-point rounding errors accumulate.
Aaron, Kudo's on that simulation, although I'm surprised at how fast the floating point errors give rise to chaos in some of them. But it does show lots of stuff that my system does when my system goes into chaos, most notably particles getting stuck together and flying off into nowhere, which I used a cutoff to keep from happening.

Taby, you actually helped me figure out what was going on and why the simulation didn't seem realistic. Also, thanks for evaluating my math, that helps a lot with my confidence. When I tried to recreate an earth body system, I discovered that what was happening was, I was creating particles within a certian range on my screen, that way I can keep everything in the camera's view. The problem with this is I'm using realistic constants, and trying to generate realistic mass. But my particles are usually within a range 1000 units in all directions. so the distance between the particles is less than 1000 meter's in all directions. Not realistic at all.

Earth lies VERY far away from the sun, based on your number. So in the gravity calculation, I multiplied the distance by 100,000,000,000 (GM / (R^2 * 1e-11)). This lead to alot more realistic behavior, since now the force fall off is a lot more dramatic, it allows the particles near each other to begin to orbit and form small systems. Although these orbits and systems usually consist of more than 2 body's, so its just a localized pocket of chaos, but its better than a huge blob in the center like I was getting.

I'm still working on commenting the class file, if anyone would like to see it. But it seems i'm either going to have to calculate the best placement for the particles to form systems, and even then I'd need infinitely long floating point percision to keep it stable for long. Or I'm going to have to find some way of having particles capture other particles, and keep them in a stable orbit, with the latter seeming like a decent solution.

EDIT:
Taby, what did you mean by backwards integration? Currently in my loop, it calls a update function, that runs through the particles and updates the velocity, then after getting done with that, it updates position, then repeats. Is it wrong to do it this way?
For all who are interested, here is the source for the n-body system generator, it seems to be working well. But if anyone spots a way to make it improvement, or an error, let me know!

using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;using System;using System.ComponentModel;using System.Drawing;using System.Windows.Forms;using System.Diagnostics;using System.Net;namespace DirectX{    public class Newton    {        // Newton v1.1        // Author: Mykel Ne'Oa Ikon        // Description: This class is a functional n-body system        //it requires a DirectX Based project that is setup to the point that a blank scene        // is being drawn. You can then invoke the class and use the functions        //        // Add_NBody - adds a n-body to the system        // Delete_NBody - deletes a n-body from the system        // Toggle - by passing true(On) or false (Off) you can cause the particle to be affected by other particles gravity or not, other particles will still be affected by the off particle        Int32 Max = 500; // This is the maximum number of particles we can have through newton               Int32 N = 0; // This is the particle counter, when we add a particle this increments, and decrements when we delete it        n_body[] newton = new n_body[1000]; // Our array for our particle structure        float GC = 6.673e-11f; // Our gravitational constant, .00000000006.673        float MC = 1e+22f; // We multiply this by our radius, double squared.                float GD = 1000000000000; // our galactic distance, makes the force seem like we're at large                // This method allows us to add a particle        public void Add_NBody(Mesh mesh, Vector3 position, Vector3 acceleration, Color diffuse, float radius)        {            if (N < Max)            {                                newton[N].nradius = radius; // Pass the radius                newton[N].nmass = (radius * radius) * MC; // Turn the radius into mass                newton[N].ncutoff = (radius * radius) + radius;// * (newton.nradius + newton.nradius); // Set the gravity cutoff distance                newton[N].nmesh = mesh; // Set the mesh                newton[N].nposition = position; // Set the mesh position                newton[N].nacceleration += acceleration; // Define the acceleration                newton[N].nmaterial.Diffuse = diffuse; // Set the diffuse color                newton[N].ngravity = true; // Turn the particle on                N += 1; // Increase our particle counter            }        }        public void Add_NBody(Mesh mesh, Vector3 position, Vector3 acceleration, Color diffuse, float radius, Boolean status)        {            if (N < Max)            {                newton[N].nradius = radius; // Pass the radius                newton[N].nmass = (radius * radius) * MC; // Turn the radius into mass                newton[N].ncutoff = (radius * radius) + radius;// * (newton.nradius + newton.nradius); // Set the gravity cutoff distance                newton[N].nmesh = mesh; // Set the mesh                newton[N].nposition = position; // Set the mesh position                newton[N].nacceleration += acceleration; // Define the acceleration                newton[N].nmaterial.Diffuse = diffuse; // Set the diffuse color                newton[N].ngravity = status; // Turn the particle on                N += 1; // Increase our particle counter            }        }        // This method allows us to delete a particle        public void Delete_NBody(Int32 ndex)        {            if (N > 0) // Providing we have items to work with            {                for (Int32 x = ndex; x <= N; x++) // loop through each one at the position we want to delete                {                    newton[x] = newton[x + 1]; // And copy the one ahead of it                }                N -= 1; // Decrement our counter            }        }        // This method allows us to turn off/on a particle by passing off(False) or on(True) to it        public void Toggle(Int32 ndex, Boolean status)        {            newton[ndex].ngravity = status;        }        // This method calculates the gravitation on each particle, then updates the position        public void Update()        {            #region process gravity            if (N > 0) // If we have particles to process            {                for (Int32 i = 0; i < N; i++) // Loop through our particles and update the positions                {                    newton.nposition += newton.nacceleration;                }                for (Int32 i = 0; i < N; i++) // Go though our particles                {                    // When we're ready, on this line we will move the position + acceleration bit in the loop above                    for (Int32 ii = 0; ii < N; ii++) // Go through our particles                    {                        if (i != ii) // If the focus(i) and target(ii) particles aren't the same                        {                            if (newton.ngravity != false) // If our particle can calculate gravity                            {                                float d = v3dist(newton.nposition, newton[ii].nposition); // Get the distance between them                                if (v3collision(i,ii, d) == false) // If we're greater than the cutoff distance                                {                                        float g = v3grav(newton[ii].nmass, newton.nposition, newton[ii].nposition); // Get the gravitational force                                        Vector3 vd = v3direction(newton.nposition, newton[ii].nposition, d); // get a vector representing a normalized direction to our target                                        newton.nacceleration += new Vector3(vd.X * g, vd.Y * g, vd.Z * g); // calculate our acceleration                                }                             }                        }                                            }                }            }            #endregion        }        // This method renders our particles        public void Render(Device device)        {            for (Int32 i = 0; i < N; i++) // Go through each particle            {                if (newton.nmass != 0) // Check to make sure it isn't blank                {                    device.Transform.World = Matrix.Translation(newton.nposition); // Position our particle                    device.Material = newton.nmaterial; // Apply its material                    newton.nmesh.DrawSubset(0); // Draw it                }            }        }        // This method calculates the gravity        public float v3grav(float mass, Vector3 object1, Vector3 object2)        {            // this function will calculate true gravity between 2 objects            float dist = (float)v3dist(object1, object2); // Get the distance between the 2 points            float gf = (GC * mass); // Get the gravitational force            return gf / (dist * GD); // Return the gravitational force after dividing it by r2 and scaling it        }        // This method calculates a scalar vector toward our target        public Vector3 v3direction(Vector3 object1, Vector3 object2, float d)        {            float x = (object1.X - object2.X); // Get our x distance            float y = (object1.Y - object2.Y); // Get our y distance            float z = (object1.Z - object2.Z); // Get our z distance            return new Vector3(-(x / d), -(y / d), -(z / d)); // Return our vector        }        // Get the distance between 2 points        public float v3dist(Vector3 object1, Vector3 object2)        {            Double x = (object1.X - object2.X) * (object1.X - object2.X); // Get X            Double y = (object1.Y - object2.Y) * (object1.Y - object2.Y); // Get Y            Double z = (object1.Z - object2.Z) * (object1.Z - object2.Z); // Get Z            float dist = (float)(x + y + z); // Get distance            return dist; // Return distance        }        // Returns true if we have a collision with the two particles in question using sphere bound detection        public Boolean v3collision(Int32 object1, Int32 object2, float dist)        {                        float collisiondistance = newton[object1].ncutoff + newton[object2].ncutoff; // The collision distance is the sum of both particles cutoffs, while not exact it works            if (dist <= collisiondistance) // If we're closer than our colision distance, we're colliding            {                return true; // return true            }            else            {                return false; // return false            }        }        public struct n_body        {            public Mesh nmesh;            public float ncutoff;            public Vector3 nposition;            public Vector3 nacceleration;            public Material nmaterial;            public float nradius;            public float nmass;            public bool ngravity;        }    }}

This topic is closed to new replies.

Advertisement