Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Numerical solution of differential equations - initial value problem

Faculty of Mechanical Engineering, University of Ljubljana

Introduction

Writing (a single) differential equation

Assume that a first-order differential equation can be written in explicit form:

y′=f(t,y),y'=f(t, y),

where f(t,y)f(t, y) is a given function and y′=dy/dty'=dy/dt.

Additionally, an initial condition is given:

y(t0)=y0.y(t_0)=y_0.

The goal of solving the differential equation is to compute the function y(t)y(t) that solves the initial value problem defined above. Under certain conditions on the function f(t,y)f(t, y), the initial value problem has a unique solution on an interval containing t0t_0.

In numerical solving we always compute a table of the function y(ti)y(t_i) that solves the given initial value problem. The nodes tit_i are usually equidistant:

t0,t0+h,t0+2h,…t_0, t_0+h, t_0+2h,\dots

and we call hh the (time) step (of integration).

Here we will look at some numerical methods for solving differential equations with an initial condition.

Euler’s method

The explicit Euler’s method is based on the Taylor series expansion of the function yy:

y(t+h)=y(t)+f(t,y(t)) h+O(h2).y(t+h)=y(t)+f(t, y(t))\,h + \mathcal{O}(h^2).

We make a method error of O(h2)\mathcal{O}(h^2) because we neglect the second and higher-order derivatives; now, given a known value y(t)y(t) and the derivative y′(t)=f(t,y)y'(t)=f(t,y), we can estimate the value at the next time step t+ht+h. Once we know the values at t+ht+h, we repeat the procedure!

Steps of Euler’s method:

  1. Set i=0i=0, t0t_0, y0=y(t0)y_0=y(t_0).

  2. Compute the function value at ti+1=ti+ht_{i+1}=t_i+h: yi+1=yi+f(ti,yi) h.y_{i+1}= y_i + f(t_i, y_i)\,h.

  3. i=i+1i=i+1 and continue at step 2.

We solve the differential equation on the interval [t0,tn][t_0,t_n] and h=(tn−t0)/nh=(t_n-t_0)/n. nn is the number of integration steps (how many times we perform step 2 in the algorithm above).

Numerical solution of the initial value problem:

y0,y1,y2,…,yny_0, y_1, y_2,\dots, y_{n}

at the values of the independent variable:

t0,t1,t2 … tn.t_0, t_1, t_2\, \dots \, t_n.
Loading...

Error of Euler’s method

The error of Euler’s method at each step is of order O(h2)\mathcal{O}(h^2).

Since over the interval from t0t_0 to tnt_n we make this error nn times, the cumulative error is n O(h2)=tn−t0h O(h2)=O(h)n\,\mathcal{O}(h^2)=\frac{t_n-t_0}{h}\,\mathcal{O}(h^2)=\mathcal{O}(h).

Locally the error is second order, but globally the error is first order, and because Euler’s method is so inaccurate, we rarely use it in practice!

Error estimate

The exact solution y(tn)y(t_n) at step size hh is:

y(tn)=yn,h+Eh,y(t_n)=y_{n,h}+E_h,

where yn,hy_{n,h} is the numerical approximation and EhE_h is the method error. Since the global error is first order, we can write the error as:

Eh=k h.E_h=k\,h.

Similarly, for step size 2h2h we can write:

y(tn)=yn,2h+E2h,y(t_n)=y_{n,2h}+E_{2h},

where yn,2hy_{n,2h} is the numerical approximation and E2hE_{2h} is the method error:

E2h=k 2 h.E_{2h}=k\,2\,h.

Assuming that the constant kk is the same for step hh and step 2h2h, we can determine the error estimate for the better approximation EhE_h. Clearly:

yn,h+k h=yn,2h+2 k hy_{n,h}+k\,h=y_{n,2h}+2\,k\,h

then we determine the error estimate:

Eh=k h=yn,h−yn,2h.E_h=k\,h=y_{n,h}-y_{n,2h}.

A note on the implicit Euler’s method

In the explicit Euler’s method we compute the solution at ti+1t_{i+1} from the computed value at tit_i.

If the unknown value of the solution at ti+1t_{i+1}, that is yi+1y_{i+1}, appeared on the right-hand side as well, we would be talking about the implicit Euler’s method (or backward Euler’s method):

yi+1=yi+f(ti+1,yi+1) h.y_{i+1}=y_i+f(t_{i+1}, y_{i+1})\,h.

