Solving initial value problems

In this section, we will consider the mathematical task of numerically solving a system of ordinary equations for given initial values:

y'(t) = f(t, y)      y(t0) = y0∈ ℝn

The solution of this problem is a function y. A numerical method aims at computing good approximations, yi y(ti) at discrete points, the communications points ti, within the interval of interest [t0, te]. We collect the data that describes the problem in a class, as follows:

class IV_Problem:
    """
    Initial value problem (IVP) class
    """
    def __init__(self, rhs, y0, interval, name='IVP'):
        """
        rhs 'right hand side' function of the ordinary differential
                                                   equation f(t,y)
        y0 array with initial values
        interval start and end value of the interval of independent
        variables often initial and end time
        name descriptive name of the problem
        """
        self.rhs = rhs
        self.y0 = y0
        self.t0, self.tend = interval
        self.name = name

The differential equation:

Solving initial value problems

describes a mathematical pendulum; y1 describes its angle with respect to the vertical axis, g is the gravitation constant, and l is its length. The initial angle is π/2 and the initial angular velocity is zero.

The pendulum problem becomes an instance of the problem class, as follows:

def rhs(t,y):
    g = 9.81
    l = 1.
    yprime = array([y[1], g / l * sin(y[0])])
    return yprime
 
pendulum = IV_Problem(rhs, array([pi / 2, 0.]), [0., 10.] ,
                                            'mathem. pendulum')

There might be different views on the problem at hand, leading to a different design of the class. For example, one might want to consider the interval of independent variables as a part of a solution process instead of the problem definition. The same holds when considering initial values. They might, as we did here, be considered a part of the mathematical problem, while other authors might want to allow variation of initial values by putting them as a part of the solution process.

The solution process is modeled as another class:

class IVPsolver:
    """
    IVP solver class for explicit one-step discretization methods
    with constant step size
    """
    def __init__(self, problem, discretization, stepsize):
        self.problem = problem
        self.discretization = discretization
        self.stepsize = stepsize
    def one_stepper(self):
        yield self.problem.t0, self.problem.y0
        ys = self.problem.y0
        ts = self.problem.t0
        while ts <= self.problem.tend:
            ts, ys = self.discretization(self.problem.rhs, ts, ys,
                                                self.stepsize)
            yield ts, ys
    def solve(self):
        return list(self.one_stepper())

We continue by first defining two discretization schemes:

  • Explicit Euler method:
      def expliciteuler(rhs, ts, ys, h):
          return ts + h, ys + h * rhs(ts, ys)
  • Classical Runge-Kutta four-stage method (RK4):
      def rungekutta4(rhs, ts, ys, h):
          k1 = h * rhs(ts, ys)
          k2 = h * rhs(ts + h/2., ys + k1/2.) 
          k3 = h * rhs(ts + h/2., ys + k2/2.)
          k4 = h * rhs(ts + h, ys +  k3)
          return ts + h, ys + (k1 + 2*k2 + 2*k3 + k4)/6.

With these, we can create instances to obtain the corresponding discretized versions of the pendulum ODE:

pendulum_Euler = IVPsolver(pendulum, expliciteuler, 0.001) 
pendulum_RK4 = IVPsolver(pendulum, rungekutta4, 0.001)

We can solve the two discrete models and plot the solution and the angle difference:

sol_Euler = pendulum_Euler.solve()
sol_RK4 = pendulum_RK4.solve()
tEuler, yEuler = zip(*sol_Euler)
tRK4, yRK4 = zip(*sol_RK4)
subplot(1,2,1), plot(tEuler,yEuler),
       title('Pendulum result with Explicit Euler'),
       xlabel('Time'), ylabel('Angle and angular velocity')
subplot(1,2,2), plot(tRK4,abs(array(yRK4)-array(yEuler))),
       title('Difference between both methods'),
       xlabel('Time'), ylabel('Angle and angular velocity')

Solving initial value problems

Figure14.4: Pendulum simulation with the explicit Euler method and comparison with the results of the more accurate Runge–Kutta 4 method

It is worthwhile discussing alternative class designs. What should be put in separate classes, what should be bundled into the same class?

  • We strictly separated the mathematical problem from the numerical method. Where should the initial values go? Should they be part of the problem or part of the solver? Or should they be left as input parameter for the solve method of the solver instance? One might even design the program so that it allows several possibilities. The decision to use one of these alternatives depends on the future use of this program. Looping over various initial values as in parameter identification would be eased by leaving the initial values as input parameters for the solve method. On the other hand, simulating different model variants with the same initial values would motivate to couple the initial values to the problem.
  • We presented for simplicity only solvers with constant and given step size. Is the design of the IVPsolver class appropriate for a future extension of adaptive methods, where a tolerance rather than a step size is given?
  • We suggested earlier to use a generator construction for the stepping mechanism. Adaptive methods need to reject steps from time to time. Is this need conflicting with the design of the stepping mechanism in IVPsolver.onestepper?
  • We encourage you to check the design of the two SciPy tools for solving initial values, namely scipy.integrate.ode and scipy.integrate.odeint.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.179.85