Annotated example

This example walks you through the process of calculating simulated seismic observables. This example can also be downloaded as a standalone Python script. It is based on examples shown in O’Toole & Woodhouse (2011).

We begin by importing some standard libraries:

import numpy as np
import matplotlib.pyplot as plt

We then import the pyprop8 core routines, and some utility functions:

import pyprop8 as pp
from pyprop8.utils import stf_trapezoidal, make_moment_tensor, rtf2xyz

Our first task is to define the earth model we wish to use. This requires us to create an instance of LayeredStructureModel(). We specify thicknesses, P- and S-wave velocities and density in each layer:

#                                  thick.  Vp    Vs    rho
model = pp.LayeredStructureModel([[ 3.00, 1.80, 0.00, 1.02],
                                  [ 2.00, 4.50, 2.40, 2.57],
                                  [ 5.00, 5.80, 3.30, 2.63],
                                  [20.00, 6.50, 3.65, 2.85],
                                  [np.inf,8.00, 4.56, 3.34]])

Note that this example has an S-wave velocity of zero in the uppermost layer - i.e. this is a fluid.

Next, we define the properties of the seismic source. We choose to use make_moment_tensor() to construct a moment tensor from strike, dip and rake angles. However, this outputs a moment tensor using the global seismology convention of polar coordinates, and so we need to use rtf2xyz() to convert it to Cartesian form. We set the force vector to zero:

strike = 340
dip = 90
rake = 0
scalar_moment = 2.4E8
Mxyz = rtf2xyz(make_moment_tensor(strike, dip, rake, scalar_moment,0,0))
F =np.zeros([3,1])

For this example, we assume the event occurred at the origin of the coordinate system, at a depth of 34km below the ‘top’ of the model (which, given the 3km water layer, corresponds to a depth of 31km below the sea floor). We can then create an instance of PointSource:

event_x = 0
event_y = 0
event_depth = 34
event_time = 0
source =  pp.PointSource(event_x, event_y, event_depth, Mxyz, F, event_time)

Finally, we need to define our recording locations. For this example, we create a set of 18 equally-spaced stations due North of the earthquake:

stations = pp.ListOfReceivers(xx = np.zeros(18),yy=np.linspace(30,200,18),depth=3)

We are now ready to perform our simulations. We request 181 data points, sampled at 2Hz, and apply a trapezoidal source time-functions:

nt = 181
dt = 0.5
tt,seis = pp.compute_seismograms(model, source, stations, nt, dt,
                                 xyz=False,source_time_function=lambda w:stf_trapezoidal(w,3,6))

Because we (implicitly) used the default option of squeeze_outputs=True, the output array seis has shape (18, 3, 181): 18 stations, with 3 components and 181 time series points for each. We can plot the result:

fig = plt.figure(figsize=(5,8))
ax = fig.subplots(3,1)
ax[0].set_title("Radial")
ax[1].set_title("Transverse")
ax[2].set_title("Vertical")
for i in range(18):
    # Plot each trace, shifting by 50 units between each
    ax[0].plot(tt,seis[i,0,:]-50*i,'k')
    ax[1].plot(tt,seis[i,1,:]-50*i,'k')
    ax[2].plot(tt,seis[i,2,:]-50*i,'k')
for i in range(3):
    ax[i].set_xlim(0,90)
    ax[i].set_ylim(-1000,400)
    ax[i].set_xticks([])
    ax[i].set_yticks([])
ax[2].set_xticks([0,30,60,90])
plt.tight_layout()
plt.savefig('fig1.png',dpi=300)
plt.show()

All being well, you should obtain the following image, which matches Fig. 1 of O’Toole & Woodhouse (2011):

Seismograms produced by pyprop8

Our second example simulates regional static offset. We change the earth model to one representing a homogeneous half-space, and we change our station distribution to a dense, regular array:

model_halfspace = pp.LayeredStructureModel([[np.inf,6.00,3.46,2.70]])
stations = pp.RegularlyDistributedReceivers(1,151,300,0,360,72)

Now it is straightforward to compute the static offset information:

static = pp.compute_static(model_halfspace,source,stations)
amax = abs(static).max()

To plot the data, we use contourf() to interpolate between stations. The output array, static, has shape (nr, nphi, 3), where nr and nphi are the numbers of stations defining our (polar) grid of receivers. We use as_xy() to obtain x- and y-coordinates for each station:

fig = plt.figure(figsize=(8,5))
ax = fig.subplots(2,3)
ax[0,0].contourf(*stations.as_xy(),static[:,:,1],levels=np.linspace(-1.05*amax,1.05*amax,101),cmap=plt.cm.RdBu)
ax[0,1].contourf(*stations.as_xy(),static[:,:,0],levels=np.linspace(-1.05*amax,1.05*amax,101),cmap=plt.cm.RdBu)
sc = ax[0,2].contourf(*stations.as_xy(),static[:,:,2],levels=np.linspace(-1.05*amax,1.05*amax,101),cmap=plt.cm.RdBu)
for i in range(3):
    ax[0,i].set_xlim(-100,100)
    ax[0,i].set_ylim(-100,100)
    ax[0,i].set_aspect(1)
    ax[0,i].set_xticks([])
    ax[0,i].set_yticks([])
ax[0,0].set_title('North')
ax[0,1].set_title('East')
ax[0,2].set_title('Vertical')

ax[1,1].set_aspect(.1)
c = plt.colorbar(sc,cax=ax[1,1],orientation='horizontal',label='Displacement (mm)')
c.set_ticks([-amax,0,amax])
ax[1,0].set_visible(False)
ax[1,2].set_visible(False)
plt.tight_layout()
plt.savefig('fig2.png',dpi=300)
plt.show()

This should produce a figure matching Fig. 2 of O’Toole & Woodhouse (2011):

Static displacements produced by pyprop8