Because the sought value yi+1y_{i+1} appears on both sides of the equation, to determine yi+1y_{i+1} we must solve a (nonlinear) equation. The advantage of the implicit Euler’s method is that it is more stable (e.g., for stiff systems, which we will learn about later) than the explicit form, but it is numerically more demanding (because of solving the equation).

Numerical implementation

First we import the required libraries:

Next let us define Euler’s method:

Let us prepare a function for the error estimate (in numerical terms it would be better to include the error estimate inside the euler function, but for clarity we present it separately):

Numerical example

As an example, let us solve the differential equation that describes a falling body subjected to gravity and air resistance: Falling body

According to Newton’s second law, we can write the differential equation:

m g−c v=m v′,m\,g-c\,v=m\,v',

where mm is the mass, gg the gravitational acceleration, cc the air-resistance coefficient, and vv the velocity. We would like to solve the differential equation subject to the initial condition:

v(0)=0 m/s.v(0)=0\,\textrm{m/s}.

The right-hand side / first-derivative function f(t,y)f(t,y) is:

f(t,y)=g−c ymf(t,y)=g-c\, \frac{y}{m}

and the initial condition:

y0=0.y_0=0.

Let us define the right-hand-side function:

Let us define the initial condition and the time vector where we want the result:

array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])

We call the euler function to compute the values of yy (velocity vv):

array([ 0. , 9.81 , 14.715 , 17.1675 , 18.39375 , 19.006875 , 19.3134375 , 19.46671875, 19.54335938, 19.58167969, 19.60083984])

We plot the result:

<Figure size 640x480 with 1 Axes>

Let us now check the effect of the time step:

<Figure size 640x480 with 1 Axes>

We observe that the numerical error decreases substantially when the step changes from 1 to 0.1!

Let us also estimate the error at 100 and 1000 subintervals:

Loading...
Loading...

When we reduced the step to a tenth, the error decreased proportionally as well (first-order error).

Let us also look at the case where the air resistance c is an argument of the euler function and is passed via **kwargs to the f_air_resistance() function:

<Figure size 640x480 with 1 Axes>

Second-order Runge–Kutta method

Euler’s method is first order (the global error O(h)\mathcal{O}(h) is indeed first order). If we wanted to derive a second-order-error method, we would need to use the Taylor series expansion of y(t+h)y(t+h), where we neglect the third and higher derivatives:

y(t+h)=y(t)+y′(t) h+12y′′(t) h2+O(h3).y(t+h)=y(t)+y'(t)\,h + \frac{1}{2}y''(t)\,h^2+\mathcal{O}(h^3).

The local error of the method will thus be third order and the global error second order.

We use the substitutions y′(t)=f(t,y)y'(t)=f(t,y) and y′′(t)=f′(t,y)y''(t)=f'(t,y):

y(t+h)=y(t)+f(t,y) h+12f′(t,y) h2+O(h3).y(t+h)=y(t)+f(t,y)\,h + \frac{1}{2}f'(t,y)\,h^2+\mathcal{O}(h^3).

Because the right-hand side f(t,y)f(t,y) depends on both the independent variable tt and the dependent variable yy, we must use implicit differentiation:

f′(t,y)=∂f(t,y)∂t+∂f(t,y)∂y dydt⏟y′=f(t,y)=∂f(t,y)∂t+∂f(t,y)∂y f(t,y).f'(t,y)=\frac{\partial f(t,y)}{\partial t}+\frac{\partial f(t,y)}{\partial y}\,\underbrace{\frac{\textrm{d} y}{\textrm{d} t}}_{y'=f(t,y)} =\frac{\partial f(t,y)}{\partial t}+\frac{\partial f(t,y)}{\partial y}\,{f(t, y)}.

We substitute into the Taylor series expression:

y(t+h)Taylor=y(t+h)=y(t)+f(t,y) h+12 (∂f(t,y)∂t+∂f(t,y)∂y f(t,y)) h2.y(t+h)_{\textrm{Taylor}}=y(t+h)=y(t)+f(t,y)\,h + \frac{1}{2}\,{\LARGE(} \frac{\partial f(t,y)}{\partial t}+\frac{\partial f(t,y)}{\partial y}\,{f(t, y)} {\LARGE)}\,h^2.

