(module nbody-ics mzscheme
(require (lib "kw.ss")
(lib "42.ss" "srfi")
(lib "math.ss")
(only (lib "43.ss" "srfi") vector-copy)
(lib "contract.ss"))
(define-struct body
(m q p) #f)
(define nbody-system/c (vectorof body?))
(define 3vector/c (flat-named-contract "3vector/c"
(lambda (v)
(and (vector? v)
(= (vector-length v) 3)))))
(provide/contract
(body-copy (-> body? body?))
(copy-bodies (-> nbody-system/c nbody-system/c))
(total-mass (-> nbody-system/c (>/c 0.0)))
(center-of-mass (-> nbody-system/c 3vector/c))
(total-momentum (-> nbody-system/c 3vector/c))
(adjust-frame! (-> nbody-system/c nbody-system/c))
(make-cold-spherical (-> natural-number/c nbody-system/c))
(make-hot-spherical (case-> (-> natural-number/c nbody-system/c)
(-> natural-number/c (between/c 0.0 1.0) nbody-system/c)))
(make-plummer (-> natural-number/c nbody-system/c)))
(provide (struct body (m q p))
nbody-system/c 3vector/c)
(define (body-copy b)
(make-body (body-m b) (vector-copy (body-q b)) (vector-copy (body-p b))))
(define (copy-bodies bs)
(vector-of-length-ec (vector-length bs)
(:vector b bs)
(body-copy b)))
(define (random-between a b)
(let ((delta (- b a)))
(+ a (* delta (random)))))
(define (square x) (* x x))
(define (random-3vector r)
(let ((phi (random-between 0.0 (* 2.0 pi)))
(cos-theta (random-between -1.0 1.0)))
(let ((sin-theta (sqrt (- 1.0 (square cos-theta))))) (vector (* r sin-theta (cos phi))
(* r sin-theta (sin phi))
(* r cos-theta)))))
(define/kw (random-from-distribution dist #:key (x-min 0.0) (x-max 1.0) (y-min 0.0) (y-max 1.0))
(let loop ((x (random-between x-min x-max))
(y (random-between y-min y-max)))
(if (< y (dist x))
x
(loop (random-between x-min x-max)
(random-between y-min y-max)))))
(define (vector-add! v w)
(do-ec (:parallel (:vector velt (index i) v)
(:vector welt w))
(vector-set! v i (+ velt welt)))
v)
(define (vector-sub! v w)
(do-ec (:parallel (:vector velt (index i) v)
(:vector welt w))
(vector-set! v i (- velt welt)))
v)
(define (vector-scale! v x)
(do-ec (:vector velt (index i) v)
(vector-set! v i (* velt x)))
v)
(define (vector-scale v x)
(vector-of-length-ec (vector-length v)
(:vector velt v)
(* velt x)))
(define (total-mass bs)
(sum-ec (:vector b bs)
(body-m b)))
(define (total-momentum bs)
(let ((p-tot (make-vector 3 0.0)))
(do-ec (:vector b bs)
(vector-add! p-tot (body-p b)))
p-tot))
(define (center-of-mass bs)
(let ((M (total-mass bs))
(mq (make-vector 3 0.0)))
(do-ec (:vector b bs)
(vector-add! mq (vector-scale (body-q b) (body-m b))))
(vector-scale! mq (/ M))))
(define (adjust-frame! bs)
(let ((M (total-mass bs))
(Q (center-of-mass bs))
(P (total-momentum bs)))
(do-ec (:vector b bs)
(begin
(vector-sub! (body-q b) Q)
(vector-sub! (body-p b) (vector-scale P (/ (body-m b) M)))))
bs))
(define (make-cold-spherical n)
(let ((m (/ 1.0 n)))
(adjust-frame!
(vector-of-length-ec n (:range i n)
(let ((r (random-from-distribution square #:x-max 12/5 #:y-max 144/25)))
(make-body m (random-3vector r) (make-vector 3 0.0)))))))
(define/kw (make-hot-spherical n #:optional (Q 0.5))
(let ((m (/ 1.0 n)))
(let ((R (* 12/5 (- 1.0 Q)))
(V (sqrt (/ (* 5/6 Q) (- 1.0 Q)))))
(adjust-frame!
(vector-of-length-ec n (:range i n)
(let ((r (random-from-distribution square #:x-max R #:y-max (square R)))
(v (random-from-distribution square #:x-max V #:y-max (square V))))
(make-body m (random-3vector r) (random-3vector (* m v)))))))))
(define (make-plummer n)
(let ((m (/ 1.0 n))
(scalefactor (/ 16.0 (* 3.0 pi))))
(adjust-frame!
(vector-of-length-ec n (:range i n)
(let ((r (/ 1.0 (sqrt (- (expt (random) -2/3) 1.0))))
(x (random-from-distribution (lambda (x) (* (square x) (expt (- 1.0 (square x)) 3.5)))
#:y-max 0.1)))
(let ((v (* x (sqrt 2) (expt (+ 1.0 (square r)) -0.25))))
(make-body m (random-3vector (/ r scalefactor))
(random-3vector (* m v (sqrt scalefactor)))))))))))