10.1 Introduction
10.2 Initial-Value Problem for First-Order ODE
10.3 Taylor Series Method
10.4 Runge-Kutta of Order 2 Method
10.5 Runge-Kutta of Order 4 Method
10.6 Predictor-Corrector Multistep Method
10.7 System of First-Order ODEs
10.8 Second-Order ODE
10.9 Initial-Value Problem for Second-Order ODE
10.10 Finite-Difference Method for Second-Order ODE
10.11 Differentiated Boundary Conditions
10.12 Visual Solution: Code10
10.13 Summary
Numerical Exercises
Programming Challenges
Problems involving the ordinary differential equation (ODE) arise in many areas, including engineering, natural sciences, medicine, economics, and anthropology. These problems are normally generally numeric-intensive and require fast computers in their implementation. Solutions to these problems are normally formulated as models that involve ordinary and partial differential equations. A mathematical model is the general solution to a given problem that is subject to some conditions and limitations. A typical model works best when all conditions and constraints in the problem are satisfied. At the same time, the model may not work if one or more of these conditions are not satisfied.
An ordinary differential equation is an equation that has one or more terms in the expression in the form of derivatives. A good understanding of problems involving ordinary differential equations is necessary in order to produce efficient mathematical models and their simulations on the computer. In engineering, models involving ordinary differential equations are commonly deployed as the fundamentals for the overall solution to a given problem.
Some well-known models for problems in engineering from ordinary differential equations are shown below:
Decay Equation:
Damped harmonic oscillator equation:
Pendulum equation:
Van der Pol equation:
The general form of an ordinary differential equation of order n with m variables, xi, for i = 1, 2, ..., m, is stated as
In the above equation, f(n) denotes the nth derivative of the function f (x1, x2, ..., xm). Hence, the order of the differential equation is determined from the highest derivative in the given equation.
The degree of an ODE is the derivative with the highest power in the equation. We illustrate two examples to differentiate the concepts of degree and order, as follows:
The first equation above is a first-order ODE, whereas the second is a second-order, ODE as determined from their highest derivatives. The degree of the first equation is three, whereas the second is one, as determined from their highest power.
An ODE is said to be implicit if the derivative term cannot be separated from other variables in the equation. Otherwise, if the differential equation can be separated from other variables, then the equation is said to be in an explicit form. It can be verified that the first equation above is implicit, whereas the second one can be written in an explicit form as follows:
In this chapter, we will discuss several common numerical methods for solving the first- and second-order ordinary differential equation problems. A strong emphasis will be placed on the visual model using Visual C++ for each problem in the discussion.
In general, the first-order ordinary differential equation having two variables, x and y = f(x), is expressed as
Definition 10.1. The initial-value problem for the first-order ordinary differential equation consists of the equation
whose initial value is given by
Equations (10.3a) and (10.3b) make up the initial value problem for a first-order ordinary differential equation. The initial value specifies the starting point where the solution to the first-order differential equation exists. The solution to the initial-value problem is obtained analytically according to the following steps:
It is obvious that the numerical solution requires computing the definite integral of g(x, y) from x = x0 = a to x = b, assuming the function is continuous in this interval. The existence and uniqueness of the solution within a given domain is guaranteed if certain conditions are satisfied. We start with the following definition:
Definition 10.2. A function g(x, y) at y = y1 and y = y2 is said to satisfy a Lipschitz condition in the variable y if a constant C called the Lipschitz constant exists in such a way that |g(x, y1) − g(x, y2)| ≤ C | y1 − y2|.
The above definition suggests the difference in g(x, y) at two y locations is bounded by the Lipschitz constant. This definition paves the way for the uniqueness of the solution.
Theorem 10.1. Suppose D = {(x, y) | a ≤ x ≤ b, − ∞ < y < ∞ } and f(x, y) is continuous on D. If f(x, y) satisfies the Lipschitz condition on D in the variable y, then the initial-value problem given by
has a unique solution in a ≤ x ≤ b.
Definition 10.2 and Theorem 10.1 together guarantee the uniqueness of a solution to the initial-value problem. Several exact methods for solving the initial-value problem have been studied and applied. However, we will not discuss these methods as our focus is on the numerical solutions to the problems.
There are also several numerical methods for solving the initial-value problem in the first-order ODE based on the general formulation in Equation (10.3). In this chapter, we will discuss some of these methods, including the Taylor series, Runge-Kutta of order 2, Runge-Kutta of order 4, and the Adams-Bash forth-Moulton multistep methods. We will also discuss the initial-value problem for a system of ODEs.
The Taylor series method is based on the expansion of a given function based on the Taylor series equation. The nth-order expansion in the Taylor series is an approximation that produces a polynomial with (n + 1) terms.
Definition 10.3. The nth-order Taylor series of y = f(x) at yi+1 = y(xi+1) is the sum of (n + 1) terms at y i = y(xi) for i = 0, 1, ..., m and x0 ≤ x ≤ xm in equal-width subintervals, each of size h = Δx. The Taylor series is defined as
In the above equation, O (n + 1) is the sum of the terms involving y(n+1) onward. The Taylor series method for the initial-value problem is an approximation of the Taylor series taken up to the nth term expansion of Equation (10.5), as follows:
The initial value in the Taylor series method is y0 = y(x0). It is obvious from the above equation that the nth-order expansion includes terms involving the derivatives from y′i to y i(n). The Taylor series method requires the subintervals in x0 ≤ x ≤ xm to be uniform.
The implementation of the Taylor series method for solving the initial-value problem according to Equation (10.6) is very straightforward. Algorithm 10.1 shows how this method works.
It can be verified from Equation (10.6) that the solution generated in the Taylor series method becomes more accurate with higher order expansion of the terms. This is obvious as a high-order value like n = 5 requires the computation of up to y(5) that results in six terms in the expansion. The expansion supports a higher precision value that arises from a large number of decimal places. Algorithm 10.1 is illustrated through the following example:
Example 10.1. Given y' = x cos 2y for 0 ≤ x ≤ 1 with h = Δx = 0.1 and y(0) = −0.2, find y(0.1), y(0.2), ..., y(1.0) using the Taylor series method of order 3.
Solution. First, sketch the domain of this problem, as shown in Figure 10.1. We obtain the Taylor series of order 3 as
The equation requires y, y', y", and y'" in their discrete form. They are evaluated as
The equations are expressed into their discrete forms as
Starting at i = 0, we have x0 = 0 and y0 = −0.2. The discrete elements become
Table 10.1. Solution to the initial-value problem in Example 10.1
xi | yi | y'i | y"i | y"'i | |
---|---|---|---|---|---|
0 | 0 | −0.2 | 0.000000 | 0.921061 | 0.000000 |
1 | 0.100000 | −0.195395 | 0.092461 | 0.931653 | 0.208695 |
2 | 0.200000 | −0.181456 | 0.186973 | 0.961417 | 0.375875 |
3 | 0.300000 | −0.157888 | 0.285167 | 1.003691 | 0.448502 |
4 | 0.400000 | −0.124279 | 0.387707 | 1.045571 | 0.354170 |
5 | 0.500000 | −0.080221 | 0.493578 | 1.066008 | 0.004724 |
6 | 0.600000 | −0.025532 | 0.599218 | 1.035399 | −0.674864 |
7 | 0.700000 | 0.039454 | 0.697822 | 0.919879 | −1.680773 |
8 | 0.800000 | 0.113555 | 0.779457 | 0.693513 | −2.846112 |
9 | 0.900000 | 0.194494 | 0.832764 | 0.356803 | −3.816957 |
10 | 1.000000 | 0.278919 | 0.848402 | −0.049805 | −4.186357 |
We obtain y1 = y(0.1)
By repeating the same step above for i = 1 with x1 = 0.1 and y1 = −0.195395, we get
Finally, we obtain the solution:
The complete results for other values in the range of 0 ≤ x ≤ 1.0 are shown in Table 10.1. Figure 10.2 is the curve representing the solution to the initial-value problem.
A classic technique called Euler's method is a special case of the Taylor series method whose order is n = 1. The method is derived from the fundamental theorem of calculus, stated as
Simplifying the above equation gives
Euler's method is easy to implement as it involves only the first derivative in the Taylor series. It is not necessary to compute the derivatives of higher orders in this method. However, because of its two-term only expansion in the Taylor series, the results obtained using the Euler's method are not as accurate as the Taylor series method using higher orders.
One difficulty with the Taylor series method is the necessity to evaluate one or higher derivatives of the given expression that may become very tedious and complicated. As a result, the method may not be practical for implementation on the computer as a special routine for evaluating the derivatives has to be developed along with the normal program. This special routine may involve symbolic computation, which is one area of study that requires a good understanding of data structure and numerical database knowledge. Therefore, the Taylor series method is seldom used in most applications for solving the initial-value problems in ODE.
A suitable alternative to the Taylor series method is the Runge-Kutta methods, which were first proposed by the German mathematicians, C. Runge and M. W. Kutta in 1900. Because of its simplicity, the Runge-Kutta method became very popular, and several variations to the original method were produced. The general solution for the explicit ODE is given by
where n is the order of the Runge-Kutta equation, cj is the weight, and kj is the term in the Taylor series expansion of the equation y' = g(x, y). We will discuss the Runge-Kutta of order 2 and 4 methods in this chapter, which has two and four terms in the summation of Equation (10.9), respectively.
Originally, the Runge-Kutta method of order 2 (RK2) is derived from the Taylor series method based on the equation given by
The above equation simplifies into
where
and 0 < r ≤ 1. RK2 is stable and produces reasonably good solutions if the value of r is kept in the given range. The method assumes m equal-width subintervals with h = Δx in x0 ≤ x ≤ xm.
Example 10.2. Solve the problem in Example 10.1 using RK2 with r = 0.8.
Solution. Given
With r = 0.8,
This produces
Continuing at i = 1, we have x1 = 0.1 and y1 = −0.195394:
And, finally,
Table 10.2 summarizes the results obtained for i = 0, 1, ..., 10.
Heun's method is a special case of RK2 with r = 1. This produces
Table 10.2. RK2 solution to the initial-value problem
xi | yi | k1 | k2 | |
---|---|---|---|---|
0 | 0 | −0.2 | 0 | 0.007368 |
1 | 0.1 | −0.195394 | 0.009246 | 0.016742 |
2 | 0.2 | −0.181463 | 0.018697 | 0.026461 |
3 | 0.3 | −0.157913 | 0.028516 | 0.036621 |
4 | 0.4 | −0.124331 | 0.038769 | 0.047166 |
5 | 0.5 | −0.080313 | 0.049356 | 0.057806 |
6 | 0.6 | −0.025675 | 0.059920 | 0.067932 |
7 | 0.7 | 0.039252 | 0.069784 | 0.076593 |
8 | 0.8 | 0.113292 | 0.077955 | 0.082625 |
9 | 0.9 | 0.194166 | 0.083298 | 0.084967 |
10 | 1 | 0.278508 | 0.084883 | 0.083099 |
where k1 = hg(xi, yi) and k2 = hg(xi + h, yi + k1). To implement this method, Algorithm 10.2 is used by setting r = 1.
The modified Euler-Cauchy is another special case of RK2 with r = 0.5 to produce
where k1 = hg(xi, yi) and k2 = hg(xi + 0.5h, yi + 0.5k1). Although k1 appears missing in Equation (10.13), the variable is still needed in the evaluation of k2 and, there-fore, will still need to be evaluated.
The Runge-Kutta method of order 4 (RK4) is derived from the Taylor series of order 4 by approximating the second, third, and fourth derivatives. The method is given as
where
RK4 is a method of choice in many applications for solving the initial-value problem as it provides a more accurate solution compared with RK2. The method is also easy to implement, as summarized in Algorithm 10.3. Example 10.3 shows an example from this method.
Example 10.3. Solve the problem in Example 10.1 using RK4.
Solution. Given
At i = 0, x0 = 0 and y0 = −1. This produces
Therefore,
At i = 1, x0 = 0.1 and y0 = −0.195386. With similar steps, we get
Table 10.3. Results using RK4 for Example 10.3
xi | yi | k1 | k2 | k3 | k4 | |
---|---|---|---|---|---|---|
0 | 0 | −0.2 | 0 | 0.004605 | 0.004614 | 0.009246 |
1 | 0.1 | −0.195386 | 0.009246 | 0.013921 | 0.013947 | 0.018698 |
2 | 0.2 | −0.181439 | 0.018697 | 0.023534 | 0.023574 | 0.028517 |
3 | 0.3 | −0.157867 | 0.028517 | 0.033566 | 0.033616 | 0.038771 |
4 | 0.4 | −0.124258 | 0.038771 | 0.044014 | 0.044062 | 0.049358 |
5 | 0.5 | −0.080211 | 0.049357 | 0.054661 | 0.054693 | 0.059922 |
6 | 0.6 | −0.025547 | 0.059921 | 0.064997 | 0.064994 | 0.069782 |
7 | 0.7 | 0.039401 | 0.069782 | 0.074174 | 0.074124 | 0.077947 |
8 | 0.8 | 0.113455 | 0.077949 | 0.081081 | 0.081000 | 0.083279 |
9 | 0.9 | 0.194353 | 0.083285 | 0.084613 | 0.084556 | 0.084841 |
10 | 1 | 0.278764 | 0.084856 | 0.084070 | 0.084120 | 0.082279 |
The above values produce
The full results of Example 10.3 are listed in Table 10.3.
All the methods discussed in the earlier sections are single-step methods. The solutions to these methods are based on iterations starting from a single initial value. The results obtained are good, but they may not be precise because of factors such as error truncation and the approximated approach in the methods.
A refinement to the solution in a single-step method is the multistep method. A multistep method implements the predictor-corrector approach, which first deploys a predictor function to predict the solution using the Lagrange polynomial interpolation. The predicted value is then refined further using a corrector function.
We discuss the solution to the first-order ODE problem
The Adams-Bashforth-Moulton(ABM) method is the most popular multi step method for solving the initial-value problem. The method is based on the fundamental theorem of calculus from y = f (x) given by
Table 10.4. Adams-Bashforth predictor functions
Adams-Bashforth | Predictor Function |
---|---|
Two-step | |
Three-step | |
Four-step |
The Adams-Bashforth-Moulton method is a two-fold process consisting of the predictor and corrector functions, pi+1 and yi+1. The predictor function is called the Adams-Bashforth function, whereas the corrector is the Adams-Moulton function. Several variations to the method have been documented, and they differ through the number of step sizes in both the predictor and the corrector functions.
Tables 10.4 and 10.5 show some of the most common Adams-Bashforth predictor and Adams-Moulton corrector functions. We discuss the implementation of the Adams-Bashforth-Moulton method using the four-step Adams-Bashforth method as the predictor function and the three-step Adams-Moulton function as the corrector function. The functions are given as
Four-step Adams-Bashforth Equation (Predictor):
Three-step Adams-Moulton Equation (Corrector):
The Adams-Bashforth predictor is based on the Lagrange polynomial approximation on four points, (xi−3, yi−3), (xi−2, yi−2), (xi−1, yi−1), and (xi, yi) to produce the extrapolated point (xi+1, pi+1) using Equation (10.16a). Starting with i = 3, the values of g(x0, y0), g(x1, y1), g(x2, y2) and g(x3, y3) are first evaluated to produce (x4, p4). Any single-step method discussed earlier can be used to evaluate these three values.
The Adams-Moulton corrector involves a Lagrange interpolation over the points (xi−2, yi−2), (xi−1, yi−1), (xi, yi) and the new point (xi+1, pi+1) starting at i = 3, to produce the corrected value, (xi+1, yi+1). With i = 3, the corrected value of (x4, y4) is obtained using Equation (10.16b) from the values of g(x1, y1), g(x2, y2), and g(x3, y3).
Algorithm 10.4 outlines the computational steps for the Adams-Bashforth− Moulton method. This algorithm is illustrated using Example 10.4.
Example 10.4. Solve the problem in Example 10.1 using the Adams-Bashforth-Moulton method, starting with RK4 for the first three values.
Solution. Starting with x0 = 0 from the initial value, we apply RK4 to produce the values of y1, y2, and y3. The values are obtained from Example 10.3, as
We get the predictor value at i = 3:
The corrected value follows:
Similar calculations for i = 4 produce p5 and y5, as follows:
The full results from Example 10.4 are shown in Table 10.6.
An initial-value problem also arises from a system of ordinary differential equations. A system of first-order ordinary differential equations consists of two or more differential equations that share the same set of variables. As in the system of linear equations, a system with n differential equations requires n initial values before it can be solved with unique solutions.
Definition 10.4. In general, the initial-value problem for a system of n ordinary differential equations involving y1(x), y2(x), ..., yn(x) can be written as
where g1(x, y1, y2, ..., yn), g2(x, y1, y2, ..., yn), ..., gn(x, y1, y2, ..., yn) are the given functions in x0 ≤ x ≤ xm. The initial values in this problem are given by
The domain for this problem consists of the successive points x0, x1, ..., xm in m equal-width subintervals that are h = Δx apart.
Primarily, the solution to an initial-value problem involving a system of differential equations is derived from the same methods used in the single-equation case. Therefore, any method discussed earlier can be applied to solve this problem.
We discuss the Runge-Kutta of order 4 method for solving a system of two differential equations having three variables each. A system of two differential equations with three variables x, y(x), and z(x) in x0 ≤ x ≤ xm has the following form:
The initial values are given by y0 = y(x0) and z0 = z(x0). The discrete values of x are expressed as xi for i = 0, 1, 2, ..., m. Since the intervals are uniform with width h= Δx, the terms in x can be expressed as
The RK4 method for a system of two differential equations is expressed as
In Equations (10.18a) and (10.18b), ki and Ki for i = 1, 2, 3, 4 are the RK4 parameters defined in Equations (10.15a), (10.15b), (10.15c), and (10.15d). They are
Algorithm 10.5 summarizes the steps in solving the initial-value problem from a system of two ordinary differential equations using RK4.
Example 10.5. Solve the differential equations
Solution. The system consists of
We obtain m = (xm − x0)/h = (1 − 0)/ 0.1 = 10. The parameters in RK4 are evaluated as
Table 10.7. RK4 Solution to the system of ODE problem in Example 10.5
xi | yi | zi | k1, K1 | k2, K2 | k3, K3 | k4, K4 | |
---|---|---|---|---|---|---|---|
0 | 0 | 2.000000 | −1.000000 | 0.200000 0.000000 | 0.220000 −0.010500 | 0.221053 −0.010605 | 0.242317 −0.022446 |
1 | 0.1 | 2.220737 | −1.010776 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
2 | 0.2 | 2.486556 | −1.047268 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
3 | 0.3 | 2.752376 | −1.083760 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
4 | 0.4 | 3.018195 | −1.120252 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
5 | 0.5 | 3.284015 | −1.156744 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
6 | 0.6 | 3.549834 | −1.193236 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
7 | 0.7 | 3.815653 | −1.229728 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
8 | 0.8 | 4.081473 | −1.266220 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
9 | 0.9 | 4.347292 | −1.302712 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
10 | 1 | 4.613112 | −1.339204 | 0.242289 −0.022447 | 0.264848 −0.035901 | 0.266178 −0.036311 | 0.290575 −0.052080 |
Therefore,
Table 10.7 summarizes the results for (xi, yi, zi) for i = 0, 1, ..., 10.
The second-order ordinary differential equation is an equation that has the second derivative as its highest derivative. The general form of a second-order ODE involving y(x) is
Two problems arise in the second-order ODE, and they are called the initial-value problem and the boundary-value problem. For an interval defined as a ≤ x ≤ b, the initial-value problem involves Equation (10.19) with its initial values at x = a given. A boundary-value problem has its boundary values given at x = a and x = b for solving Equation (10.19).
In solving a second-order ODE problem, conditions from either its initial values or boundary values are needed. A solution obtained from the second-order differential equations may exist uniquely or infinitely. The following theorems describe the cases of unique solution.
Theorem 10.2. Given y" = f(x, y, y') for x0 ≤ x ≤ xm with y(x0) = y0 and y(xm) = ym, which is continuous in
Theorem 10.3. If y" = p(x)y' + q(x)y + r(x) for x0 ≤ x ≤ xm, with y(x0) = y0 and y(xm = ym; p(x), q(x), and r(x) are continuous in [x0, xm]; and q(x) > 0 in [x0, xm], then the solution is unique.
We discuss the solution to the initial-value problem in the second-order ordinary differential equation using a technique of reducing its order to a system of first-order equations. This is followed by the boundary-value problem involving a method called finite-difference.
The initial-value problem for a second-order ordinary differential equation is defined as follows:
Definition 10.5. The initial-value problem for a continuous second-order ordinary differential equation in an interval defined as x0 ≤ x ≤ xm with the variables y = f(x) consists of solving Equation (10.19) with the initial conditions given by
A second-order ODE requires two initial conditions, one from the normal starting value and another involving the first derivative. One good strategy for solving the initial-value problem for the second-order ODE is to reduce its order to a system of first-order ODEs. The same technique discussed in Section 10.8 can then be applied to solve the problem once the system of first-order ODEs has been obtained. To achieve this objective, any single-step method discussed in this chapter can be used to generate the solution.
We discuss the reduction of the second-order ODE into a system of first-order ODEs. The numerical solution to the initial-value problem involving the second-order ODE can be modeled as the discrete points (xi, yi) over m uniform intervals in x0 ≤ x ≤ xm whose width is given by h = Δx.
A second-order ODE with two variables, x and y = f(x), can be reduced into a system of two first-order ODEs. This is possible by setting z = y', and this transforms y" into z'. Hence, g(x, y, y', y") = 0 is reduced to the following system of first-order ODEs:
The initial-value conditions for this problem become y(x0) = y0 and z0 = y'(x0). The solutions are then obtained by applying the same method discussed in Section 10.8. Any suitable first-order method such as RK2 and RK4 can be used to solve the system of differential equations.
Algorithm 10.6 summarizes the steps in solving the initial-value problem. The method applies RK4 for solving the system of first-order ODEs. The algorithm is illustrated using Example 10.6.
Example 10.6. Solve the equation y" + 4y' + 5y = 0, with the initial conditions given by y(0) = −1 dan y'(0) = 2, for 0 ≤ x ≤ 1 and h = 0.1.
Solution. The number of subintervals is m = (1 − 0)/0.1 = 10. Let z = y', and this reduces y" + 4y' + 5y = 0 into z' + 4z + 5y = 0. We obtain a system of first-order ODEs, as follows:
The initial values are y0 = y(0) = −1 and z0 = z(0) = 2. The parameters in RK4 are obtained as follows:
At i = 0, x0 = 0, y0 = −1, and z0 = 2. This gives
We obtain the first solution as
Table 10.8. RK4 Solution to the second-order ODE boundary value problem in Example 10.6
i | xi | yi | zi | k1, K1 | k2, K2 | k3, K3 | k4, K4 |
---|---|---|---|---|---|---|---|
0 | 0 | −1.000000 | 2.000000 | 0.200000 −0.300000 | 0.185000 −0.290000 | 0.185500 −0.288250 | 0.171175 −0.277450 |
1 | 0.1 | −0.814638 | 1.711008 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
2 | 0.2 | −0.656954 | 1.447074 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
3 | 0.3 | −0.499270 | 1.183139 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
4 | 0.4 | −0.341587 | 0.919205 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
5 | 0.5 | −0.183903 | 0.655271 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
6 | 0.6 | −0.026220 | 0.391336 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
7 | 0.7 | 0.131464 | 0.127402 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
8 | 0.8 | 0.289148 | −0.136533 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
9 | 0.9 | 0.446831 | −0.400467 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
10 | 1 | 0.604515 | −0.664401 | 0.171101 −0.277085 | 0.157247 −0.264443 | 0.157879 −0.263508 | 0.144750 −0.250621 |
The full results from this problem are shown in Table 10.8.
The boundary-value problem for a second-order ordinary differential equation has the boundary conditions given. In an interval defined as a ≤ x ≤ b, the boundaries for the continuous function y = f(x) in the interval are the left and right points, (a, f(a)) and (b, f(b)). To solve the differential equation, the boundary values must be given.
A boundary is an end point in the given interval or domain of the problem. There are two types of boundaries in an ordinary differential equation:
Dirichlet boundary conditions, which are stated as the given values at the ends of one of the intervals, for example, y(a) = α and y(b) = β in a ≤ x ≤ b.
Neumann boundary conditions, which are stated as the given values of the first derivatives at the ends of one of the intervals. For example, y'(a) = λ and y'(b) = μ in a ≤ x ≤ b are the Neumann boundary conditions.
We will deal with the first type of boundary condition in this section and with the second type in the next section.
Definition 10.6. The boundary-value problem involving a second-order ordinary differential equation is Equation (10.19) in x0 ≤ x ≤ xm with boundary conditions given by y(x0) = y0 and y(xm) = ym. In this interval, the width is given by h = Δx and there are m uniform subintervals.
We restrict our discussion on the boundary-value problems to the case of linear second-order ODEs. A second-order differential equation is said to linear if it can be expressed into the following form:
where p(x), q(x), r(x), and w(x) are continuous functions of x in the interval x0 ≤ x ≤ xm.
A common approach for solving a linear second-order differential equation with boundary conditions is the finite-difference method. The method is based on the approximation of the derivatives of y at several finite points in the interval to yield a finite-difference formula. The points are distributed at equal-width subintervals so that the derivatives y' and y" can be replaced by their approximated discrete values.
The solution to the boundary-value problem for ODE2 consists of two main steps, as depicted in Figure 10.3. First, the differential equation is discretized where the terms involving y' and y" are replaced by their approximated values using the central-difference rules. This step leads the way to the formation of the finite-difference formula for the problem.
The second step starts by applying the finite-difference formula to the m subintervals in x0 ≤ x ≤ xm. The finite-difference formula is applied at each of the m − 1 interior points in the interval to produce a system of (m − 1) × (m − 1) linear equations. A technique from Chapter 5, such as the Gaussian elimination method, is then applied to solve this system to produce the final solution to the boundary-value problem.
Algorithm 10.7 outlines the implementation of the finite-difference method for solving the boundary-value problem for a linear second-order ODE.
We discuss the general solution to Equation (10.21) using Algorithm 10.7. The discrete form of this equation is given by
Let pi = p(xi), qi = q(xi), ri = r(xi), and wi = w(xi), and the above equation becomes
Finite-difference values are obtained by employing the central difference rules discussed in Chapter 8 to approximate the first and second derivatives, given as
Substituting y' and y", we get
Rearranging the terms in the order of yi−1, yi and yi+1, we obtain the finite-difference formula for the given problem:
There are m − 1 unknowns in the system, yi for i = 1, 2, ..., m − 1, which can be solved from the (m − 1) × (m − 1) system of linear equations. This system of linear equations is obtained by first substituting i = 1, 2, ..., m − 1 into the above equation. The process starts with i = 1:
Since the value of y0 is given, the first term above is moved to the right-hand side of the equation to give
At i = m − 1, the equation becomes
Since the value of ym is given, the last term in the left-hand side is moved to the right-hand side, and this produces the last equation in the system as
Regrouping all the above equations into a matrix form, we obtain a tridiagonal system of linear equations of size (m − 1) × (m − 1), as follows:
From the above system of linear equations, we obtain the algorithm for the nonzero entries of A = [ai,j] and b = [bi] in Ay = b, as follows:
Since Equation (10.23) is tridiagonal, the most practical approach for solving this system is the Thomas algorithm as the computational steps required in this method are not as massive as in other methods. We discuss an example that shows the method for solving this problem.
Example 10.7. Given (cosx)y" + (sin(2x − 1)) y' + (sin(1 − 5x))y = x cosx in 1 < x < 3 whose width is h = Δx = 0.4, the boundary values in this problem are given as y(1) = −1 and y(3) = 1. Find the values of y(1.4), y(1.8), y(2.2), and y(2.6).
Solution. Figure 10.4 shows the solution diagram for the problem. There are four unknowns in this problem, yi for i = 1, 2, 3, 4, since m = 5. We start with
Applying the central-difference rules for substituting y'i and y"i,
Simplifying the terms in the above equation, we obtain the following finite-difference formula:
The next step is to form the system of linear equations. A system of four linear equations is to be formed since there are four unknowns, y1, y2, y3, and y4. The equations are found by setting i = 1, 2, 3, 4 into the finite-difference formula to form a 4 × 4 system of linear equations, as follows:
In matrix form, the linear equations become
The above system of linear equations is solved to produce the following solution:
Figure 10.5 shows the solution graph for Example 10.7.
Under certain circumstances, the boundary conditions for the second-order ODE in Equation (10.21) may be given in the form of derivatives, as follows:
where α and β are constants and m is the number of subintervals in x0 ≤ x ≤ xm.
In solving this problem, similar steps as in the previous case are applied to obtain the finite-difference formula in Equation 10.22.
The left boundary is y'0 = α. Applying the central-difference rule from Equation (8.5a),
We obtain a virtual value, y−1 as this quantity is not inside y0 ≤ y ≤ ym. Expressing y−1 as a subject of the equation, we obtain
The right boundary condition consists of y'm = β, which becomes
Again, another virtual value ym+1 is obtained as it is outside of y0 ≤ y ≤ ym. This value is made the subject, as follows:
It can be verified that the virtual values are only applicable in the finite-difference formula in cases of i = 0 and i = m. At i = 0, Equation (10.22) produces:
Substituting the value of y−1, the above equation simplifies to
Similarly, applying Equation (10.22) at i = m:
Substituting the value of ym+1:
For i = 1, 2, ..., m − 1, the entries in each row of the coefficient matrix consist of the diagonal, and its left and right terms, as follows:
We obtain a (m + 1) × (m + 1) tridiagonal system of linear equations:
Equation (10.27) is summarized as Ay = b, where A = [aij] is the coefficient matrix in the left-hand side, b = [bi] is the vector in the right side of the equation, and y = [yi] is the unknown vector, for i, j = 0, 1, 2, ..., m. The tridiagonal elements of A are obtained as
From the same equation, we obtain the representation for b, as follows:
Collectively, Equations (10.28a), (10.28b), (10.28c), and (10.28d) are sufficient to solve for y. Since the coefficient matrix in Equation (10.27) is tridiagonal, the most suitable choice for solving the system of linear equations is the Thomas algorithm method.
Example 10.8. Given
Solution. There are
Replacing y'i and y"i in the above equation using central-difference rules, we get
The above equation is simplified to produce the finite-difference formula given by
The boundary values at x0 = 0 and x5 in this problem are given in the form of first derivatives at these points. This implies y0 and y5 are also the unknowns in this problem along with y1, y2, y3, and y4. Therefore, there are six unknowns that require the reduction of the boundary-value problem to a 6 × 6 system of linear equations.
The given first derivative at x0 = 0 is simplified using the central-difference rule to produce a virtual value, y−1. This is obtained as follows:
The right boundary value is simplified in a similar fashion to produce another virtual value, y6, as follows:
The first virtual value and the finite-difference formula are applied at i = 0 to produce
Applying the formula at the other interior points produce
The second virtual value is applied to the right boundary point, at i = 5, to produce
We obtain the following system of linear equations:
The above system is solved to produce the final solutions, given by
The solution curve for this problem is shown in Figure 10.7
We discuss the visual interface for the ordinary differential equations problems. The project is called Code10
, and it consists of all the methods discussed in this chapter. Code10
is menu-driven, and this provides friendliness to problems that are generally considered difficult.
Figure 10.8 shows the output from Code10
, which consists of a menu with eight items that represent the methods in order from top to bottom, as follows: Taylor, Runge-Kutta of order2, Runge-Kutta of order 4, Adams-Bashforth-Moulton, ODE1 system, ODE2 initial-value problem, finite-difference 1, and finite-difference 2. The figure shows the solution to a sample problem from the fifth item in the menu (ODE 1 System), which is an initial-value problem on a system of first-order ODEs given by
whose initial values are x0 = 0, y0 = 0.5, and z0 = 0.5, for 0 ≤ x ≤ 8. The results are shown in the table with their corresponding graphs of y = q(x) and z = r(x) generated.
Code10
has been designed to allow the user a full control of the input as well as the output. To allow this flexibility, the output from Figure 10.8 is divided into four regions. The first region is the menu displayed as shaded rectangles on the top left. The second region is the input area, which becomes active when an item in the menu is selected. The third region is the list view table for displaying the results from the calculations. The fourth region is the graphical area that displays the solution graph for the problem.
Code10
has a single class called CCode10
, and this class is derived from CFrameWnd
. The development files in Code10
are Code10.cpp, Code10.h
, and MyParser.obj
.
Figure 10.9 is the schematic drawing of Code10
that shows the development stages of Code10
. In this diagram, a status flag called fStatus
monitors the execution progress whose updated value represents a step in the execution. Initially, fStatus
has a value of 0. When an item in the menu is selected, fStatus
changes its value to 1. At the same time, a variable called fMenu
is assigned with a number that represents the order of the item from top to bottom. The selection also creates edit boxes for collecting input from the user and a push button called Compute that, when activated, calls the function that corresponds to the selected method.
When input has been completed and confirmed with a click at the Compute button, fStatus
value is updated to 2. The click causes a call to be made to a function corresponding to the selected method in the menu. This function represents a method for solving the given problem. The function solves the problem according to the input values, and the results are displayed in the list view table through ShowTable()
. The solution graph for the problem is also displayed through DrawCurve()
.
Changes in the input values in the edit boxes are allowed by resetting fStatus
to 1 in DrawCurve()
once the results have been obtained and displayed. This option is necessary as part of the user-friendliness features for the given problem. With this update, any small changes in the input values will cause the whole data to be reevaluated, and the results are immediately updated both in the table and in the solution graph.
The data structure in Code10
consists of four structures. The first structure is PT, which represents the variables xi, yi, pi, and zi as the array pt
in the methods. The structure also defines the left, right, maximum, and minimum points in the solution curve in the problem. The structure is declared as follows:
typedef struct { double x, y, p, z: //xi
,y
i,p
i, andz
i components } PT; PT *pt;
A structure called INPUT
declares the input objects, and these objects are linked to an array called input
. The structure is declared as
typedef struct { CString item, label,; // input string and its label CPoint hm; // home coordinates CEdit ed; // edit box CRect rc; // rectangular object } INPUT; INPUT input[maxInput+1];
The third structure is MENU
, which declares objects for the items in the menu. This structure is declared as
typedef struct { CString item; // menu item CPoint hm; // home coordinates CRect rc; // rectangular region } MENU; MENU menu[nMenuItems+1];
The last structure is CURVE
, which provides objects for creating the solution curve. The structure defines the rectangular region for displaying the solution graph.
typedef struct { CRect rc; // rectangular region CPoint hm, end; // starting and end coordinates } CURVE; CURVE curve;
Three events are mapped in Code10
, namely, the display update, left-button click, and push button click.
BEGIN MESSAGE MAP(CCode10, CFrameWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN()
ON_BN_CLICKED(IDC_BUTTON, OnButton) END_MESSAGE_MAP()
Table 10.9. Functions in code10
Eight items in the menu represent eight different methods for solving the initial-and boundary-value problems. Each method is represented by a function as described in Table 10.9.
The Taylor series method is represented by ODE1Taylor()
. This function supports the Taylor series method of order three only. It would be a good challenge for the reader to modify the item to make it more flexible by supporting the method using any high order. In the given problem, y = g(x, y) is the input function. Because of the non-symbolic nature of the application, the program does not evaluate the second or third derivatives automatically from the input function. The user needs to enter the derivatives in the edit boxes, and the string will then be passed to parse()
for processing.
In reading the input string, the derivatives are represented as single characters in parse()
, as follows:
The representation is necessary so as to use the character codes defined in Table 4.1 where the corresponding codes for x, y, u, v, w, and z are 23, 24, 20, 21, 22, and 25, respectively. For example, y"' = 3xy' − 4(y")2 cosxy" is written as
The Taylor series method in ODE1Taylor()
is written based on Algorithm 10.1 and Equation (10.6). The function is given as follows:
void CCode10::ODE1Taylor() { double psv[6], tmp, max; int psi[6]; int i; double h, u, v, w, z; pt[0].x=atof(input[2].item); pt[0].y=atof(input[5].item); h=atof(input[4].item); tmp=atof(input[3].item); m=(tmp-pt[0].x)/h; m=((m<M)?m:M); max=pt[0].x+(double)m*h; tmp=(tmp<max)?tmp:max; pt[m].x=tmp; psi[1]=23; psi[2]=24; psi[3]=20; psi[4]=21; for (i=0;i<=m;i++) { psv[1]=pt[i].x; psv[2]=pt[i].y; u=parse(input[1].item, 2, psv, psi); psv[3]=u; v=parse(input[6].item, 3, psv, psi); psv[4]=v; w=parse(input[7].item, 4, psv, psi); if (i<m) { pt[i+1].y=pt[i].y+h*u+pow(h, 2)/ 2*v+pow(h, 3)/6*w; pt[i+1].x=pt[i].x+h; } } }
There are m subintervals that require m iterations for computing yi, for i = 1, 2, ..., m. The input strings for the equations are read as input[1].item, input[6].item
, and input[7].item
. These strings are passed to parse()
for processing. The returned values are stored as u, v
, and w
, which represent y', y", and y"', respectively.
psv[1]=pt[i].x; psv[2]=pt[i].y; u=parse(input[1].item, 2, psv, psi); psv[3]=u; v=parse(input[6].item, 3, psv, psi); psv[4]=v; w=parse(input[7].item, 4, psv, psi);
The Taylor series method solves the initial-value problem according to Equation (10.6). The code for this equation is written as
if (i<m) { pt[i+1].y=pt[i].y+h*u+pow(h, 2)/2*v+pow(h, 3)/6*w; pt[i+1].x=pt[i].x+h; }
RK2 is easier to implement than the Taylor series method as it does not require the evaluation of high-order derivatives. In Code10
, RK2 is handled by ODE1RK2()
. The method is very straightforward, as shown below:
void CCode10::ODE1RK2() { int i, psi[6]; double h, r, psv[6], tmp, max; double k1, k2; pt[0].x=atof(input[2].item); pt[0].y=atof(input[5].item); h=atof(input[4].item); tmp=atof(input[3].item); m=(tmp-pt[0].x)/h; m=((m<M)?m:M); max=pt[0].x+(double)m*h; tmp=(tmp<max)?tmp:max; pt[m].x=tmp; r=atof(input[6].item); psi[1]=23; psi[2]=24; for (i=0;i<=m;i++) {
psv[1]=pt[i].x; psv[2]=pt[i].y; k1=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+r*h; psv[2]=pt[i].y+r*k1; k2=h*parse(input[1].item, 2, psv, psi); if (i<m) { pt[i+1].y=pt[i].y+(1-1/(2*r))*k1+1/(2*r)*k2; pt[i+1].x=pt[i].x+h; } } }
ODE1RK2()
solves the initial-value problem using Equations (10.10) and (10.11), whose computational steps have been outlined in Algorithm 10.2. Equation (10.11a) and (10.11b) are processed using
psv[1]=pt[i].x; psv[2]=pt[i].y; k1=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+r*h; psv[2]=pt[i].y+r*k1; k2=h*parse(input[1].item, 2, psv, psi);
The values of yi, for i = 1, 2, ..., m, are updated using Equation (10.10), and they are written in ODE1RK2()
as
if (i<m) { pt[i+1].y=pt[i].y+(1-1/(2*r))*k1+1/(2*r)*k2; pt[i+1].x=pt[i].x+h; }
The function ODE1RK4()
represents the solution to the initial-value problem based on Algorithm 10.3 and Equation (10.14). The code segment is given as
void CCode10::ODE1RK4() { int i, psi[6]; double h, psv[6], tmp, max; double k1, k2, k3, k4; pt[0].x=atof(input[2].item);
pt[0].y=atof(input[5].item); h=atof(input[4].item); tmp=atof(input[3].item); m=(tmp-pt[0].x)/h; m=((m<M)?m:M); max=pt[0].x+(double)m*h; tmp=(tmp<max)?tmp:max; pt[m].x=tmp; psi[1]=23; psi[2]=24; for (i=0;i<=m;i++) { psv[1]=pt[i].x; psv[2]=pt[i].y; k1=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k1/2; k2=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k2/2; k3=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h; psv[2]=pt[i].y+k3; k4=h*parse(input[1].item, 2, psv, psi); if (i<m) { pt[i+1].y=pt[i].y+(k1+2*k2+2*k3+k4)/6; pt[i+1].x=pt[i].x+h; } } }
The parameters k1, k2, k3, and k4 in Equations (10.15a), (10.15b), (10.15c), and (10.15d), respectively, are written in the following code fragments:
psv[1]=pt[i].x; psv[2]=pt[i].y; k1=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k1/2; k2=h*parse(input[1].item, 2, psv, psi);
psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k2/2; k3=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h; psv[2]=pt[i].y+k3; k4=h*parse(input[1].item, 2, psv, psi);
RK4 solution in Equation (10.14) is written compactly as
if (i<m) { pt[i+1].y=pt[i].y+(k1+2*k2+2*k3+k4)/6; pt[i+1].x=pt[i].x+h; }
The Adams-Bashforth-Moulton method is a multistep method that requires a single step method in its first few iterations to produce the predictor values. These values are read and inserted into the corrector function to produce solutions in the subsequent iterations. In Code10
, the function ODE1AB()
implements this method based on Algorithm 10.4.
void CCode10::ODE1AB() { int i, psi[6]; double h, f0, f1, f2, f3, fp, psv[6], tmp, max; double k1, k2, k3, k4; pt[0].x=atof(input[2].item); pt[0].y=atof(input[5].item); h=atof(input[4].item); tmp=atof(input[3].item); m=(tmp-pt[0].x)/h; m=((m<)?m:M); max=pt[0].x+(double)m*h; tmp=(tmp<ax)?tmp:max; pt[m].x=tmp; psi[1]=23; psi[2]=24; for (i=0;i<=m;i++) { if (i<3) { psv[1]=pt[i].x; psv[2]=pt[i].y; k1=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h/2;
psv[2]=pt[i].y+k1/2; k2=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k2/2; k3=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h; psv[2]=pt[i].y+k3; k4=h*parse(input[1].item, 2, psv, psi); pt[i+1].y=pt[i].y+(k1+2*k2+2*k3+k4)/6; } if (i<) { pt[i+1].x=pt[i].x+h; if (i>=3) { psv[1]=pt[i-3].x; psv[2]=pt[i-3].y; f0=parse(input[1].item, 2, psv, psi); psv[1]=pt[i-2].x; psv[2]=pt[i-2].y; f1=parse(input[1].item, 2, psv, psi); psv[1]=pt[i-1].x; psv[2]=pt[i-1].y; f2=parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x; psv[2]=pt[i].y; f3=parse(input[1].item, 2, psv, psi); pt[i+1].p=pt[i].y+h/24*(−9*f0+37*f1 −59*f2+55*f3); psv[1]=pt[i+1].x; psv[2]=pt[i+1].p; fp=parse(input[1].item, 2, psv, psi); pt[i+1].y=pt[i].y+h/24*(f1-5*f2+19*f3 +9*fp); } } } }
The Adams-Bashforth predictor function is Equation (10.16a), and it is based on RK4. The function is represented by the following code segment:
if (i<3) { psv[1]=pt[i].x; psv[2]=pt[i].y; k1=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k1/2; k2=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k2/2; k3=h*parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x+h; psv[2]=pt[i].y+k3; k4=h*parse(input[1].item, 2, psv, psi); pt[i+1].y=pt[i].y+(k1+2*k2+2*k3+k4)/6; }
The Adams-Moulton corrector function is based on Equation (10.16b). The code segment consists of
if (i<m) { pt[i+1].x=pt[i].x+h; if (i>=3) { psv[1]=pt[i-3].x; psv[2]=pt[i-3].y; f0=parse(input[1].item, 2, psv, psi); psv[1]=pt[i-2].x; psv[2]=pt[i-2].y; f1=parse(input[1].item, 2, psv, psi); psv[1]=pt[i-1].x; psv[2]=pt[i-1].y; f2=parse(input[1].item, 2, psv, psi); psv[1]=pt[i].x;
psv[2]=pt[i].y; f3=parse(input[1].item, 2, psv, psi); pt[i+1].p=pt[i].y+h/24*(−9*f0+37*f1-59*f2+55*f3); psv[1]=pt[i+1].x; psv[2]=pt[i+1].p; fp=parse(input[1].item, 2, psv, psi); pt[i+1].y=pt[i].y+h/24*(f1-5*f2 +19*f3+9*fp); } }
The initial-value problem can be extended into a system of ordinary differential equations by having an equivalent number of initial values. A system with three independent variables requires two equations and two initial values in order to produce unique solutions.
In Code10
, a system with two ordinary differential equations is solved in ODE1System()
. The code fragments are written based on Algorithm 10.5, and they are given as
void CCode10::ODE1System() { int i, psi[6]; double h, psv[6], tmp, max; double k1, k2, k3, k4; double K1, K2, K3, K4; pt[0].x=atof(input[3].item); pt[0].y=atof(input[4].item); pt[0].z=atof(input[5].item); h=atof(input[6].item); tmp=atof(input[7].item); m=(tmp-pt[0].x)/h; m=((m<M)?m:M); max=pt[0].x+(double)m*h; tmp=(tmp<Max)?tmp:max; pt[m].x=tmp; psi[1]=23; psi[2]=24; psi[3]=25; for (i=0;i<=m;i++) { psv[1]=pt[i].x; psv[2]=pt[i].y; psv[3]=pt[i].z; k1=h*parse(input[1].item, 3, psv, psi);
K1=h*parse(input[2].item, 3, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k1/2; psv[3]=pt[i].z+K1/2; k2=h*parse(input[1].item, 3, psv, psi); K2=h*parse(input[2].item, 3, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k2/2; psv[3]=pt[i].z+K2/2; k3=h*parse(input[1].item, 3, psv, psi); K3=h*parse(input[2].item, 3, psv, psi); psv[1]=pt[i].x+h; psv[2]=pt[i].y+k3; psv[3]=pt[i].z+K3; k4=h*parse(input[1].item, 3, psv, psi); K4=h*parse(input[2].item, 3, psv, psi); if (i<m) { pt[i+1].y=pt[i].y+(k1+2*k2+2*k3+k4)/6; pt[i+1].z=pt[i].z+(K1+2*K2+2*K3+K4)/6; pt[i+1].x=pt[i].x+h; } } }
RK4 is deployed in ODE1System()
based on the solutions provided by Equations (10.18a) and (10.18b).
The solution to the initial-value problem for the second-order ODE consists of reducing the equation into a system of two first-order ODEs. The two systems are then solved using RK4, using Algorithm 10.6. In Code10
, a function called ODE2toODE1System()
performs this task.
void CCode10::ODE2toODE1System() { int i, psi[6]; double psv[6]; double h, tmp;
double k1, k2, k3, k4; double K1, K2, K3, K4; pt[0].x=atof(input[2].item); pt[0].y=atof(input[3].item); pt[0].z=atof(input[4].item); h=atof(input[5].item); tmp=atof(input[6].item); m=(tmp-pt[0].x)/h; m=((m<M)?m:M); pt[m].x=tmp; psi[1]=23; psi[2]=24; psi[3]=25; for (i=0;i<=m;i++) { psv[1]=pt[i].x; psv[2]=pt[i].y; psv[3]=pt[i].z; k1=h*pt[i].z; K1=h*parse(input[1].item, 3, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k1/2; psv[3]=pt[i].z+K1/2; k2=h*(pt[i].z+K1/2); K2=h*parse(input[1].item, 3, psv, psi); psv[1]=pt[i].x+h/2; psv[2]=pt[i].y+k2/2; psv[3]=pt[i].z+K2/2; k3=h*(pt[i].z+K2/2); K3=h*parse(input[1].item, 3, psv, psi); psv[1]=pt[i].x+h; psv[2]=pt[i].y+k3; psv[3]=pt[i].z+K3; k4=h*(pt[i].z+K3); K4=h*parse(input[1].item, 3, psv, psi); if (i<m) { pt[i+1].y=pt[i].y+(k1+2*k2+2*k3+k4)/6; pt[i+1].z=pt[i].z+(K1+2*K2+2*K3+K4)/6; pt[i+1].x=pt[i].x+h; } } }
The boundary-value problem for the second-order ODE is solved using the finite difference method. The solution is provided based on Algorithm 10.7. The operating function is ODE2FD1()
, and this function represents the method in item 7 of the menu. The code fragments for this function are given as
void CCode10::ODE2FD1() { int i, j, psi[6]; double psv[6], h, tmp; double **a, *b; double *p, *q, *r, *w; b=new double [M+1]; p=new double [M+1]; q=new double [M+1]; r=new double [M+1]; w=new double [M+1]; a=new double *[M+1]; h=atof(input[5].item); m=(int)(atof(input[7].item)-atof(input[6].item))/h; m=((m<M)?m:M); pt[0].x=atof(input[6].item); pt[m].x=pt[0].x+(double)m*h; pt[0].y=atof(input[8].item); pt[m].y=atof(input[9].item); psi[1]=23; for (i=1;i<=m-1;i++) { pt[i].x=pt[i-1].x+h; psv[1]=pt[i].x; p[i]=parse(input[1].item, 1, psv, psi); q[i]=parse(input[2].item, 1, psv, psi); r[i]=parse(input[3].item, 1, psv, psi); w[i]=parse(input[4].item, 1, psv, psi); } for (i=0;i<=M;i++) a[i]=new double [M+1]; for (i=1;i<=m-1;i++) { for (j=1;j<=m-1;j++) a[i][j]=0; b[i]=0; } b[1]=w[1]-(p[1]/(h*h)-q[1]/(2*h))*pt[0].y;
b[m-1]=w[m-1]-(p[m-1]/(h*h)+q[m-1]/(2*h))*pt[m].y; for (i=1;i<=m-1;i++) { a[i][i]=−2*p[i]/(h*h)+r[i]; if (i<m-1) a[i+1][i]=p[i+1]/(h*h)-q[i+1]/(2*h); if (i>1) a[i-1][i]=p[i-1]/(h*h)+q[i-1]/(2*h); if (i>1 && i<m-1) b[i]=w[i]; } SolveSLE(a, b); for (i=0;i<=M;i++) delete a[i]; delete p, q, r, w, a, b; }
The input for the ordinary differential equation is obtained from Equation (10.21) in the form of p(x), q(x), r(x), and s(x). Their values are read as input strings from the edit boxes, and these strings are processed into numerical values through parse()
. The following code segment implements this idea:
psi[1]=23; for (i=1;i<=m-1;i++) { pt[i].x=pt[i-1].x+h; psv[1]=pt[i].x; p[i]=parse(input[1].item, 1, psv, psi); q[i]=parse(input[2].item, 1, psv, psi); r[i]=parse(input[3].item, 1, psv, psi); w[i]=parse(input[4].item, 1, psv, psi); }
Two main steps are involved in solving the boundary-value problem. First, a system of linear equations Ay = b is to be formed using the finite-difference equation in Equation (10.22). Here, A = [aij] and b = [bi] are determined using Equations (10.24a) and (10.24b), respectively. The code segment is given as
for (i=1;i<=m-1;i++) { for (j=1;j<=m-1;j++) a[i][j]=0; b[i]=0; }
b[1]=w[1]-(p[1]/(h*h)-q[1]/(2*h))*pt[0].y; b[m-1]=w[m-1]-(p[m-1]/(h*h)+q[m-1]/(2*h))*pt[m].y; for (i=1;i<=m-1;i++) { a[i][i]=−2*p[i]/(h*h)+r[i]; if (i<m-1) a[i+1][i]=p[i+1]/(h*h)-q[i+1]/(2*h); if (i>1) a[i-1][i]=p[i-1]/(h*h)+q[i-1]/(2*h); if (i>1 && i<m-1) b[i]=w[i]; }
The second step is to solve the system of linear equations. We apply the Gaussian elimination method by calling a function called SolveSLE()
. Two arguments are supplied as input to this function, namely, A = [aij] and b = [bi]. SolveSLE()
solves the problem and produces the solution as y = [yi], which is pt[i].y
in the program. The function is shown as follows:
void CCode10::SolveSLE(double **a, double *b) { int i, j, k, lo, hi; double m1, Sum; if (fMenu==7) { lo=1; hi=m-1; } if (fMenu==8) { lo=0; hi=m; } for (k=lo;k<=hi-1;k++) for (i=k+1;i<=hi;i++) { m1=a[i][k]/a[k][k]; for (j=lo;j<=hi;j++) a[i][j] -= m1*a[k][j]; b[i] -= m1*b[k]; } for (i=hi;i>=lo;i--) { Sum=0; pt[i].y=0; for (j=i;j<=hi;j++)
Sum += a[i][j]*pt[j].y; pt[i].y=(b[i]-Sum)/a[i][i]; } }
SolveSLE()
is shared by items 7 and 8 in the menu. The size of the matrices in the systems of linear equations in the two items are not the same. Two local variables called lo and hi have been introduced to represent the starting and ending indices of the elements in the matrices. The current system has a size of (m − 1) × (m − 1), whose rows and columns start with i = 1 to i = m − 1. Therefore, lo
and hi
are 1 and m − 1, respectively.
Item 8 in the menu is represented by ODE2FD2()
. This function solves the boundary-value problem in the second-order ODE whose boundary conditions are given in the form of first-order derivatives. Basically, ODE2FD2()
is a little bit different from ODE2FD1() as it has to support the differentiated boundary conditions, which results in a(m + 1) × (m + 1) system of linear equations. The complete code for ODE2FD2()
is given below:
void CCode10::ODE2FD2() { int i, j, psi[6]; double psv[6], tmp; double h, alpha, beta; double **a, *b; double *p, *q, *r, *w; b=new double [M+1]; p=new double [M+1]; q=new double [M+1]; r=new double [M+1]; w=new double [M+1]; a=new double *[M+1]; for (i=0;i<=M;i++) a[i]=new double [M+1]; h=atof(input[5].item); m=(int)(atof(input[7].item)-atof(input[6].item))/h; m=((m<M)?m:M); pt[0].x=atof(input[6].item); pt[m].x=pt[0].x+(double)m*h; alpha=atof(input[8].item); beta=atof(input[9].item); for (i=0;i<=m;i++) { if (i<m) pt[i+1].x=pt[i].x+h;
psv[1]=pt[i].x; psi[1]=23; p[i]=parse(input[1].item, 1, psv, psi); q[i]=parse(input[2].item, 1, psv, psi); r[i]=parse(input[3].item, 1, psv, psi); w[i]=parse(input[4].item, 1, psv, psi); } for (i=0;i<=m;i++) { for (j=0;j<=m;j++) a[i][j]=0; b[i]=0; } for (i=0;i<=m;i++) { a[i][i]=−2*p[i]/(h*h)+r[i]; if (i>0 && i<M) a[i][i+1]=p[i]/(h*h)+q[i]/(2*h); if (i<m-1) a[i+1][i]=p[i+1]/(h*h)-q[i+1]/(2*h); if (i>0 && i<M) b[i]=w[i]; } a[0][1]=2*p[0]/m; a[m][m-1]=2*p[m]/(h*h); b[0]=w[0]+(p[0]/(h*h)-q[0]/(2*h))*2*h*alpha; b[m]=w[m]-(p[m]/(h*h)+q[m]/(2*h))*2*h*beta; SolveSLE(a, b); for (i=0;i<=M;i++) delete a[i]; delete p, q, r, w, a, b; }
As in item 7, the solution to the boundary-value problem consists of two main steps. First, a system of linear equations is created using the finite-difference equation of Equation (10.22). We have to solve Ay = b, and A= [aij] and b = [bi] are obtained from Equations (10.28a), (10.28b), (10.28c), and (10.28d). The boundary conditions are given in the form of first-order derivatives with α and β as the left and right derivative values in the interval. Therefore, y0 and yy become the unknowns along with yi for i= 1, 2, ..., m − 1.
There are m + 1 unknowns, and this creates a system of linear equations of size (m + 1) × (m + 1). From Equations (10.28a), (10.28b), (10.28c), and (10.28d), we obtain the code segment for creating A = [aij] and b = [bi]:
for (i=0;i<=m;i++)
{ a[i][i]=−2*p[i]/(h*h)+r[i]; if (i>0 && i<M) a[i][i+1]=p[i]/(h*h)+q[i]/(2*h); if (i<m-1) a[i+1][i]=p[i+1]/(h*h)-q[i+1]/(2*h); if (i>0 && i<m) b[i]=w[i]; } a[0][1]=2*p[0]/m; a[m][m-1]=2*p[m]/(h*h); b[0]=w[0]+(p[0]/(h*h)-q[0]/(2*h))*2*h*alpha; b[m]=w[m]-(p[m]/(h*h)+q[m]/(2*h))*2*h*beta;
The system of linear equations is solved using SolveSLE()
. The unknowns in the (m + 1) × (m + 1) system are yi for i = 0, 1, ..., m, and this prompts lo=0
and hi=m
in SolveSLE()
. The solution is provided as pt[i].y
, which represents y = [yi].
The chapter discusses the numerical solutions to the first- and second-order ordinary differential equations. The first-order differential equations involve initial-value problems, and their numerical solutions consist of the Taylor series, Runge-Kutta of order 2, Runge-Kutta of order 4, and the Adam-Bashforth-Moulton methods. Problems in the second-order ordinary differential equations include both the initial-value and boundary-value problems. Their initial-value solutions are derived from the first-order methods, whereas the boundary-value problems are solved using the finite-difference methods.
Several creative projects can be embarked from our discussion. Problems in ordinary differential equations are commonly encountered in science and engineering. The problems are expressed in the form of models involving ordinary differential equations, whereas simulations are performed to support these theoretical models. In most cases, visualization from the solution is necessary to support the work. Therefore, programming with a friendly graphical user interface is needed for visualizing the results from the applications.
The following exercises are intended to test your understanding of the materials discussed in this chapter. Use six decimal places for calculations.
Solve and compare the results from the following initial-value problems using the Taylor series method of order 2 and 3:
y' = 3sinx − 4cosx, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y (0) = − 1.
y' = 3x2y − 4y, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y (0) = −1.
y' = 3 sinxy, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y(0) = −1.
Run Code10
to check the results from your work.
Solve and compare the results from the following initial-value problems using RK2 (with r = 0.8) and RK4:
y' = 3 sin x − 4cosx, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y (0) = − 1.
y' = 3x2y − 4y, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y (0) = −1.
y' = 3 sinxy, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y(0) = −1.
3xy' − 2y2 + 4x = − 1, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y(0) = −1.
Run Code10
to check the results from your work.
Solve the following initial-value problems using the Adams-Bashforth-Moulton method with RK4 as their starting predictor values:
y' = 3 sin x − 4cosx, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y (0) = − 1.
y' = 3x2y − 4y, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y (0) = −1.
y' = 3 sin xy, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y (0) = −1.
3xy' − 2y2 + 4x = − 1, for 0 ≤ x ≤ 1, h = Δx = 0.25 and y(0) = −1.
Run Code10
to check the results from your work.
Solve the following initial-value problems for the system of first-order ordinary differential equations using RK4:
y' = 2x − 3y and z' = 3z + 2x, for 0 ≤ x ≤ 1. The initial values are y(0) = −1 and z(0) = −2.
y' = 2 cos y − 3 sin x and z' = 1 − 2 sin xyz, for 0 ≤ x ≤ 1. The initial values are y(0) = −0.7 and z(0) = 0.2.
Run Code10
to check the results from your work.
Solve the following initial-value problems for the second-order ordinary differential equations using RK4 by reducing the equations to a system of first-order ordinary differential equations:
y" = 3y' + y − x − 1, for 0 ≤ x ≤ 1 and h = Δx = 0.25. The initial values are y(0) = 0.5 and y'(0) = −0.5.
y" = 3x2y' + 2xy − 3x − 2, for 0 ≤ x ≤ 1 and h = Δx = 0.25. The initial values are y(0) = 0.5 and y'(0) = −0.5.
y" = (3 sin x) y' + (2 cos x)y − x − 1, for 0 < x < 1 and h = Δx = 0.25. The initial values are y(0) = 0.5 and y'(0) = −0.5.
Run Code10
to check the results from your work.
Solve the boundary-value problems for the second-order ordinary differential equations using the finite-difference method, given by
y" = 3 y' + y − x − 1, for 0 < x 1, and h = Δx = 0.25, y(0) = −1 and y(1) = −0.5.
y" = 3x2y' + 2xy − 3x − 2, for 0 < x 2, and h = Δx = 0.5, y(0) = 0 and y(2) = −2.
2y" + 3x2y' − 3xy − 4x = 1, for −1 < x 1, and h = Δx = 0.5, y(−1) = − 1 and y(1) = − 0.5.
y" = (3sin x)y' + (2cos x)y − x − 1, for 0 ≤ x ≤ 1, and h = Δx = 0.25, y'(0) = −0.5 and y'(1)= 1.
y" = (3sinx)y' + (2cos x)y − x − 1, for 0 ≤ x ≤ 1, and h = Δx = 0.25, y'(0) = −1 and y(1) = −0.5.
y" = (3sin x)y' + (2cos x)y − x 1, for 0 ≤ x ≤ 1, and h = Δx = 0.25, y(0) − y'(0) = −1 and y(1) + 2 − (1) = −0.5.
Run Code10
to check the results from your work in problems (a), (b), (c), and (d).
Modify the Code10
project by improving on the module in the Taylor series method to allow the user to determine the order of the series from two to five. This improvement requires new edit boxes for collecting the additional derivatives as input to the method.
Modify the Code10
project by improving on the module in the Adams-Bashforth-Moulton method to allow the user to determine the step size in both the predictor and the corrector functions according to the given equations in Tables 10.4 and 10.5.
Modify the Code10
project by improving on the finite-difference method for the second-order ordinary differential equations to support mixed boundary-value conditions. For example, the boundary values may be given in the form of Problems 6(e) and (f) in the above numerical exercises.
Modify the Code10
project to add some important features, such as file open and retrieve options. These features are important because data are normally placed in separate files from the program files.