As can be seen from the expression above, we need additional derivatives. This poses a certain difficulty that can be avoided in various ways; in what follows we will look at the Runge–Kutta approach. Because we will need the expression above later, we have named it here y(t+h)Taylory(t+h)_{\textrm{Taylor}}.

Idea of the Runge–Kutta approach

The dilemma above is resolved by the Runge–Kutta method (developed in 1901) with an idea we have already encountered in Gaussian integration: it tries to find a more accurate solution with a weighted additional value of the function ff:

y(t+h)Runge-Kutta=y(t)+c0 f(t,y) h+c1f(t+p h,y+q h f(t,y))⏟A h.y(t+h)_{\textrm{Runge-Kutta}}=y(t)+c_0\,f\left(t,y\right)\,h +c_1 \underbrace{f{\large(}t+p\,h,y+q\,h\,f(t,y){\large)}}_{A}\,h.

where c0c_0, c1c_1, pp, and qq are unknown constants (in principle from 0 up to and including 1). If we used c1=0c_1=0 in the expression above, we would derive a first-order method; but with an additional function value (AA) it will turn out that we derive a second-order method.

We continue the search for the unknown constants c0c_0, c1c_1, pp, qq by writing AA in the form of a first-order Taylor series:

f(t+p h,y+q h f(t,y))=f(t,y)+∂f(t,y)∂t (p h)+∂f(t,y)∂y (q h f(t,y))⏟B.f{\large(}t+p\,h,y+q\,h\,f(t,y){\large)}= \underbrace{ f{\large(}t,y{\large)}+ \frac{\partial f(t,y)}{\partial t}\,\left(p\,h\right)+ \frac{\partial f(t,y)}{\partial y}\,\left(q\,h\,f(t, y)\right) }_{B}.

Now we substitute the derived BB back into the expression for y(t+h)Runge-Kuttay(t+h)_{\textrm{Runge-Kutta}}:

y(t+h)Runge-Kutta=y(t)+c0 f(t,y) h+c1(f(t,y)+∂f(t,y)∂t (p h)+∂f(t,y)∂y (q h f(t,y))) h.y(t+h)_{\textrm{Runge-Kutta}}=y(t)+c_0\,f\left(t,y\right)\,h +c_1 {\LARGE(} f{\large(}t,y{\large)}+ \frac{\partial f(t,y)}{\partial t}\,\left(p\,h\right)+ \frac{\partial f(t,y)}{\partial y}\,\left(q\,h\,f(t, y)\right) {\LARGE)} \,h.

We continue the derivation and rearrange the equation so that it resembles the one derived above with the Taylor series y(t+h)Taylory(t+h)_{\textrm{Taylor}}:

y(t+h)Runge-Kutta=y(t)+(c0+c1) f(t,y) h+12(∂f(t,y)∂t 2 c1 p+2 c1 q ∂f(t,y)∂y f(t,y)) h2.y(t+h)_{\textrm{Runge-Kutta}}=y(t)+(c_0+c_1)\,f\left(t,y\right)\,h +\frac{1}{2} {\LARGE(} \frac{\partial f(t,y)}{\partial t}\,2\,c_1\,p+ 2\,c_1\,q\,\frac{\partial f(t,y)}{\partial y}\,f(t, y) {\LARGE)} \,h^2.

Now let us compare with the expression derived above:

y(t+h)Taylor=y(t)+f(t,y) h+12 (∂f(t,y)∂t+∂f(t,y)∂y f(t,y)) h2.y(t+h)_{\textrm{Taylor}}=y(t)+f(t,y)\,h + \frac{1}{2}\,{\LARGE(} \frac{\partial f(t,y)}{\partial t}+\frac{\partial f(t,y)}{\partial y}\,{f(t, y)} {\LARGE)}\,h^2.

We find that for equality to hold we must have:

c0+c1=1,2 c1 p=1,2 c1 q=1.c_0+c_1=1,\qquad 2\,c_1\,p=1,\qquad 2\,c_1\,q=1.

So we have three equations and four unknowns. We can therefore choose one of the constants arbitrarily and compute the other three. If, for example, we choose c0=0c_0=0, we would call this the modified Euler’s method and the remaining parameters would be: c1=1c_1=1, p=q=1/2p=q=1/2. The choice of parameters has no essential effect on the solution. It is worth noting, however, that even the second-order Runge–Kutta method is rarely used, since better methods exist.

We substitute the parameters c0c_0, c1c_1, pp, and qq into the first equation of this section. Once the initial time t0t_0 and the initial condition y0y_0 are defined, we use the second-order Runge–Kutta method:

