rungeku.red 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. module rungeku; % Numeric solution of ODE's with Runge-Kutta.
  2. % Version 2: supporting ODE systems
  3. % Author: H. Melenk, ZIB, Berlin
  4. % Copyright (c): ZIB Berlin 1993, all rights resrved
  5. fluid '(!*noequiv accuracy!*);
  6. global '(iterations!* !*trnumeric);
  7. put ('num_odesolve,'psopfn,'rungekuttaeval);
  8. symbolic procedure rungekuttaeval u;
  9. % interface function;
  10. begin scalar e,f,x,y,sx,sy,en,d,v,w,q;
  11. u := for each x in u collect reval x;
  12. u := accuracycontrol(u,20,6);
  13. % equations
  14. e :=car u; u :=cdr u;
  15. e := if eqcar(e,'list) then cdr e else {e};
  16. % dependent variable
  17. q :=car u; u :=cdr u;
  18. q := if eqcar(q,'list) then cdr q else {q};
  19. for each yy in q do
  20. <<if not eqcar(yy,'equal) or not idp cadr yy
  21. then typerr(yy,"expression `dep. variable=starting value'");
  22. sy:=caddr yy.sy; y := cadr yy.y;
  23. >>;
  24. sy:=reversip sy; y:=reversip y;
  25. if length sy neq length e then
  26. rederr "number of equations and variables don't correspond";
  27. % independent variable
  28. x :=car u; u :=cdr u;
  29. if not eqcar(x,'equal) or not idp cadr x
  30. or null (w:=revalnuminterval(caddr x,t))
  31. then typerr(x,"expression `indep. variable=interval'");
  32. sx:=car w; en:=cadr w; x := cadr x;
  33. % convert expressions to explicit ODE system.
  34. q := for each yy in y collect {'df,yy,x};
  35. v:=cdr solveeval list('list. e, 'list . q);
  36. if cdr v then
  37. ruksyserr(nil,"cannot convert to explicit ODE system");
  38. f := for each d in q collect rukufindrhs(d,v);
  39. return rungekutta1(f,x,y,sx,en,sy);
  40. end;
  41. symbolic procedure rukufindrhs(d,e);
  42. % Find the righthand side for d in list e.
  43. if atom e then
  44. ruksyserr(d," cannot be moved to lhs of system") else
  45. if eqcar(car e,'equal) and cadr car e = d then
  46. reval caddr car e else
  47. if pairp e and eqcar(car e,'list) then rukufindrhs(d,cdar e)
  48. else rukufindrhs(d,cdr e);
  49. symbolic procedure ruksyserr(u,m);
  50. <<if u then writepri(mkquote u,'first);
  51. writepri(mkquote m,'last);
  52. rederr "Runge-Kutta failed";
  53. >>;
  54. symbolic procedure rungekutta1(f,x,y,sx,ex,sy);
  55. %preparations for rungekutta iteration.
  56. (begin scalar acc,r,oldmode,!*noequiv;
  57. integer prec;
  58. if not memq(dmode!*,'(!:rd!: !:cr))then
  59. <<oldmode:=t; setdmode('rounded,t)>>;
  60. !*noequiv:=t; prec:=precision 0;
  61. sx := force!-to!-dm numr simp sx;
  62. ex := force!-to!-dm numr simp ex;
  63. sy := for each z in sy collect force!-to!-dm numr simp z;
  64. acc := !:!:quotient(1,expt(10,accuracy!*));
  65. if !*trnumeric then prin2t "starting rungekutta iteration";
  66. r := rungekutta2(f,x,y,sx,ex,sy,acc);
  67. if oldmode then setdmode('rounded,nil);
  68. precision prec;
  69. if null r then rederr "no solution found";
  70. return 'list.r;
  71. end) where !*roundbf=!*roundbf;
  72. symbolic procedure rungekutta2(f,xx,yy,xs,xe,ys,acc);
  73. % Algorithm for numeric ODE solution
  74. % f(xx,yy): rhs;
  75. % x: independent variable;
  76. % y: dependent variable;
  77. % s: starting point;
  78. % e: endpoint;
  79. % acc: required accuracy
  80. % f,yy,ys are vectors (lists).
  81. dm!:
  82. begin scalar h,h2,h4,x,y,r,w1,w2,vars,dir;
  83. integer count,st;
  84. vars := xx.yy;
  85. x:=xs; y:=ys;
  86. h:=(xe - xs)/iterations!*;
  87. dir := h>0; st:= iterations!*;
  88. % compute initial stepsize
  89. adapt:
  90. h2:=h/2; h4:=h/4;
  91. % full stepsize.
  92. w1:=rungekuttastep(f,vars,x,y,h,h2);
  93. % same with half stepsize.
  94. w2:=rungekuttastep(f,vars,x,y,h2,h4);
  95. w2:=rungekuttastep(f,vars,x+h2,w2,h2,h4);
  96. % abs(w2 - w1)
  97. if normlist list!-list(w2,w1) > acc then
  98. <<h:=h2; st:=st+st; goto adapt>>;
  99. if !*trnumeric and not(st=iterations!*) then
  100. <<prin2
  101. "*** RungeKutta increased number of steps to ";
  102. prin2t st>>;
  103. loop:
  104. if (dir and x>xe) or (not dir and x<xe) then goto finis;
  105. r:=('list.x.y) . r;
  106. count:=add1 count;
  107. y:= rungekuttastep(f,vars,x,y,h,h2);
  108. % Advance solution.
  109. x:=x+h;
  110. goto loop;
  111. finis:
  112. r:= ('list.xx.yy).('list.xs.ys). rungekuttares(reversip r,st);
  113. return r;
  114. end;
  115. symbolic procedure rungekuttares(l,st);
  116. % eliminate intermediate points.
  117. if st=iterations!* then l else
  118. << for i:=1:iterations!* collect
  119. <<for j:=1:m do l:= cdr l; car l>>
  120. >> where m=st/iterations!*;
  121. symbolic procedure rungekuttastep(f,vars,x,y,h,h2);
  122. dm!:
  123. begin scalar d1,d2,d3,d4;
  124. % f(x,y)
  125. d1:= list!-evaluate(f,vars,x.y);
  126. % f(x+h2,y+h2*d1)
  127. d2:= list!-evaluate(f,vars,
  128. (x+h2). list!+list(y,scal!*list(h2,d1)));
  129. % f(x+h2,y+h2*d2)
  130. d3:= list!-evaluate(f,vars,
  131. (x+h2). list!+list(y,scal!*list(h2,d2)));
  132. % f(x+h,y+h*d3)
  133. d4:= list!-evaluate(f,vars,
  134. (x+h).list!+list(y,scal!*list(h,d3)));
  135. % find y(x+h) = y+h*(d1 + 2*d2 + 2*d3 + d4)/6.
  136. y:=list!+list(y,
  137. scal!*list(!:!:quotient(h,6),
  138. list!+list(d1,
  139. list!+list(scal!*list(2,d2),
  140. list!+list(scal!*list(2,d3),
  141. d4)))));
  142. return y;
  143. end;
  144. endmodule;
  145. end;