Numerical Methods for Differential Equations 1 2 NUMERICAL METHODS FOR DIFFERENTIAL EQUATIONS Introduction Differential equations can describe nearly all systems undergoing change. They are ubiquitous is science and engineering as well as economics, social science, biology, business, health care, etc. Many mathematicians have studied the nature of these equations for hundreds of years and there are many well-developed solution techniques. Often, systems described by differential equations are so complex, or the systems that they describe are so large, that a purely analytical solution to the equations is not tractable. It is in these complex systems where computer simulations and numerical methods are useful. The techniques for solving differential equations based on numerical approximations were developed before programmable computers existed. During World War II, it was common to find rooms of people (usually women) working on mechanical calculators to numerically solve systems of differential equations for military calculations. Before programmable computers, it was also common to exploit analogies to electrical systems to design analog computers to study mechanical, thermal, or chemical systems. As programmable computers have increased in speed and decreased in cost, increasingly complex systems of differential equations can be solved with simple programs written to run on a common PC. Currently, the computer on your desk can tackle problems that were inaccessible to the fastest supercomputers just 5 or 10 years ago. This chapter will describe some basic methods and techniques for programming simulations of differential equations. First, we will review some basic concepts of numerical approximations and then introduce Euler's method, the simplest method. We will provide details on algorithm development using the Euler method as an example. Next we will discuss error approximation and discuss some better techniques. Finally we will use the algorithms that are built into the MATLAB programming environment. The fundamental concepts in this chapter will be introduced along with practical implementation programs. In this chapter we will present the programs written in the MATLAB programming language. It should be stressed that the results are not particular to MATLAB; all the programs in this chapter could easily be implemented in any programming language, such as C, Java, or assembly. MATLAB is a convenient choice as it was designed for scientific computing (not general purpose software development) and has a variety of numerical operations and numerical graphical display capabilities built in. The use of MATLAB allows the student to focus more on the concepts and less on the programming. 1.1 FIRST ORDER SYSTEMS A simple first order differential equation has general form §=/(*)■ a-47) The Runge-Kutta method is defined as: kl = Atf(tN ,yN) (1.48) k2 = Atf(tN + At/2,yN + kl/2) (1.49) fc3 = Atf(tN + At/2,yN + k2/2) (1.50) k4 = Atf(tN + At,yN + k3) (1.51) yN+1 nj lz\ Ä/2 Ä/3 Ä/4 =y +y+y+y+y (1.52) One should note the similarity to the midpoint method discussed in the previous section. Also note that each time step requires 4 evaluations of the derivatives, i.e. the function f. The programming of this method will follow the format used already for the midpoint and Euler methods and is provided in the program below. Program 1.10: General Runge-Kutta solver that has the same usage as the the Euler and midpoint solvers created earlier. This program should be created and placed in a fi le called rksolver . m. %% Runge-Kutta Solver function [t, data] = rksolver (y, dt, t_f inal, derivs_Handle ) time - 0; Nsteps = round(t_final/dt) %% number of steps to take. t = zeros(Nsteps,1); data = zeros(Nsteps,length(y)); t(l) = time; %% store intial condition data(1,:) = y'; for i =l:Nsteps kl = dt*feval(derivs_Handle,time ,y ); k2 = dt*feval(derivs-Handle,time + dt/2,y + kl/2); BUILT-IN MATLAB SOLVERS 19 10 10" 10" ,-5 ,-10 ,-15 10" ,-20 10" 10" ,-3 10" 10' Fig. 1.11 The error between the Runge-Kutta method and exact solution as a function of time step size. One the plot we also display a function that scales as At4. We see that this fits the slop of the data quite well, therefore error in the Runge-Kutta approximation scales as At4. Once the error reaches 10-15 then the error is dominated by the resolution of double precision numbers. k3 = dt*feval(derivs_Handle,time+dt12, y + k212); k4 = dt*f eval (derivs_Handle, time + dt , y + k3 ); y = y + kl/6 + k2/3 + k3/3 + k4/6; time = time+dt; t(i+l) = time; data(i+1,:) = y'; Since we have only given the equations to implement the Runge-Kutta method it is not clear how the error behaves. Rather than perform the analysis, we will compute the error by solving an equation numerically and compare the result to an exact solution as we vary the time step. To test the error we solve the model problem, dy/dt = —y, where y(0) = 1 and we integrate until time t = 1. We have conducted this same test with the midpoint and Euler solvers. In Figure 1.11 we plot the error between the exact and numerical solutions at t = 1 as a function of the time step size. We also plot a function Ai4 on the same graph to show that the error of the Runge-Kutta method scales as Ai4. This is quite good - if we halve the time step size we reduce the error by 16 times. To generate this figure only requires minor modification to Program 1.9. The minimum error of 10-15 is due to the accuracy of representing real numbers with a finite number of digits. 1.5 BUILT-IN MATLAB SOLVERS At this point it is worth introducing the ODE solvers that are built into MATLAB. These solvers are very general, employ adaptive time stepping (speed up or slow down when it needs to), and use the Runge-Kutta method as the basic workhorse. So you ask, if MATLAB can do all this already then why did you make us write all these programs? Well, it is very easy to employ packaged numerical techniques and obtain bad answers, especially in complex problems. It is also easy to use a package that works just fine, but the operator (i.e. you) makes a mistake and gets a bad answer. It is important to understand some of the basic issues of ODE solvers so that you will be able to use them correctly and intelligently. On the other hand, if other people have already spent a lot of time developing and debugging sophisticated techniques that work really well, why should we replicate all their work? We turn to these routines at this time. Just as you have developed three solvers that have the same functionality, MATLAB has employed several different algorithms for solving differential equations. The most common of the MATLAB solvers is ode 4 5; the end 20 NUMERICAL METHODS FOR DIFFERENTIAL EQUATIONS usage of the function will be very similar to the routines that you wrote for the Euler method, midpoint method, and Runge-Kutta. The ode 4 5 command uses the same Runge-Kutta algorithm you developed, only the MATLAB version uses adaptive time stepping. With these algorithms, the user specifies the amount of acceptable error and the algorithm adjusts the time step size to maintain this value constant. Therefore, with adaptive algorithms you cannot generate a plot of error vs. time step size. The algorithm does have the flexibility to force a fixed time step size. In general these adaptive algorithms work by comparing the difference between taking a step with two methods that have different orders (i.e. midpoint (Ai2) and Runge-Kutta (Ai4)). The difference is indicative of the error, and the time step is adjusted (increased or decreased) to hold this error constant. An example of the usage of MATLAB's ode 4 5 command is illustrated in the commands below. We show the same example of the mass on the spring as in previous sections. Below we can see that the usage of MATLAB the ode 4 5 command is quite similar to the methods we developed in previous sections. The command ode set allows the user to control all the options for the solver including method, maximum time step size, acceptable error tolerances, and output format options. The reader should consult the MATLAB help functions to discover the different options with the odeset command. options = odeset('AbsTolle-9); %% set solver options [t,y] = ode45(@derivs_spring,[0 30],1,options); %% solve equations plot(t,y); %% plot position Notice that the assumption of the ode 4 5 command is that the function that supplies the derivatives has the formdy_dt = der ivativef unction (t, y). We have assumed the same format for the derivative functions throughout this chapter. You may use the same derivative functions with the routines that you have written as well as the MATLAB solvers. Besides ode 4 5, MATLAB has several other solvers that are designed for different types of equations. There are also a variety of plotting and display functions that accompany the differential equation solvers. The functionality of MATLAB is well documented on their web-page and we leave it to the student to explore the different functions. 1.6 CHECKING THE SOLUTION One of the common difficulties in using numerical methods is that takes very little time to get an answer, it takes much longer to decide if it is right. The first test is to check that the system is behaving physically. Usually before running simulations it is best to use physics to try and understand qualitatively what you think your system will do. Will it oscillate, will it grow, will it decay? Do you expect your solution to be bounded, i.e. if you start a pendulum swinging under free gravity you would not expect that the height of the swing would grow. We already encountered unphysical growth when we solved the mass-spring motion using Euler's method in Figure 1.6. When the time step was large we noticed unphysical behavior: the amplitude of the mass on the spring was growing with time. This growth in oscillation amplitude is violating the conservation of energy principle, therefore we know that something is wrong with the result. One simple test of a numerical method is to change the time step and see what happens. If you have implemented the method correctly the answer should converge as the time step is decreased. If you know the order of your approximation then you know how fast this convergence should happen. If the method has an error proportional to Ai then you know that cutting the time step in half should cut the error in half. You should note that just because the solution converges does not mean that your answer is correct. The MATLAB routines use adaptive time stepping, therefore you should vary the error tolerance rather than the time step interval. You should always check the convergence as you vary the error. Plot the difference between subsequent solutions as you vary the error tolerance. Finally, we note that most of the examples that we cover in this class are easily tackled with with the tools presented in this chapter. This does not mean that there are not systems where the details of the numerical method can contaminate the results. However, the algorithms included with MATLAB are very robust and work well in many applications. EXAMPLES 21 1.7 EXAMPLES In this final section we will introduce some systems that have larger equation sets and exhibit some non-linear behaviors. These examples are meant to provide further guidance to the student on implementing numerical methods for a variety of problems. This section is also meant to look at systems that have interesting and complex behavior that is not tractable via pure mathematical analysis. However, these results coupled with more advanced analysis techniques can uncover even more unusual system behavior. 1.7.1 Lorentz Attractor Lorentz proposed a system of differential equations as a simple model of atmospheric convection and hoped to use his equations to aid in weather prediction. The details of the derivation of the model are beyond the scope of this course, so we will have to take the equations for granted. Since the resulting equations were very complex, Lorentz solved his equations numerically. Computers were slow at this time and one day, rather than re-run a particular calculation from time=0, he used the data written out by the program from a previous day at an intermediate time. He noticed that he when he solved his equations, he got completely different answers if he started the solution from the beginning or stopped halfway and restarted. Lorentz tracked the difference down to the fact that when he typed in the restart conditions, he only was using the first few significant digits. He soon discovered that the system was very sensitive to the initial condition. For only a small change in initial condition the solution to the equations significantly diverged over time. Further, he also noticed that while the variables plotted as a function of time seemed random, the variables plotted against each other showed regular and interesting patterns. We will explore his system using the numerical solvers that we have developed. The system of equations that Lorentz developed with were dx — = W(y-x) (1.53) ^=x(27-z)-y (1.54) — = xy--z (1.55) dt y 3 We will not discuss the derivation of these equations but they were based on physical arguments relating to atmospheric convection. The variables x, y, z represent physical quantities such as temperatures and flow velocities, while the numbers 10, 27, and 8/3 represent properties of the atmospheric system. The constants are not universal and the system will behave differently for different constants. For the purposed of this section we will take these numbers as a given. In order to solve this system of equations, we need only implement the derivatives into a function file and then we may use the ode 4 5 command or the solvers that we have written to generate the solution. Program 1.11: Function to compute the derivatives of the Lorentz equations. function dy = lorentz (time, y) dy = zeros(3,1); X = 1; Y = 2; Z = 3; dy(X) = 10*(y(Y)-y(X)); dy(Y) = y(X)*(27-y(Z)) -y (Y); dy(Z) = y(X)*y(Y) - 8/3*y(Z); Some interesting results are shown in Figure 1.12. In this figure we demonstrate several interesting features of the Lorentz equations. The first concept is that of sensitivity to initial conditions. The first image shows the time history of two initial conditions that differ by only 1%. The two systems evolve identically for some time then diverge. Such a basic result is one reason why detailed weather prediction is difficult more than a few days out. Regardless of the quality of the model, simply not knowing the precise condition to start the model (i.e. today's weather) means that details cannot be predicted far in the future. The next plot shows the evolution using different solvers, midpoint and Runge-Kutta. The same reason the equations are sensitive to the initial condition makes them sensitive to the details of the numerical method. Just like the small differences in the initial condition caused a very 22 NUMERICAL METHODS FOR DIFFERENTIAL EQUATIONS Fig. 1.12 Various results generated from the Lorentz equations. The fi gures go left to right, then down and are as follows: i) shows the time history of the variable x for the initial condition [1,0,0] (solid line) and [1,0.01,0.01] (dashed line); we see the sensitivity to the initial condition; ii) shows the time history of x for the same initial condition but for the Runge-Kutta (solid) and midpoint (dashed) solvers; iii) shows the plot of a; vs. 2, even though the variables have a random looking time history the solution predictably lies somewhere on the "butterfly"; iv) the same plot as iii, only the parameter 27 was changed to 20 in the equation for dy/dt. different evolution, errors in the numerical method can cause too numerical solutions to be very different. The next plot shows the plot of x versus z. Even though the time history will follow a somewhat random and unpredictable pattern, predictably any current state will always fall somewhere on the butterfly. One can think that this plot shows that even though the detailed state of the system is unknown, there is some predictability and pattern to the behavior. Finally, we change the parameter 27 to 20 in the equation for dy jdt and see that the system behaves quite differently. The system is not only sensitive to the initial condition but is sensitive to the parameters in the equation. When the parameter is 27 the system oscillates around the "butterfly" forever. When the parameter is 20 the system is drawn to a steady state. This system has been much discussed in the literature and many detailed results and analysis exist therein. These results were meant to give the student a taste of complex behavior and interesting systems that can be easily approached using numerical methods. 1.7.2 Forced Pendulum A simple pendulum can be an extremely rich non-linear system. Imagine a heavy mass, m on the end of rigid and light rod of length L. The other end of the rod is connected to a small motor which supply a torque, r. We drive the motor in a sinusoidal fashion and we are able to control the torque and frequency, ui. Gravity, g, acts downward and the angle of the pendulum, 9, is considered zero when at rest. Friction in the motor and bearings supplies a torque proportional to the angular velocity with a coefficient, /3. Applying Newton's laws we obtained the force balance as d?9 dB mL—^ = —mgsin(6) — /3— + rsin(wi) (1.56) Civ Civ which can be rearranged to read M=-iSm(6)-£Ldt+^LSm("t)- (L57) PROBLEMS 23 250 260 270 280 250 260 270 280 t t 250 260 270 280 250 260 270 280 t t Fig. 1.13 Evolution of 9 with time for different forcing amplitudes. Going left to right then down, the amplitude is 1.6, 1.61, 2, and 7. We see in the fi rst two images that the pendulum is swinging at a steady state. A little more energy and the pendulum swings over the top. A further simplification arises if we redefine a scaled time that has no units and is scaled by the natural frequency. The new time is defined as i=ty/g/L. (1.58) Making this substitution yields = _sin(0) _ _JL^ + ^sin(udy/i/L). (1.59) dt2 my/gL dt mg We choose to force the pendulum at its natural frequency (much like a child on a swing would like to do) and the damping parameter is chosen to be 0.2. We can now study a variety of interesting system behaviors. Program 1.11: Function to compute the derivatives of the pendulum equations. function dy = pend(time,y) dy = zeros(2,1); dy(1) = y(2); %%% dtheta/dt = omega dy(2) = -0.2*y(2) - sin(y(l)) + 2*sin(time); %%% d omega/dt = -v theta In Figure 1.13 we show the non-linear evolution of the pendulum system. In the four images, we show the evolution of 6 for different forcing amplitudes. In the first two images we find that a small increase in the amount of forcing energy takes the pendulum from a steady oscillation between ±140 degrees to a more random oscillation that swings over the top. The non-linear behavior as the pendulum swings at high amplitudes allows this sudden transition rather than an steadily increasing steady oscillation amplitude to ±180°. Another way to represent the complex, non-linear behavior of this system is to ask the question, in the high forcing case does the system first over-rotate going clockwise or counter-clockwise? We span the space of initial conditions in both angle and angular velocity and plot a black pixel for clockwise and and white pixel for counterclockwise. The result of this exercise is shown in Figure 1.13. We observe very complex structure and find that the system is quite sensitive to the initial condition. For such a simple system it is very difficult to make this precise prediction in a real physical system. Problems 1.1 The numerical derivative is only an approximation, in this problem we will explore the error in that approximation. When the true solution and the numerical solution are known, one definition of the error is the relative 24 NUMERICAL METHODS FOR DIFFERENTIAL EQUATIONS Fig. 1.14 Map of the direction that the pendulum fi rst rotates past 9 = ■k. The black pixels are for clockwise rotation and the white pixels for counter-clockwise. The x and y axis are the initial condition space, 9 and uj respectively. The pendulum has a forcing amplitude of 3. The interesting features is that one can zoom in even further and fi nd fi ner structure and features of this plot. difference between the true and approximate derivative: Vexact Vnumerical error =- Vexact Modify Program 1 to compute the error between the true and numerical derivative and plot the error as a function of time. Change the number of time samples from 20 to 10, and rerun your error program. What happened to the error? Try changing the number of samples to 40. Predict what will happen if you change the number of samples to 200. Re-run your program and see if you are correct. 1.2 Modify program 1 to plot the numerical derivative and analytical derivative of the following functions on the interval 0 < t < 5. Compare the analytical and the numerical result by plotting both functions on the same plot: y = t3 - t, y = e"5*, y = log(t). 1.3 Implement Euler's method (Program 2) for the equation dy/dy = —y with y(0) = 1. Verify Figure 1.3. 1.4 Adjust the size of At to observe that the exact and numerical solution become closer to each other as you make the time step smaller and take more time steps. Define the error as the absolute difference between the analytical and numerical solutions at t = 2. Using the programs created above, compute the error at t = 2. Check the error for At = 0.5, At = 0.25, and At = 0.125. What happens to the error when you change the time step size by a factor of 2? 1.5 Write out the first 4 terms of the Taylor series for the function f{x) = sin(x). Plot the true function and the approximation as each term is added on the interval 0 < x < it. 1.6 Consider the differential equation dy . , . — = sm(y) with the initial condition y = 1. Using a time step of St = 0.1 what is numerical value of the error after one time step with Euler's method. 1.7 Implement Programs 1.4-1.6 and make sure that they can all provide the same results as presented in this chapter. Work with each program to understand each step. Modify the program as you wish to fit your own programming style. PROBLEMS 25 1.8 Implement the midpoint solver as shown in Program 1.9 and verify that your program is working correctly. Reproduce Figure 1.9. 1.9 Damping due to friction provides a force proportional to velocity by some constant, /3. Add friction to the differential equations for the mass spring system. Plot the solution for /3 = [0.01,0.1,10] on the same graph. Solve using the Midpoint method and Euler's method. 1.10 Implement the Runge-Kutta solver as shown in Program 1.9 and verify that it is working correctly by solving the basic first order system and the mass-spring system. Reproduce Figure 1.9 showing the comparison of Euler's method, the midpoint method, and Runge-Kutta.