yi+1=yi+f(ti+12 h,yi+12 h f(ti,yi)) h.y_{i+1} = y_i + f\left(t_i+\frac{1}{2}\,h, y_i+\frac{1}{2}\,h\,f(t_i,y_i)\right)\,h.

Fourth-order Runge–Kutta method

Similarly to how we derived the second-order Runge–Kutta method, the fourth-order Runge–Kutta method is derived. For the fourth-order method, too, there are several variants, and by the fourth-order Runge–Kutta method we mean the following method:

yi+1=yi+16(k0+2 k1+2 k2+k3),y_{i+1}=y_i+\frac{1}{6}(k_0+2\,k_1+2\,k_2+k_3),

where:

k0=h f(ti,yi)k1=h f(ti+h2,yi+k02)k2=h f(ti+h2,yi+k12)k3=h f(ti+h,yi+k2).\begin{aligned} k_0&=h\,f(t_i,y_i)\\ k_1&=h\,f\left(t_i+\frac{h}{2},y_i+\frac{k_0}{2}\right)\\ k_2&=h\,f\left(t_i+\frac{h}{2},y_i+\frac{k_1}{2}\right)\\ k_3&=h\,f\left(t_i+h,y_i+k_2\right). \end{aligned}

The steps of the fourth-order Runge–Kutta method are:

  1. Set i=0i=0 and t0t_0, y0=y(t0)y_0=y(t_0),

  2. Compute the coefficients: k0k_0, k1k_1, k2k_2, k3k_3,

  3. Compute the value of the solution of the differential equation at ti+1=ti+ht_{i+1}=t_i+h: yi+1=yi+16(k0+2 k1+2 k2+k3),\quad y_{i+1}=y_i+\frac{1}{6}(k_0+2\,k_1+2\,k_2+k_3),

  4. i=i+1i=i+1 and continue at step 2.

Error of the fourth-order Runge–Kutta method

The fourth-order Runge–Kutta method is named so because it has a local error of fifth order O(h5)\mathcal{O}(h^5), but we make this error nn times, so the global error is fourth order O(h4)\mathcal{O}(h^4).

Error estimate

The exact result y(tn)y(t_n) at step size hh is:

y(tn)=yn,h+Eh,y(t_n)=y_{n,h}+E_h,

where yn,hy_{n,h} is the numerical approximation of the solution and EhE_h is the method error. Since the global error is fourth order, we can write the error as:

Eh=k h4.E_h=k\,h^4.

Similarly, for step size 2h2h we can write:

y(tn)=yn,2h+E2h,y(t_n)=y_{n,2h}+E_{2h},

where yn,2hy_{n,2h} is the numerical approximation of the solution and E2hE_{2h} is the method error:

E2h=k (2 h)4=16 k h4.E_{2h}=k\,(2\,h)^4=16\,k\,h^4.

Assuming that the constant kk is the same for step hh and step 2h2h, we can compute the error estimate for the better approximation EhE_h.

First, it is true that:

yn,h+k h4=yn,2h+16 k h4,y_{n,h}+k\,h^4=y_{n,2h}+16\,k\,h^4,

it follows:

15 k h4=yn,h−yn,2h15\,k\,h^4=y_{n,h}-y_{n,2h}

and then we determine the error estimate of the more accurate solution:

Eh=yn,h−yn,2h15.E_h=\frac{y_{n,h}-y_{n,2h}}{15}.

Numerical implementation

Function for the error estimate:

Numerical example

Let us now look at the example of computing the velocity of a falling mass:

Let us provide the initial condition and the time vector where we want the result:

array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])

For comparison, let us compute the solution with the euler and runge_kutta_4 functions:

We plot the result:

<Figure size 640x480 with 1 Axes>

Let us also look at the numerical error:

Loading...
Loading...

When the step is reduced to a tenth, the error decreased by about 104 times (which matches the expectation for a fourth-order method).

Using scipy to solve ordinary differential equations

The scipy package has many numerical methods implemented for solving initial value problems of ordinary differential equations. Here we will look at the function scipy.integrate.solve_ivp, which is suitable for most initial value problems.

scipy.integrate.solve_ivp

Syntax for use (IVP - Initial Value Problem):

scipy.integrate.solve_ivp(fun, t_span, y0, method='RK45',
                          t_eval=None, dense_output=False,
                          events=None, vectorized=False,
                          args=None, **options)

