My first post on Stack Overflow, so bear with me. I have built a model of a chaotic double pendulum in python using the pymunk physics library, which in turn uses the chipmunk C library. The pendulum, being chaotic, displays extreme sensitivity to initial conditions. However, I have realized that even if I run the simulation with the exact same initial conditions (changing zero code whatsoever), the two simulations diverge fairly quickly.
There is something in my code that has the potential to yield different results each time it is run. Because the simulation is chaotic, any tiny error that isn't constant between my runs can result in large changes.
What could be causing this? I am not sure if this is a result of a small flaw in chipmunk, pymunk, or even python or my OS/PC.
EDIT:
Here is my code:
import pyglet
import pymunk
import pymunk.pyglet_util
def add_pendulum(space):
static_body = pymunk.Body()
static_body.position = 400, 400
mass = 10
inertia = pymunk.moment_for_box(mass, 5, 100)
body1 = pymunk.Body(mass, inertia)
body1.position = 350, 400
l1 = pymunk.Poly(body1, [(50, 0), (-50, 0), (-50, 5), (50, 5)])
l1.friction = 0.1
l1.collision_type = 2
pivot_joint = pymunk.PivotJoint(static_body, body1, (400, 400))
space.add(body1, l1, pivot_joint)
mass = 10
inertia = pymunk.moment_for_box(mass, 5, 100)
body2 = pymunk.Body(mass, inertia)
body2.position = 250, 400
l2 = pymunk.Poly(body2, [(50, 0), (-50, 0), (-50, 5), (50, 5)])
l2.friction = 0.1
l2.collision_type = 2
pivot_joint2 = pymunk.PivotJoint(body1, body2, (300, 402.5))
space.add(body2, l2, pivot_joint2)
# A hack to disable all collisions
nocollisions = lambda space, arbiter: False
space.add_collision_handler(2, 2, nocollisions, nocollisions, nocollisions, nocollisions)
window = pyglet.window.Window(800, 600)
space = pymunk.Space()
add_pendulum(space)
space.gravity = (0, -10)
test = [()] # A temporary hack to hide the tuple in a mutable object
def on_draw(dt): #For graphing the moving pendulum
points = test[0]
space.step(dt)
pyglet.gl.glClearColor(0, 0, 0, 1)
window.clear()
pymunk.pyglet_util.draw(space)
points += (int(tuple(space.shapes[1].get_vertices()[1])[0]), int(tuple(space.shapes[1].get_vertices()[1])[1]))
pyglet.gl.glClearColor(200, 200, 200, 1)
pyglet.graphics.draw(len(points)/2, pyglet.gl.GL_POINTS,
('v2i', points)
)
test[0] = points
pyglet.clock.schedule_interval(on_draw, 1/1000.0)
pyglet.app.run()
Regarding your on_draw(dt)
function called from pyglet.clock.schedule_interval
, the pyglet docs state:
The dt parameter gives the number of seconds (due to latency, load and timer inprecision, this might be slightly more or less than the requested interval).
Each time you run your program, it's going to call on_draw
with a slightly different dt
parameter. You go on to call space.step(dt)
which will be slightly different on each call. Given how sensitive your program is to changing conditions, I think this is the culprit.
Not sure if it helps, but here is another question regarding fixed or variable time steps & physics (well, & game engines).