PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/php/rk4.php

https://bitbucket.org/oblivionx/cyb
PHP | 284 lines | 184 code | 43 blank | 57 comment | 19 complexity | 3127a32349276615b3c3b8b379e61dd7 MD5 | raw file
  1. <?php
  2. function cmp_key_len($a,$b) {
  3. if(strlen($a) > strlen($b)) {
  4. return -1;
  5. } else if(strlen($a) < strlen($b)) {
  6. return 1;
  7. } else {
  8. return 0;
  9. }
  10. }
  11. class rk4 {
  12. var $h; //step size
  13. var $start_t; //start time
  14. var $end_t; //end time
  15. var $odes; //simultaneous odes
  16. var $params; //parameter values
  17. var $ics; //initial conditions
  18. var $outputs; //outputs
  19. var $inputs; //input array, heavily modified from js for processing purposes
  20. var $inputs_raw; //unmodified inputs, for possible future use, might be needed for optimization
  21. var $ode_system = array(); //odes with param values replaced and initial conditions set
  22. var $data;
  23. function rk4($step_size, $end_time, $start_time = 0) {
  24. $this->h = $step_size;
  25. $this->start_T = $start_time;
  26. $this->end_t = $end_time;
  27. }
  28. function set_system($odeArr) {
  29. $this->odes = $odeArr;
  30. }
  31. function set_params($paramArr){
  32. $this->params = $paramArr;
  33. }
  34. function set_ics($icArr) {
  35. $this->ics = $icArr;
  36. }
  37. function set_outputs($outArr) {
  38. $this->outputs = $outArr;
  39. }
  40. /**
  41. * Input array format:
  42. * $inputs['q1'][0]['type'] //bolus or infusion, currently only bolus implemented
  43. * ['start'] //start time
  44. * ['end'] //end time (ignored for bolus)
  45. * ['repeat'] //# of times to repeat (0 for none). repeats will only be processed until sim end tiem
  46. * ['interval'] //interval between repeats. same unit of time as simulation
  47. * ['magnitude'] //magnitude of event
  48. *
  49. *
  50. * Output array format:
  51. * $inputs[<event time step>][0]['magnitude'] //magnitude of event
  52. * ['sv'] //state var
  53. *
  54. * repeat events will have multiple entries at the closest time step point. constant infusion will have entries in range
  55. */
  56. function set_inputs($inputArr) {
  57. $this->inputs_raw = $inputArr;
  58. foreach($inputArr as $sv => $inputs) {
  59. foreach($inputs as $input) {
  60. switch($input['type']) {
  61. case "bolus":
  62. $start = floor(1.0 * $input['start'] / $this->h);
  63. if($input['repeat'] > 0) {
  64. $end_time = min(($input['repeat'] * $input['interval'] * 1.0),$this->end_t);
  65. $num_entries = floor(1.0 * $end_time / $input['interval']);
  66. $interval = floor(1.0 * $input['interval'] / $this->h);
  67. $end = ceil(1.0 * $end_time / $this->h);
  68. for($i = $start; $i < $end; $i += $interval) {
  69. $this->inputs[$i][$sv][] = $input['magnitude'];
  70. }
  71. } else {
  72. $this->inputs[$start][$sv][] = $input['magnitude'];
  73. }
  74. break;
  75. case "infusion":
  76. break;
  77. }
  78. }
  79. }
  80. }
  81. function init_system() {
  82. uksort($this->params,'cmp_key_len');
  83. //print_r($this->params);
  84. //echo 'after sort';
  85. //print_r($this->params);
  86. foreach($this->odes as $sv=>$ode) {
  87. foreach($this->params as $param => $val) {
  88. // echo $param . ' ' . $val . ' ' . $ode . ' ';
  89. $ode = str_replace($param,$val,$ode);
  90. }
  91. $this->ode_system[$sv] = $ode;
  92. }
  93. foreach($this->ics as $sv => $val) {
  94. $this->data[0][$sv] = ($val * 1.0) + array_sum($this->inputs[0][$sv]);
  95. }
  96. //print_r($this->ode_system);
  97. //$this->_a($this->ode_system['q1'],1);
  98. }
  99. function calculate_outputs() {
  100. foreach($this->data as $i => $y_vec) { //for each time point
  101. foreach($this->outputs as $out => $eqn) { //for each output
  102. $this->data[$i]['y' . $out] = $this->f($eqn,$this->i2t($i),$this->data[$i]);
  103. }
  104. }
  105. }
  106. function format_data($format = "js") {
  107. if($format == "js" ) {
  108. $js = array();
  109. foreach($this->data as $x => $y_vec) {
  110. foreach($y_vec as $sv => $y) {
  111. if($sv != "t") {
  112. $js[$sv][] = array($this->i2t($x),$y);
  113. }
  114. }
  115. }
  116. return $js;
  117. }else if($format == "matlab") {
  118. //matlab array output
  119. $matlab = "";
  120. return $matlab;
  121. }
  122. else if($format == "gnu") {
  123. $gnu = "";
  124. //print_r($this->data);
  125. foreach($this->data as $x =>$y_vec) {
  126. $gnu .= $this->data[$x]['t'] . "\t";
  127. foreach($y_vec as $y => $val) {
  128. $gnu .= $this->data[$x][$y] . "\t";
  129. }
  130. $gnu .="\n";
  131. }
  132. //print_r($y_vec);
  133. return $gnu;
  134. }
  135. }
  136. function simulate() {
  137. $this->init_system();
  138. if(count($this->ode_system) != count($this->ics) ) {
  139. return false;
  140. }
  141. $test = array();
  142. $num_steps = ($this->end_t - $this->start_t) / $this->h;
  143. //data already contains our initial conditions, so we start at $i = 1
  144. for($i = 0; $i < $num_steps; $i ++) { //main step loop
  145. $this->rk_iter($i);
  146. $this->data[$i]['t'] = $this->i2t($i);
  147. }
  148. $this->calculate_outputs();
  149. // print_r($this->data);
  150. }
  151. //convert from step to time
  152. function i2t($i) {
  153. return ($i * $this->h * 1.0);
  154. }
  155. //ode, t, x (x = data[sv][t])
  156. //t is previous step, current t = t + h
  157. function f($ode,$t,$x_vec) {
  158. foreach(array_keys($this->ics) as $sv) {
  159. $ode = str_replace($sv,$x_vec[$sv],$ode);
  160. //$ode = str_replace("TIME",$t,$ode);
  161. }
  162. //echo 'echo '. $t . " " . $ode . ";\n\n\n";
  163. eval("\$result = $ode;");
  164. //echo $t . " " . $ode . "=" . $result . "\n";
  165. return $result;
  166. }
  167. //i = previous iteration
  168. function _k1($i) {
  169. $t = $this->i2t($i);
  170. $k1 = array();
  171. foreach($this->ode_system as $sv=>$ode) {
  172. /*foreach($this->ics as $sv=>$ic) {
  173. $ode = str_replace($sv,$this->data[$sv][$t],$ode);
  174. }*/
  175. $k1[$sv] = $this->f($ode,$t,$this->data[$i]);
  176. }
  177. return $k1;
  178. }
  179. function _k2($k1,$i) {
  180. $t = $this->i2t($i);
  181. //print_r($k1);
  182. foreach($this->ode_system as $sv=>$ode) {
  183. //echo $i . " ". $this->data[$i][$sv] . " ";
  184. $x_a[$sv] = $this->data[$i][$sv] + (($this->h/2.0) * $k1[$sv]);
  185. //echo "x_a[$sv] " . $x_a[$sv] . " ";
  186. }
  187. //echo "\n";
  188. foreach($this->ode_system as $sv=>$ode) {
  189. $k2[$sv] = $this->f($ode,($t+($this->h/2.0)),$x_a);
  190. }
  191. return $k2;
  192. }
  193. function _k3($k2,$i) {
  194. $t = $this->i2t($i);
  195. //print_r($k2);
  196. foreach($this->ode_system as $sv=>$ode) {
  197. $x_b[$sv] = $this->data[$i][$sv] + (($this->h/2.0) * $k2[$sv]);
  198. }
  199. //print_r($x_b);
  200. foreach($this->ode_system as $sv=>$ode) {
  201. $k3[$sv] = $this->f($ode,($t+($this->h/2.0)),$x_b);
  202. }
  203. return $k3;
  204. }
  205. function _k4($k3,$i) {
  206. $t = $this->i2t($i);
  207. //print_r($k3);
  208. foreach($this->ode_system as $sv=>$ode) {
  209. $x_c[$sv] = $this->data[$i][$sv] + ($this->h * $k3[$sv]);
  210. }
  211. //print_r($x_c);
  212. foreach($this->ode_system as $sv=>$ode) {
  213. $k4[$sv] = $this->f($ode,($t+$this->h),$x_c);
  214. }
  215. return $k4;
  216. }
  217. function rk_iter($i) {
  218. //print_r($k4);
  219. $t = $this->i2t($i);
  220. $k1 = $this->_k1($i);
  221. $k2 = $this->_k2($k1,$i);
  222. $k3 = $this->_k3($k2,$i);
  223. $k4 = $this->_k4($k3,$i);
  224. /*echo 'k1: ';
  225. print_r($k1);
  226. echo ' k2: ';
  227. print_r($k2);
  228. echo ' k3: ';
  229. print_r($k3);
  230. echo ' k4: ';
  231. print_r($k4);*/
  232. foreach($this->ode_system as $sv=>$ode) {
  233. $this->data[$i+1][$sv] = $this->data[$i][$sv] + ($this->h/6.0)*($k1[$sv] + (2.0 * $k2[$sv]) + (2.0 * $k3[$sv]) + $k4[$sv]);
  234. //foreach($this->inputs[$i+1][$sv] as $inp) {
  235. $this->data[$i+1][$sv] += array_sum($this->inputs[$i+1][$sv]);
  236. //}
  237. }
  238. //return $x1;
  239. }
  240. }
  241. ?>