An explanation of all the arguments is in the documentation; here we will highlight a few:

  • fun is the right-hand side (func(t, y...)),

  • t_span a tuple (t0, tf) defining the initial time t0 and final time tf,

  • y0 a list of initial value(s),

  • method defines the numerical method (the default is the explicit ‘RK45’; for stiff systems the following are better: ‘Radau’, ‘BDF’, ‘LSODA’),

  • t_eval is used if we want the solution at specific values of the independent variable,

  • dense_output whether a continuous solution is also prepared, default False,

  • events for tracking events that stop solving the initial value problem (e.g., when the mass touches the ground and the relative distance to the ground is zero).

  • args for passing arguments into the function fun (see the example below).

The result of calling solve_ivp is an object with attributes (selected):

  • t the values of the independent variable at which the result is computed,

  • y the result,

  • sol the continuous-solution function (only in the case of dense_output=True)

  • success is True if the solution was computed successfully or terminated due to an event (events).

Numerical example

Let us define the initial condition and the time vector where we want the result (we show only the first ten elements):

array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

We pass the arguments (g=9.81, m=1., c=0.4) into the function via the args parameter:

We could also use a lambda expression (documentation):

y_ivp = solve_ivp(lambda t,y: f_air_resistance(t, y, c=0.4), t_span=(t[0], t[-1]), y0=[v0])

The lambda expression used above, lambda t, y: f_air_resistance(t, y, c=0.4), is equivalent to:

def function_name(t, y):
    return f_air_resistance(t, y, c=0.4)

Let us look at the result:

message: The solver successfully reached the end of the integration interval. success: True status: 0 t: [ 0.000e+00 1.000e-04 1.100e-03 1.110e-02 1.111e-01 1.111e+00 2.982e+00 5.236e+00 7.948e+00 1.000e+01] y: [[ 0.000e+00 9.810e-04 1.079e-02 1.086e-01 1.066e+00 8.800e+00 1.708e+01 2.150e+01 2.350e+01 2.407e+01]] sol: None t_events: None y_events: None nfev: 56 njev: 0 nlu: 0

Because solve_ivp is already set up for a system of first-order ordinary differential equations, the result is given in two dimensions. The sought result is therefore: y_ivp.y[0].

For comparison, here is also the result with our own fourth-order Runge–Kutta implementation:

array([0. , 0.96163898, 1.88557156, 2.77327623, 3.6261735 , 4.44562819, 5.23295161, 5.98940363, 6.71619474, 7.41448797])

We plot the result:

<Figure size 640x480 with 1 Axes>

System of ordinary differential equations

Above we looked at solving the initial value problem of a single ordinary differential equation; now we will generalize the solving to an initial value problem for a system of mm first-order ordinary differential equations.

Such a system is written as:

y′=f(t,y),\mathbf{y}'=\mathbf{f}(t, \mathbf{y}),

where additional (initial) conditions are given:

y(t0)=y0.\mathbf{y}(t_0)=\mathbf{y}_0.

By tt we denote the independent variable (it need not always be time) and by f\mathbf{f} the vector of right-hand sides.

We compute the solution of a system of mm differential equations y′=f(t,y)\mathbf{y}'=\mathbf{f}(t, \mathbf{y}) at a=t0,t1,…,tn−1=ba=t_0, t_1,\dots,t_{n-1}=b, that is mm function values at each step yk(ti)y_k(t_i), k=0,1,…,m−1k=0,1,\dots,m-1 and i=0,1,…,n−1i=0,1,\dots,n-1. It can be proved that any method we derived for first-order differential equations can also be used for a system of first-order ordinary differential equations if we replace the scalar quantities with the corresponding vector ones.

Numerical implementation

The Euler and fourth-order Runge–Kutta methods need only minor adjustments (y0 is a numerical array, f returns a list of derivative values):

Numerical example

We extend the falling mass to planar motion; let the magnitude of the air-resistance force be defined as:

∣F(v)∣=c ∣v∣2.|\mathbf{F}(\mathbf{v})|=c\,|\mathbf{v}|^2.

The air-resistance force acts in the opposite direction to the velocity vector. Falling body coupled

With the help of the figure, we define the force in the xx direction:

