3

I'm attempting a Runge-Kutta implementation for a mass on a spring in Javascript and visualizing it with D3. The purpose is to compare it to Forward Euler and comment on the differences. My FE works fine and plots fine, but the Runge-Kutta is shooting off in a negative direction and never wrapping around.

Here's a plunkr with the vis and the code, but I'll add the JS (only for the ODE solvers) too.

// *** Functions for ODE Solvers *** //

function FEx (x, v, h)
{
    return x + h*v;
}

function FEv (x, v, h)
{
    var k = 1; var m = 0.5; var g = 0;

    return v + h*( (-k/m)*x + g );
}

function RKx (x, v, h)
{
    var k1 = FEx(x, v, h);
    var k2 = FEx(x+h/2*k1, v+h/2, h);
    var k3 = FEx(x+h/2*k2, v+h/2, h);
    var k4 = FEx(x+h*k3, v+h, h);

    return x + h/6*(k1 + 2*k2 + 2*k3 + k4);
}

function RKy (x, v, h)
{
    var k1 = FEv(x, v, h);
    var k2 = FEv(x+h/2, v+h/2*k1, h);
    var k3 = FEv(x+h/2, v+h/2*k2, h);
    var k4 = FEv(x+h, v+h*k3, h);

    return v + h/6*(k1 + 2*k2 + 2*k3 + k4);
}

// FORWARD EULER
function forewardEuler (x, v, h, n)
{
    // Initialize an array to hold the values
    // JS doesn't really support multi-dimensional arrays
    // so this is a "jagged" nested array
    var values = new Array(n);
    for(i = 0; i < values.length; i++)
        values[i] = new Array(2);

    // Initial conditions
    values[0] = [x, v];

    for (i = 1; i < n; ++i)
    {
        values[i][0] = FEx(values[i-1][0], values[i-1][1], h);
        values[i][1] = FEv(values[i-1][0], values[i-1][1], h);
    }

    return values;
}

// 4TH ORDER RUNGE-KUTTA 
function RK4 (x, v, h, n)
{
    // Initialize an array to hold the values
    var values = new Array(n);
    for(i = 0; i < values.length; i++)
        values[i] = new Array(2);

    // Initial conditions
    values[0] = [x, v];

    for (i = 1; i < n; ++i)
    {
        values[i][0] = RKx(values[i-1][0], values[i-1][1], h);
        values[i][1] = RKy(values[i-1][0], values[i-1][1], h);
    }

    return values;
}

// *** Setting up the data *** //

var rkValues = RK4(1, 0, 0.1, 100);
var feValues = forewardEuler(1, 0, 0.1, 100);
Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
Will Luce
  • 1,781
  • 3
  • 20
  • 33

1 Answers1

3

This has some very basic conceptual problems. For a coupled system you have to evaluate all operations simultaneously. That is, in y'(t)=f(y(t)) the function y(t) is vector valued, f has vectors as inputs and vectors as outputs. The Euler method can then be summarized as

k = f(y[i]);
y[i+1] = y[i] + h*k;

allowing flexibility in how the components of f are evaluated. RK4 follows then a similar scheme, the slopes k0,...,k3 are all values of the function f at various modified points.

The Euler step certainly is not a part of the RK4 step and should also not be mixed up with the system function of the ODE.

So you should use something in the direction of

function odefuncX(x,v) {return v;}

function odefuncV(x,v) { 
    var k = 1; var m = 0.5; var g = 0;
    return (-k/m)*x + g;
}

function EulerStep(x,v,h) {
    var kx = odefuncX(x,v);
    var kv = odefuncV(x,v);
    return [ x+h*kx, v+h*kv ];
}

function RK4Step(x,v,h) {
    var kx0 = odefuncX(x,v);
    var kv0 = odefuncV(x,v);

    var kx1 = odefuncX(x+0.5*h*kx0,v+0.5*h*kv0);
    var kv1 = odefuncV(x+0.5*h*kx0,v+0.5*h*kv0);

    var kx2 = odefuncX(x+0.5*h*kx1,v+0.5*h*kv1);
    var kv2 = odefuncV(x+0.5*h*kx1,v+0.5*h*kv1);

    var kx3 = odefuncX(x+    h*kx2,v+    h*kv2);
    var kv3 = odefuncV(x+    h*kx2,v+    h*kv2);

    return [ x+h/6*(kx0+2*(kx1+kx2)+kx3),
             v+h/6*(kv0+2*(kv1+kv2)+kv3) ];
}

// 4TH ORDER RUNGE-KUTTA 
function RK4 (x, v, h, n) {
    // Initialize an array to hold the values
    var values = new Array(n);

    // Initial conditions
    values[0] = [x, v];
    for (i = 1; i < n; ++i) {
        values[i] = RK4Step(values[i-1][0], values[i-1][1], h); 
    }
    return values;
}

See forked Plunker

Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51
  • That worked perfectly, thank you. I'm taking a numerical methods class without having taken an ODE class so the processes are a little foreign. – Will Luce Apr 23 '15 at 19:20
  • To be fair one should consider that 100 RK4 steps are 400 function evaluations. The Euler call for a similar effort over the same time interval would be `forwardEuler(1, 0, 0.025, 400);` which looks a little less catastrophic – Lutz Lehmann Apr 23 '15 at 19:26