1

I am using the following code to solve a simple harmonic motion which is a 2nd Order ODE. The code gives me a sinusoidal response, but the amplitude keeps getting bigger and bigger which in actual should stay constant throughout. Can anyone point out any mistake that I have made?

#include<stdio.h>
#include<conio.h>

#define f(t,x1,x2) x2
#define g(t,x1,x2) -x1

int main()
{
  FILE *fp;

  float x1_0, x2_0, t0, tn, h, yn, k1, l1, k2, l2, k3, l3, k4, l4, k, l,
    x1_n, x2_n;
  int i, n;
  x1_0 = 3;
  x2_0 = 0;
  t0 = 0;
  tn = 100;
  h = 0.1;
  n = tn/h;


  /* Calculating step size (h) */
  printf("h = %f\n", h);
  /* Runge Kutta Method */
  printf("\nt0\tx1_0\tx1_n\tx2_n\tx2_n\n");
  for (i = 0; i < n; i++)
  {
    k1 = h * (f(t0, x1_0, x2_0));
    l1 = h * (g(t0, x1_0, x2_0));
    k2 = h * (f((t0 + h / 2), (x1_0 + k1 / 2), (x2_0 + l1 / 2)));
    l2 = h * (g((t0 + h / 2), (x1_0 + k1 / 2), (x2_0 + l1 / 2)));
    k3 = h * (f((t0 + h / 2), (x1_0 + k2 / 2), (x2_0 + l2 / 2)));
    l3 = h * (g((t0 + h / 2), (x1_0 + k2 / 2), (x2_0 + l2 / 2)));
    k4 = h * (f((t0 + h / 2), (x1_0 + k3 / 2), (x2_0 + l3 / 2)));
    l4 = h * (g((t0 + h / 2), (x1_0 + k3 / 2), (x2_0 + l3 / 2)));
    k = (k1 + 2 * k2 + 2 * k3 + k4) / 6;
    l = (l1 + 2 * l2 + 2 * l3 + l4) / 6;
    x1_n = x1_0 + k;
    x2_n = x2_0 + l;
    printf("%0.4f\t%0.4f\t%0.4f\t%0.4f\t%0.4f\n", t0, x1_0, x1_n, x2_0, x2_n);
    t0 = t0 + h;
    x1_0 = x1_n;
    x2_0 = x2_n;
  }

  /* Displaying result */
  printf("\nValue of t at x1 = %0.2f is %0.3f", tn, x1_n);

  return 0;
}

The code gives me a sinusoidal response, but the amplitude keeps getting bigger and bigger which in actual should stay constant throughout. I have tried changing the "h" values and other changes, but the result is not changing. Can anyone point out any mistake that I have made? This is how it should look This is how my code does it

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Amir Khan
  • 13
  • 2

1 Answers1

3

There may be other issues, but one is in the way k4 and l4 are calculated:

k4 = h * (f((t0 + h / 2), (x1_0 + k3 / 2), (x2_0 + l3 / 2)));
//                  ^^^              ^^^              ^^^
l4 = h * (g((t0 + h / 2), (x1_0 + k3 / 2), (x2_0 + l3 / 2)));
//                  ^^^              ^^^              ^^^

Those values shouldn't be halved1.


1) See e.g. https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods and the modified code here: https://godbolt.org/z/WKcdo4cav

Bob__
  • 12,361
  • 3
  • 28
  • 42
  • 1
    Indeed, this error in the coefficients reduces the order of the method to 1, and the reported behavior is typical for order-1-methods (for others too, but then the error is much smaller, takes much longer to become visible). – Lutz Lehmann Mar 17 '23 at 07:31
  • Thank you for help. That fixed it. One thing that I notice is that I have set the time to tn = 10 but it never reaches that. Do you have any solution to that? – Amir Khan Mar 17 '23 at 15:21
  • 1
    @AmirKhan That's due to the accumulation of numerical errors in the repeated sum `t0 = t0 + h;`. In particular, `0.1` can't *exactly* be represented as a `float` and the rounding error propagates. A first mitigation would be the use of a wider type, with more precision, like `double` (cmp. e.g. the results of [1](https://godbolt.org/z/15qjT1PTd) vs. [2](https://godbolt.org/z/xxWh9Wde8)). – Bob__ Mar 17 '23 at 15:51