1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
(* Planets: A celestial simulator
Copyright (C) 2001-2003 Yaron M. Minsky
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)
open StdLabels
open MoreLabels
open Printf
open State
open Constants
open Common
(* Faster physics implementation that is array-based *)
(* Only implements the act_all_on_all function. *)
(* Another useful thing to implement faster would be collision detection *)
(* all-float record, to let the compiler unbox in a loop *)
type fbody = { mutable x_pos: float;
mutable y_pos: float;
mutable x_vel: float;
mutable y_vel: float;
fb_radius: float;
fb_mass: float;
}
let cube x = x *. x *. x
let square x = x *. x
(* let sqrtcube x = sqrt (x *. x *. x) (* Faster but less flexible
implementaqtion of sqrtcube *) *)
let body_to_fbody body =
let (x_pos,y_pos) = body.pos
and (x_vel,y_vel) = body.velocity
(*and (x_accel,y_accel) =
match body.i with None -> (0.,0.) | Some accel -> accel *)
in
{ x_pos = x_pos;
y_pos = y_pos;
fb_radius = body.radius;
fb_mass = body.mass;
x_vel = x_vel;
y_vel = y_vel;
}
let build_fbody_array bodies =
Array.of_list (List.map ~f:body_to_fbody bodies)
(* convert a body and a b to an updated body *)
let fbody_and_body_to_body body b =
{ body with
velocity = (b.x_vel,b.y_vel);
pos = (b.x_pos,b.y_pos);
}
(******** List iterator w/index ********)
let rec list_iteri_rec ~f list i = match list with
[] -> ()
| hd::tl -> f ~i hd; list_iteri_rec ~f tl (i+1)
let list_iteri ~f list =
list_iteri_rec ~f list 0
(**************************************************)
let array_to_bodies bodies array =
let b_list = Array.to_list array in
List.map2 ~f:fbody_and_body_to_body bodies b_list
(**********************************************************************)
let timer = MTimer.create ()
let sqrtcube x = sqrt (x *. x *. x)
let act_all_on_all_rk ~bounce bodies =
let gexp = grav_exp#v in
let exp = ((1.0 +. gexp)/.2.0) in
let sqrtcube =
if gexp = 2.0 then sqrtcube else (fun x -> x ** exp) in
let const = gconst#v in
(* t is the time. This function has no time dependence.
s is the position is state space
dsdt is the array of derivatives
*)
let deriv t s dsdt =
(* initialize derivatives to 0 *)
for i = 0 to Array.length dsdt - 1 do dsdt.(i) <- 0. done;
for i = 0 to Array.length bodies - 1 do
(* x and y pos derivative *)
dsdt.(i*4) <- s.(i*4 + 2);
dsdt.(i*4 + 1) <- s.(i*4 + 3);
done;
for i = 0 to Array.length bodies - 1 do
for j = i+1 to Array.length bodies - 1 do
(* compute i's action on j and vice-versa. That way you only need to
* compute the force once. It's nearly twice as fast that way. *)
let x_pos_i = s.(i*4) and y_pos_i = s.(i*4 + 1) in
let x_pos_j = s.(j*4) and y_pos_j = s.(j*4 + 1) in
let xdiff = x_pos_i -. x_pos_j
and ydiff = y_pos_i -. y_pos_j in
let dist_sq = xdiff *. xdiff +. ydiff *. ydiff in
let mult = const /. sqrtcube dist_sq in
let mult_i = -. mult *. bodies.(j).fb_mass
and mult_j = mult *. bodies.(i).fb_mass
in
(* x vel derivative *)
dsdt.(i*4 + 2) <- dsdt.(i*4 + 2) +. xdiff *. mult_i;
dsdt.(j*4 + 2) <- dsdt.(j*4 + 2) +. xdiff *. mult_j;
(* y vel derivative *)
dsdt.(i*4 + 3) <- dsdt.(i*4 + 3) +. ydiff *. mult_i;
dsdt.(j*4 + 3) <- dsdt.(j*4 + 3) +. ydiff *. mult_j;
if bounce &&
(let total_radius = bodies.(j).fb_radius +. bodies.(i).fb_radius in
dist_sq < total_radius *. total_radius )
then (
let total_radius = bodies.(j).fb_radius +. bodies.(i).fb_radius in
let mult = -. mult *. bodies.(i).fb_mass *. bodies.(j).fb_mass
*. (total_radius *. total_radius /. dist_sq) in
let mult_i = -. mult /. bodies.(i).fb_mass
and mult_j = mult /. bodies.(j).fb_mass
in
(* x vel derivative *)
dsdt.(i*4 + 2) <- dsdt.(i*4 + 2) +. xdiff *. mult_i;
dsdt.(j*4 + 2) <- dsdt.(j*4 + 2) +. xdiff *. mult_j;
(* y vel derivative *)
dsdt.(i*4 + 3) <- dsdt.(i*4 + 3) +. ydiff *. mult_i;
dsdt.(j*4 + 3) <- dsdt.(j*4 + 3) +. ydiff *. mult_j;
)
done
done
in
let s =
Array.init (4 * Array.length bodies)
~f:(fun i -> match i mod 4 with
| 0 -> bodies.(i / 4).x_pos
| 1 -> bodies.(i / 4).y_pos
| 2 -> bodies.(i / 4).x_vel
| _ -> bodies.(i / 4).y_vel
)
in
(* dumb_solver s state.delta#v deriv; *)
Rk4.step s 0.0 state.delta#v s deriv;
for i = 0 to Array.length bodies - 1 do
bodies.(i).x_pos <- s.(4 * i);
bodies.(i).y_pos <- s.(4 * i + 1);
bodies.(i).x_vel <- s.(4 * i + 2);
bodies.(i).y_vel <- s.(4 * i + 3);
done
let act_all_on_all ~bounce bodies =
let array = build_fbody_array bodies in
act_all_on_all_rk ~bounce array;
array_to_bodies bodies array
|