The excitation force

Considering the application where the present mathematical model describes the vibrations of a vehicle driving along a bumpy road, we need to establish the force array F from the shape of the road \( h(x) \). Various shapes are available as a file with web address http://hplbit.bitbucket.org/data/bumpy/bumpy.dat.gz. The Python functionality for downloading this gzip compressed file as a local file bumpy.dat.gz and reading it into a numpy array goes as follows:

filename = 'bumpy.dat.gz'
url = 'http://hplbit.bitbucket.org/data/bumpy/bumpy.dat.gz'
import urllib
urllib.urlretrieve(url, filename)
h_data = np.loadtxt(filename)     # read numpy array from file

The h_data object is a rectangular numpy array where the first column contains the \( x \) coordinates along the road and the next columns contain various road shapes \( h(x) \). We can extract the \( x \) data and redefine h_data to contain solely the \( h(x) \) shapes:

x = h_data[0,:]                # 1st column: x coordinates
h_data = h_data[1:,:]          # other columns: h shapes

In general, the syntax a[s:t:i,2] gives a view (not a copy) to the part of the array a where the first index goes from s to t, but not including the t value, in increments of i, and the second index is fixed at 2. Just writing : for an index means all legal values of this index.

Given \( h(x) \), the corresponding acceleration \( a(t) \) needed in the force \( F(t)=-ma(t) \), follows from \( a(t)=h''(vt)v^2 \), where \( v \) is the velocity of the vehicle. The computation may utilize a finite difference approximation for the second-order derivative \( h'' \) and be encapsulated in a Python function:

def acceleration(h, x, v):
    """Compute 2nd-order derivative of h."""
    # Method: standard finite difference aproximation
    d2h = np.zeros(h.size)
    dx = x[1] - x[0]
    for i in range(1, h.size-1, 1):
        d2h[i] = (h[i-1] - 2*h[i] + h[i+1])/dx**2
    # Extraplolate end values from first interior value
    d2h[0] = d2h[1]
    d2h[-1] = d2h[-2]
    a = d2h*v**2
    return a

Note that here, h is a one-dimensional array containing the \( h(x) \) values corresponding to a given coordinate array x. Also note that we for mathematical simplicity set \( h''(x) \) at the end points equal to \( h''(x) \) at the closest interior point.

The computations of d2h above was done array element by array element. This loop can be a slow process in Python for long arrays. To speed up computations dramatically, we can invoke a vectorization of the above algorithm. This means that we get rid of the loops and perform arithmetics on complete (or almost complete) arrays. The vectorized form of the acceleration function goes like

def acceleration_vectorized(h, x, v):
    """Compute 2nd-order derivative of h. Vectorized version."""
    d2h = np.zeros(h.size)
    dx = x[1] - x[0]
    d2h[1:-1] = (h[:-2] - 2*h[1:-1] + h[2:])/dx**2
    # Extraplolate end values from first interior value
    d2h[0] = d2h[1]
    d2h[-1] = d2h[-2]
    a = d2h*v**2
    return a

For each shape \( h(x) \) we want to compute the corresponding vertical displacement \( u(t) \) using the mathematical model (1). This can be accomplished by looping over the columns of h_data and calling solver for each column, i.e., each realization of the force \( F \). The major arrays from the computations are collected in a list data. The two first elements in data are x and t. The next elements are 3-lists [h, a, u] for each road shape. Note that some elements in data are arrays while others are list of arrays. This composition is convenient when analyzing and visualizing key quantities in the problem.

The computations of u for each road shape can be done as follows:

data = [x, t]      # key input and output data (arrays)
for i in range(h_data.shape[0]):
    h = h_data[i,:]            # extract a column
    a = acceleration(h, x, v)
    F = -m*a

    u = solver(t=t, I=0, m=m, b=b, f=f, F=F)

    data.append([h, F, u])

A parameter choice \( m=60 \) kg, \( v=5 \) m/s, \( k=60 \) N/m, and \( b=80 \) Ns/m corresponds to a velocity of 18 km/h and a mass of 60 kg, i.e., bicycle conditions.