File: fast_physics.ml

package info (click to toggle)
planets 0.1.13-19
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 512 kB
  • sloc: ml: 4,541; makefile: 207; ansic: 38
file content (186 lines) | stat: -rw-r--r-- 5,504 bytes parent folder | download | duplicates (9)
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