Fx=−c (vx2+vy2) cos⁡(α)=−c (vx2+vy2) vxvx2+vy2=−c vx vx2+vy2.F_x=-c\,\left(v_x^2+v_y^2\right)\,\cos(\alpha)=-c\,\left(v_x^2+v_y^2\right)\,\frac{v_x}{\sqrt{v_x^2+v_y^2}}=-c\,v_x\,\sqrt{v_x^2+v_y^2}.

Similarly, the force in the yy direction is:

Fy=−c vy vx2+vy2.F_y=-c\,v_y\,\sqrt{v_x^2+v_y^2}.

According to Newton’s second law, we write a system of two (coupled) first-order differential equations:

Fx=m vx′,F_x=m\,v_x',
m g+Fy=m vy′,m\,g+F_y=m\,v_y',

mm is the mass, gg the gravitational acceleration, cc the air-resistance coefficient, and vxv_x and vyv_y the velocities in the xx and yy directions, respectively. We would like to solve the differential equation subject to the initial condition:

vx(0)=vy(0)=5 m/s.v_x(0)=v_y(0)=5\,\textrm{m/s}.

Let us define the list of right-hand sides:

Let us define the initial condition and the time vector where we want the result:

array([0. , 0.05, 0.1 , 0.15, 0.2 ])

For comparison, let us compute the solution with the runge_kutta_4 and solve_ivp functions:

Let us look at the result:

array([[5. , 4.23209907, 3.63858619, 3.16619484, 2.78087608], [5. , 4.68459713, 4.48359747, 4.35932897, 4.28742151]])
array([[5. , 4.23211316, 3.6399137 , 3.16660748, 2.77780226], [5. , 4.68462761, 4.48494405, 4.35999931, 4.28466858]])

We plot the result:

<Figure size 640x480 with 1 Axes>

Reformulating a higher-order differential equation into a system of first-order differential equations

We will look at how an ordinary differential equation of arbitrary order nn:

