I'm trying to do a numerical integration of the solar system. I did this before in plain Scheme, now I want to do it using the very interesting SCMUTILS-library from MIT. What I did:
- I took solar system data from the Jet Propulsion Laboratory; that is: the mass, the position and the velocity of the sun, mercurius, venus and earth in barycentric coordinates.
- I wrote code for the differential equation, such that every object in the system (sun, mercurius, venus, earth) gets attracted by the 3 others in the correct way.
- I ran the simulation through numerical integration using SCMUTILS.
If I run the simulation with the sun + 1 other planet, it works. If I try to take the sun + 2 other planets, it seems to hang. This is strange as I ran the simulation with the same data a few years ago with my own home-built Runge-Kutta integrator, and that worked fine.
Note that I'm well-known with MIT-Scheme and numerical integration, and that I only want to learn SCMUTILS. I'm doing something wrong clearly, and it would surprise me if this problem couldn't be tackled with SCMUTILS.
Also, I'm not fixed on my code: if somebody can provide me with a working implementation in SCMUTILS, then that's fine as well, as long as I understand what I'm doing wrong in my program. I just want to use SCMUTILS in an idiomatic way...
My code is below (about 60 well-documented lines). Thanks for any comments or improvements towards a working simulation.
;JPL-DE - Ephemerides from Jet Propulsion Laboratory http://ssd.jpl.nasa.gov
(define solar-system
(up (+ 0.0 (decoded-time->universal-time (make-decoded-time 0 0 0 1 1 2000 0))) ; January 1st 2000 at 0h0m0s UT
(up (up 1.3271244004193937e20 ; Sun mass * gravitational constant
(up -1068000648.30182 -417680212.56849295 30844670.2068709) ; Sun position (x,y,z) in meters in barycentric coordinates
(up 9.305300847631916 -12.83176670344807 -.1631528028381386)) ; Sun velocity (vx,vy,vz) in meters per second
(up 22031780000000. ; Mercurius
(up -22120621758.62221 -66824318296.10253 -3461601353.17608)
(up 36662.29236478603 -12302.66986781422 -4368.33605178479))
(up 324858592000000. ; Venus
(up -108573550917.8141 -3784200933.160055 6190064472.97799)
(up 898.4651054838754 -35172.03950794635 -532.0225582712421))
; (up 398600435436000. ; Earth
; (up -26278929286.8248 144510239358.6391 30228181.35935813)
; (up -29830.52803283506 -5220.465685407924 -.1014621798034465))
)))
(define (ss-time state) ; Select time from solar system state
(ref state 0))
(define (ss-planets state) ; Select up-tuple with all planets
(ref state 1))
(define (ss-planet state i) ; Select i-th planet in system (0: sun, 1: mercurius, 2: venus, 3: earth) (Note: the sun is also a "planet")
(ref (ss-planets state) i))
(define (GM planet) ; Select GM from planet (GM is gravitational constant times mass of planet)
(ref planet 0))
(define (position planet) ; Select position up-tuple (x,y,z) from planet
(ref planet 1))
(define (velocity planet) ; Select velocity up-tuple (vx,vy,vz) from planet
(ref planet 2))
(define ((dy/dt) state)
(define (gravitational-force on-planet by-planet) ; Calculate gravitational force on planet "on-planet" by "by-planet"
(if (equal? on-planet by-planet) ; Compare planets
(up 0.0 0.0 0.0) ; There is no force of a planet on itself
(let* ((r (- (position on-planet) (position by-planet))) ; Position of "on-planet" seen from "by-planet"
(d (abs r))) ; Distance between the two
(* -1.0 (GM by-planet) (/ r (* d d d)))))) ; Gravitational force is negatively directed, we divide by d^3 to cancel out r in nominator
(define (dy/dt-planet planet) ; Calculate dy/dt for a planet
(up 0.0 ; No change in GM
(velocity planet) ; Change in position is velocity
(* (s:generate (s:length (ss-planets state)) 'up (lambda (i) (gravitational-force planet (ss-planet state i)))) ; Calculate gravitation forces from
(s:generate (s:length (ss-planets state)) 'down (lambda (i) 1.0))))) ; all other planets, and sum them via inner product with down-tuple
(up 1.0 ; Timestep: dt/dt=1.0
(s:generate (s:length (ss-planets state)) 'up (lambda (i) (dy/dt-planet (ss-planet state i)))))) ; Calculate dy/dt for all planets
(define win (frame -150e9 150e9 -150e9 150e9 512 512)) ; Viewport: a square of 300e9 meters by 300e9 meters plotted in a 512 by 512 window
(define ((monitor-xy) state)
((s:elementwise (lambda (planet) (plot-point win (ref (position planet) 0) (ref (position planet) 1)))) (ss-planets state))) ; Plot X,Y (no Z) for planets
(define end ; Define end state
((evolve dy/dt) ; Run simulation
solar-system ; Starting state, Jan. 1st 2000 0h0m0s
(monitor-xy) ; Plot positions throughout simulation
(* 1.0 60 60) ; Timestep: 1 hour
(decoded-time->universal-time (make-decoded-time 0 0 0 1 1 2005 0))) ; Run for 5 years
)