Search code examples
pythonphysicsgravity

Python gravity simulator behaving strangely


I'm making a gravity simulation in Python (in 3D with VPython, to be exact) and I'm sure there's nothing wrong with the code, but it behaves strangely when two objects get close to each other.

My inspiration is http://testtubegames.com/gravity.html. Note how you can place two planets with no velocity, they move towards each other, overtake, decelerate and turn back. In my program, they overtake, and decelerate, but only proportionately to the distance, so technically it should never turn back anyway.

I realize that the law f=G*(m1*m2)/r**2 wouldn't work if r (the distance) is or gets too close to 0, so I have included a max-out, so if it is less than 1 it is set to 1 (units not in pixels, by the way), but it still does not work.

Simple logic also suggests that the objects should not react in this way, so the next thing that follows is that I must be missing something.

Here is an extract of the code:

from visual import *
a = sphere(x=-10,mass=10, vel=vector())
b = sphere(x=10, mass=10, vel=vector())

while 1:
    rate(20)

    #distance between the two objects, a and b, where a.r.mag would be the magnitude of the vector
    a.r = b.pos - a.pos
    b.r = a.pos - b.pos

    a.force = a.r
    if a.r.mag > 1:
        a.force.mag = (a.mass * b.mass) / a.r.mag**2
    else:
        a.force.mag = (a.mass * b.mass) / 1
    a.vel = a.vel + a.force / a.mass


    b.force = b.r
    if b.r.mag > 1:
        b.force.mag = (a.mass * b.mass) / b.r.mag**2
    else:
        b.force.mag = (a.mass * b.mass) / 1
    b.vel = b.vel + b.force / b.mass

    a.pos = a.pos + a.vel
    b.pos = b.pos + b.vel

EDIT: Code re-written in response to shockburner:

from visual import *
import sys

limit2 = sys.float_info.min
limit = limit2**0.5
timestep = 0.0005

a = sphere(x=-5,mass=10, vel=vector())
b = sphere(x=5, mass=10, vel=vector())

def force(ob1, ob2):
    ob1.r = ob2.pos - ob1.pos
    ob1.force = ob1.r + vector()
    if ob1.r.mag > limit:
        ob1.force.mag = (ob1.mass * ob2.mass) / ob1.r.mag2
    else:
        ob1.force.mag = (ob1.mass * ob2.mass) / limit2
    return ob1.force

while 1:
    rt = int(1/timestep)
    rate(rt)

    a.acc = force(a, b) / a.mass
    b.acc = force(b, a) / b.mass

    a.pos = a.pos + timestep * (a.vel + timestep * a.acc / 2)
    b.pos = b.pos + timestep * (b.vel + timestep * b.acc / 2)

    a.acc1 = force(a,b) / a.mass
    b.acc1 = force(b,a) / b.mass

    a.vel = a.vel + timestep * (a.acc + a.acc1) / 2
    b.vel = b.vel + timestep * (b.acc + b.acc1) / 2

Any help or pointer in the right direction would be greatly appreciated, and if the answer turns out to be idiotically simple (which with me is usually the case) remember I am quite an idiot anyway.


Solution

  • I've had this problem before too. If you just go directly to Runge-Kutta, everything will sort itself out. This pdf will explain how to incorporate the method: http://spiff.rit.edu/richmond/nbody/OrbitRungeKutta4.pdf. Good luck!