y(n)=f(t,y,y′,y′′,…,y(n−1)),y^{(n)}=f(t, y, y', y'',\dots,y^{(n-1)}),

with initial conditions:

y(t0)=k0,y′(t0)=k1,…y(n−1)(t0)=kn−1y(t_0)=k_0,\quad y'(t_0)=k_1,\quad\dots\quad y^{(n-1)}(t_0)=k_{n-1}

is reformulated into a system of first-order differential equations.

First, instead of derivatives we introduce new variables:

yi=y(i),i=0,1,…,n−1.y_i=y^{(i)}, \qquad i=0,1,\dots,n-1.

The nnth-order differential equation written with the new variables is:

yn−1′=f(t,y0,y1,y2,…,yn−1).y_{n-1}'=f(t, y_0, y_1, y_2,\dots,y_{n-1}).

We differentiate the new functions with respect to the independent variable:

yi′=y(i+1)=yi+1,i=0,1,…,n−2y_i'=y^{(i+1)}=y_{i+1}, \qquad i=0,1,\dots,n-2

and

yn−1′=f(t,y0,y1,y2,…,yn−1).y_{n-1}'=f(t, y_0, y_1, y_2,\dots,y_{n-1}).

We have obtained a system of first-order ordinary differential equations:

y0′=y1y1′=y2…yn−1′=f(t,y0,y1,y2,…,yn−1)\begin{array}{rcl} y_0'&=&y_1\\ y_1'&=&y_2\\ &\dots\\ y_{n-1}'&=&f(t, y_0, y_1, y_2,\dots,y_{n-1})\\ \end{array}

with initial conditions:

y0(t0)=k0,y1(t0)=k1,…y(n−1)(t0)=kn−1.y_0(t_0)=k_0,\quad y_1(t_0)=k_1,\quad\dots\quad y_{(n-1)}(t_0)=k_{n-1}.
Numerical example

We return to the falling mass: Beam

But this time let us write Newton’s second law in terms of displacement (a second-order differential equation):

Fx=m x′′,F_x=m\,x'',
m g+Fy=m y′′,m\,g+F_y=m\,y'',

mm is the mass, gg the gravitational acceleration, cc the air-resistance coefficient, and x′′x'' and y′′y'' the acceleration in the chosen coordinate system. Initial conditions:

x(0)=y(0)=0 mandx′(0)=y′(0)=5 m/s.x(0)=y(0)=0\,\textrm{m}\qquad\textrm{and}\qquad x'(0)= y'(0)=5\,\textrm{m/s}.

We have a system of two second-order differential equations. By introducing new variables yiy_i:

y0=x,y1=x′,y2=y,y3=y′.y_0= x,\quad y_1= x',\quad y_2= y,\quad y_3= y'.

We prepare the system of first-order differential equations:

y0′=y1y1′=Fx/my2′=y3y3′=g+Fy/m\begin{array}{rcl} y_0'&=&y_1\\ y_1'&=&F_x/m\\ y_2'&=&y_3\\ y_3'&=&g+F_y/m \end{array}

Let us define the Python function of right-hand sides / first derivatives:

Let us define the initial condition and the time vector where we want the result:

array([0. , 0.05, 0.1 , 0.15, 0.2 ])

We compute the solution:

Let us look at the result ([x,x′,y,y′][x, x', y, y']):

array([[0. , 0.22992477, 0.42611996, 0.59587483, 0.74424286], [5. , 4.23183658, 3.64002431, 3.16703014, 2.77775353], [0. , 0.24153615, 0.47038377, 0.69124852, 0.9072755 ], [5. , 4.68435696, 4.48497294, 4.36034218, 4.28444004]])

We plot the results; first the velocity, then the position (the yy coordinate is positive downward)!

<Figure size 640x480 with 1 Axes>
<Figure size 640x480 with 1 Axes>

Stability of solving differential equations*

The numerical solution of differential equations is subject to method error and round-off error. These errors differ for different numerical methods.

Solving a differential equation is stable if a small change in the initial condition leads to a small change in the computed solution; otherwise we speak of instability of the solution.

Stability depends on the differential equation, on the numerical method used, and on the integration step hh.

Example of a simple oscillator

Let us first look at an example of solving the differential equation of a simple oscillator. Oscillator figure

The figure (source: Slavič, Dinamika, mehanska nihanja in mehanika tekočin, 2017) shows a dynamic system (mass mm, stiffness kk) whose differential equation is

m x′′+k x=0.m\, x'' + k\,x=0.

We reformulate this differential equation into the standard form of free vibration:

x′′+ω02 x=0,x'' + \omega_0^2\,x=0,

where the natural angular frequency is:

ω0=km\omega_0=\sqrt{\frac{k}{m}}

and we expect a response of the form:

x(t)=A cos⁡(ω0 t)+B sin⁡(ω0 t).x(t)=A\,\cos(\omega_0\,t)+B\,\sin(\omega_0\,t).

If the initial conditions are:

x(0 s)=x0andx′(0 s)=0 m/s,x(0\,\textrm{s})=x_0\qquad\textrm{and}\qquad x'(0\,\textrm{s})=0\,\textrm{m/s},

the solution of the initial value problem is:

x(t)=x0 cos⁡(ω0 t).x(t)=x_0\,\cos(\omega_0\,t).
Numerical example

First let us define the vector of initial conditions and the function of right-hand sides / first derivatives (we convert the second-order differential equation into a system of first-order differential equations y′=f(t,y)\mathbf{y}'=\mathbf{f}(t, \mathbf{y})), where y0=x,y1=x′y_0=x, y_1=x':

Let us define the data and the analytical solution:

Solution using the Euler and fourth-order Runge–Kutta methods:

Let us plot the results:

<Figure size 640x480 with 1 Axes>

We observe that Euler’s method is unstable, and if we increased the step, the fourth-order Runge–Kutta method would also become unstable.

Why is Euler’s method so unstable?
array([ 1. , 1. , 0.93683453, 0.8105036 , 0.62499707, 0.3882947 , 0.1121141 , -0.18859332, -0.49638247, -0.79225904])

Let us recall Euler’s method:

x(t+h)=x(t)+x′(t) h,x(t+h)=x(t)+x'(t)\,h,

which tells us that we determine the displacement x(t+h)x(t+h) from the position x(t)x(t) and the velocity x′(t)x'(t). The initial conditions stem from the maximum deflection x(t=0)=1x(t=0)=1, and at that point the velocity is x′(t=0)=0x'(t=0)=0, which means that x(t+h)=1x(t+h)=1. Already in the first step we therefore make a relatively large error. But why does the value then start to grow in an alternating fashion?

Recall that the analytical solution is x(t)=x0 cos⁡(ω0 t)x(t)=x_0\,\cos(\omega_0\,t), and therefore x′(t)=−ω0 x0 sin⁡(ω0 t)x'(t)=-\omega_0\,x_0\,\sin(\omega_0\,t).

We substitute the prepared expressions into Euler’s method and rearrange:

x(t+h)=x(t)+x′(t) h=x0 (cos⁡(ω0 t)−ω0 h sin⁡(ω0 t)).x(t+h)=x(t)+x'(t)\,h=x_0\,\left(\cos(\omega_0\,t)-\omega_0\,h\,\sin(\omega_0\,t)\right).

Assume we look at the state at a time t=π/(2ω0)t=\pi/(2\omega_0), when cos⁡(ω0 t)=0\cos(\omega_0\,t)=0 and sin⁡(ω0 t)=1\sin(\omega_0\,t)=1:

x(t+h)=x0 (−ω0 h)⏟A.x(t+h)=x_0\,\underbrace{\left(-\omega_0\,h\right)}_{A}.

If the absolute value of the expression AA is greater than 1, then at time t+ht+h the value will be greater than in the previous step, and probably again in the next one. It follows that instability can occur. To avoid it, the following must hold:

∣A∣<1→h<1ω0.|A|<1\qquad\rightarrow\qquad h<\frac{1}{\omega_0}.

Note: in some books you will also see the value h<2/ω0h<2/\omega_0; a unique bound for all differential equations cannot be defined; in general, however, the step is defined relative to the shortest period TT in the differential equation (e.g.: h<2/ω0h<2/\omega_0 is essentially the same as h<2/(2π/T)h<2/(2\pi/T), i.e., h<T/πh<T/\pi). The period TT is defined relative to the highest natural frequency of the system T=1/fmaxT=1/f_{\textrm{max}}, which we compute from the eigenvalue of the system.

Additional: Example of the Van der Pol oscillator

The purpose of this example is to show how the choice of integrator can affect the speed of solving a problem! The Van der Pol oscillator is described here.

Let us define the list of derivatives:

Solution using the RK45 method (this is an explicit scheme, which is not suitable for stiff systems of differential equations; solving is very slow, so we compute the solution only up to t1/100):

The implicit BDF scheme (Backward Differentiation Formulas) turns out here to be much more suitable. Because of its stability, the steps can be much larger and therefore the solving is much faster:

<Figure size 640x480 with 1 Axes>

Additional: symbolic solving of a second-order differential equation

We will look at the example shown in the figure, where a mass mm is on an incline of slope α\alpha. The coefficient of friction is μ\mu, and the gravitational acceleration is gg. The initial velocity is y˙(0 s)=v0\dot y(0\,\textrm{s})=v_0, the displacement y(0 s)=0 my(0\,\textrm{s})=0\,\textrm{m}. Mass on an incline The equation of motion (only for the yy direction) is defined according to Newton’s second law (see the free-body force diagram).

Derivation of the equation of motion
Loading...

The solution of the equation is:

Loading...

To determine C1C_1 and C2C_2, we substitute t=0 st=0\,\textrm{s}:

Loading...

Then we differentiate with respect to time and again substitute t=0 st=0\,\textrm{s}:

Loading...

Based on the initial conditions, we have therefore determined the constants:

The solution follows:

Loading...

Let us prepare functions for the numerical call:

Displacement at 0s: 0m
Velocity at 0s: 1m/s

Let us prepare the plot:

<Figure size 640x480 with 1 Axes>
Symbolic reformulation of the differential equation into a system of first-order differential equations

Let us recall the original differential equation:

Loading...

Let us define new variables and prepare the function ff:

Loading...

Now let us connect the new variables.

Let dy0/dtd y_0/dt be equal to y1y_1:

Loading...

Let the derivative dy1/dtd y_1/dt (which is essentially y′′y'') be equal to the function ff:

Loading...

We write the expressions above in vector form:

y′=f(t,y).\mathbf{y}'=\mathbf{f}(t, \mathbf{y}).
Loading...
Loading...

Let us now recall f_vec:

Loading...

If we solve numerically, then we write the function f(t,y)\mathbf{f}(t, \mathbf{y}) as:

Loading...

Let us check the function at the initial time t=0 t=0\,s and at the initial conditions [y0,y1]=[0,v0][y_0, y_1]=[0, v_0]:

array([0., 1.])
array([ 1. , -0.30370487])

Now let us use Euler’s method:

array([[ 0. , 1. ], [ 1. , 0.69629513], [ 1.69629513, 0.39259025], [ 2.08888538, 0.08888538], [ 2.17777075, -0.2148195 ]])

We plot and compare with the analytical solution:

Loading...