First of all, I'd like to emphasize that, if you just want to solve an initial value problem (IVP) of ordinary differential equation (ODE) or ODE system, please use NDSolve
. Forget about the classical Runge-Kutta (classical RK, RK4), it's mainly for pedagogical purposes nowadays, and not comparable to the default method of NDSolve
at all. If default method of NDSolve
fails to solve your IVP, RK4 won't save your day, either. Actually I've never seen a knotty problem solved by switching from default NDSolve
method to RK4 since I began using Mathematica 10 years ago.
If you need RK4 for certain special reason (e.g. to exactly reproduce numeric result obtained with RK4 in literature, to ensure the result can be easily reproduced by others, etc.), setting proper options for NDSolve
is the way to go. Please refer to J.M.'s answer for more info. Here I'll provide a general-purposed package for setting up explicit Runge-Kutta method with no adaptive step control, which is implemented without the help of NDSolve
, just for fun:
BeginPackage["primaryRK`"];
rkstepgenerator; rhsfuncgenerator; rk4stepfunc;
Begin["`private`"];
rule = {semi -> CompoundExpression, set -> Set,
module -> Module, compile -> Compile};
SetAttributes[semi, Flat];
f[{}, _] := {};
rkstepgenerator[a_, b_, c_] := (s = Length@b;
k = ToExpression["k" <> ToString@#] & /@ Range@s;
Function @@ {f,
compile[{t, {y, _Real, 1}, dt},
module[k, semi[
semi @@ Thread@
set[k, Prepend[Thread@f[t + c dt, y + dt k . PadRight[#, s] & /@ a],
f[t, y]]],
y + dt b . k]]]} /. rule)
a = {{1/2}, {0, 1/2}, {0, 0, 1}};
b = {1/6, 1/3, 1/3, 1/6};
c = {1/2, 1/2, 1};
rk4stepfunc = rkstepgenerator[a, b, c];
rhsfuncgenerator[t_, ylst_][expr_] :=
Function @@ {{t, yvec}, module[ylst, semi[ylst~set~yvec, expr]]} /. rule
End[];
EndPackage[]
Since RK4 is so frequently requested, a function rk4stepfunc
is included in the package. Let me use the IVP
\begin{aligned}x'(t)&=-y(t)\\
y'(t)&=x(t)+\sin(3t)\\
x(0)&=1\\
y(0)&=0\end{aligned}
to illustrate its possible usage:
heads = {x, y};
vars = heads[t] // Through;
eqs = {x'[t] == -y[t], y'[t] == x[t] + Sin[3 t]};
iclst = {1, 0};
domain = {t0, tend} = {0, 6};
(* Analytic solution for comparison: *)
ref = Plot[
vars /. DSolve[{eqs, vars == iclst /. t -> t0}, vars, t] //
Evaluate, {t, t0, tend}];
expr =
Solve[eqs, D[vars, t]][[1, All, -1]] /.
(h : Alternatives @@ heads)[t] :> h
(* {-y, x + Sin[3 t]} *)
Internal`$ContextMarks = False; (* Default value is Automatic *)
rhsfunc = rhsfuncgenerator[t, heads]@expr
(* Function[{t, yvec}, Module[{x, y}, {x, y} = yvec; {-y, x + Sin[3 t]}]] *)
step = rk4stepfunc@rhsfunc;
dt = 1/30;
tlst = Range[t0, tend, dt];
sollst =
FoldList[step[#2[[1]], #, #2[[2]]] &, iclst,
Transpose@{Most@tlst, Differences@tlst}]; // AbsoluteTiming
(* {0.0013601, Null} *)
ListPlot[Thread /@ Thread@{tlst, sollst} // Transpose]~Show~ref
You can of course use the package to set up other explicit Runge-Kutta method with no error estimation e.g. explicit Euler. Just for fun, let me show another possible usage of the package:
eulerstepfunc = rkstepgenerator[{{}}, {1}, {}]
(* Function[f,
Compile[{t, {y, _Real, 1}, dt},
Module[{k1}, k1 = f[t, y]; dt k1 + y]]] *)
step = eulerstepfunc@rhsfunc;
dt = 1/50;
ti = t0 - dt;
sollst = NestList[
step[ti = ti + dt, #, dt] &, iclst, #2 - # & @@ domain/dt //
Round]; // AbsoluteTiming
(* {0.0007594, Null} *)
ListPlot[sollst\[Transpose], DataRange -> domain, PlotStyle -> Red]~Show~ref
Heun's method (Once again, I'll show a different usage of the package for fun):
heunstepfunc =
With[{α = 1},
rkstepgenerator[{{α}}, {1 - 1/(2 α), 1/(2 α)}, {α}]]
(* Function[f,
Compile[{t, {y, _Real, 1}, dt},
Module[{k1, k2}, k1 = f[t, y];
k2 = f[dt + t, dt k1 + y];
dt (k1/2 + k2/2) + y]]] *)
step = heunstepfunc@rhsfunc;
sollst = With[{dt = 1/20, t0 = N@t0, step = step, iclst = N@iclst},
With[{n = 1 - Subtract @@ domain/dt},
Compile[{}, Module[{lst = Table[iclst, {n}], ti = t0},
Do[lst[[i]] = step[ti, lst[[i - 1]], dt];
ti = ti + dt, {i, 2, n}];
lst]]]][]; // AbsoluteTiming
Show[sollst\[Transpose] //
ListPlot[#, DataRange -> domain, ColorFunction -> "Rainbow"] &, ref]
C
andE
are protected system symbols, and no value should be assigned to them. Also note that multiplication requires at least a space, thusCx
is a symbol whileC x
isC
timesx
$\endgroup$