[SciPy-User] how to create a phase portrait for a nonlinear diff. eqn.

John Hunter jdh2358 at gmail.com
Wed Jan 5 10:08:28 EST 2011


On Tue, Jan 4, 2011 at 8:07 PM, Ryan Krauss <ryanlists at gmail.com> wrote:

> I am teaching a nonlinear controls course this coming semester.  I
> plan to have the students write code to generate phase portraits as a
> project.  It is fairly easy to randomly (or intelligently?) create
> initial condition seeds and run integrate.odeint.  What isn't obvious
> to me is how to put arrows on phase portrait lines to indicate the
> direction of the evolution over time.  For example, the attached code
> creates a phase portrait for a fairly simple system.  The graph is
> shown in the attached png.  But this equilibrium point is either
> stable or unstable depending on whether the curve spirals in or out
> over time.  How do I write code to automatically determine the
> direction of increasing time and indicate it by arrow heads on the
> graph?
>
> I know x, xdot, and time as vectors for each point on the graph.  I
> guess I could numerically determine (d xdot)/dx for each point, but is
> that the best route to go?  And that leads to issues as dx gets
> small....
>


here's one example

import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate

def dr(r, f):
    """
    return the derivative of *r* (the rabbit population) evaulated as a
    function of *r* and *f*.  The function should work whether *r* and *f*
    are scalars, 1D arrays or 2D arrays.  The return value should have
    the same dimensionality (shape) as the inputs *r* and *f*.
    """
    return alpha*r - beta*r*f

def df(r, f):
    """
    return the derivative of *f* (the fox population) evaulated as a
    function of *r* and *f*.  The function should work whether *r* and *f*
    are scalars, 1D arrays or 2D arrays.  The return value should have
    the same dimensionality (shape) as the inputs *r* and *f*.
    """
    return gamma*r*f - delta*f

def derivs(state, t):
    """
    Return the derivatives of R and F, stored in the *state* vector::

       state = [R, F]

    The return data should be [dR, dF] which are the derivatives of R
    and F at position state and time *t*
    """
    R, F = state          # and foxes
    deltar = dr(r, f)     # in rabbits
    deltaf = df(r, f)     # in foxes
    return deltar, deltaf

# the parameters for rabbit and fox growth and interactions
alpha, delta = 1, .25
beta, gamma = .2, .05

# the initial population of rabbits and foxes
r0 = 20
f0 = 10

# create a time array from 0..100 sampled at 0.1 second steps
t = np.arange(0.0, 100, 0.1)


y0 = [r0, f0]  # the initial [rabbits, foxes] state vector

# integrate your ODE using scipy.integrate.  Read the help to see what
# is available
 HINT: see scipy.integrate.odeint
y = integrate.odeint(derivs, y0, t)

# the return value from the integration is a Nx2 array.  Extract it
# into two 1D arrays caled r and f using numpy slice indexing
r = y[:,0]  # extract the rabbits vector
f = y[:,1]  # extract the foxes vector

# time series plot: plot the population of rabbits and foxes as a
# funciton of time
plt.figure()
plt.plot(t, r, label='rabbits')
plt.plot(t, f, label='foxes')
plt.xlabel('time (years)')
plt.ylabel('population')
plt.title('population trajectories')
plt.grid()
plt.legend()
plt.savefig('lotka_volterra.png', dpi=150)
plt.savefig('lotka_volterra.eps')

# phase-plane plot: plot the population of foxes versus rabbits
# make sure you include and xlabel, ylabel and title
plt.figure()
plt.plot(r, f, color='red')
plt.xlabel('rabbits')
plt.ylabel('foxes')
plt.title('phase plane')


# Create 2D arrays for R and F to represent the entire phase plane --
# the point (R[i,j], F[i,j]) is a single (rabbit, fox) combinations.
# pass these arrays to the functions dr and df above to get 2D arrays
# of dR and dF evaluated at every point in the phase plance.
rmax = 1.1 * r.max()
fmax = 1.1 * f.max()
R, F = np.meshgrid(np.arange(-1, rmax), np.arange(-1, fmax))
dR = dr(R, F)
dF = df(R, F)
plt.quiver(R, F, dR, dF)


# Now find the nul-clines, for dR and dF respectively.  These are the
# points where dR=0 and dF=0 in the (R, F) phase plane.  You can use
# matplotlib's countour routine to find the zero level.  See the
# levels keyword to contour.  You will need a fine mesh of R and F,
# reevaluate dr and df on the finer grid, and use contour to find the
# level curves
R, F = np.meshgrid(np.arange(-1, rmax, 0.1), np.arange(-1, fmax, 0.1))
dR = dr(R, F)
dF = df(R, F)
plt.contour(R, F, dR, levels=[0], linewidths=3, colors='blue')
plt.contour(R, F, dF, levels=[0], linewidths=3, colors='green')
plt.ylabel('foxes')
plt.title('trajectory, direction field and null clines')

plt.savefig('lotka_volterra_pplane.png', dpi=150)
plt.savefig('lotka_volterra_pplane.eps')
plt.show()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.scipy.org/pipermail/scipy-user/attachments/20110105/82bcbb2b/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: lotka_volterra.py
Type: application/octet-stream
Size: 3613 bytes
Desc: not available
URL: <http://mail.scipy.org/pipermail/scipy-user/attachments/20110105/82bcbb2b/attachment.obj>


More information about the SciPy-User mailing list