Coverage for /opt/obspy/update-docs/src/obspy/obspy/signal/array_analysis : 85%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/env python #------------------------------------------------------------------- # Filename: array.py # Purpose: Functions for Array Analysis # Author: Martin van Driel, Moritz Beyreuther # Email: driel@geophysik.uni-muenchen.de # # Copyright (C) 2010 Martin van Driel, Moritz Beyreuther #--------------------------------------------------------------------- Functions for Array Analysis
:copyright: The ObsPy Development Team (devs@obspy.org) :license: GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) """
sigmau): """ This routine calculates the best-fitting rigid body rotation and uniform strain as functions of time, and their formal errors, given three-component ground motion time series recorded on a seismic array. The theory implemented herein is presented in the papers [Spudich1995]_, (abbreviated S95 herein) [Spudich2008]_ (SF08) and [Spudich2009]_ (SF09).
This is a translation of the Matlab Code presented in (SF09) with small changes in details only. Output has been checked to be the same as the original Matlab Code.
.. note:: ts\_ below means "time series"
:type vp: Float :param vp: P wave speed in the soil under the array (km/s) :type vs: Float :param vs: S wave speed in the soil under the array Note - vp and vs may be any unit (e.g. miles/week), and this unit need not be related to the units of the station coordinates or ground motions, but the units of vp and vs must be the SAME because only their ratio is used. :type array_coords: numpy.ndarray :param array_coords: array of dimension Na x 3, where Na is the number of stations in the array. array_coords[i,j], i in arange(Na), j in arange(3) is j coordinate of station i. units of array_coords may be anything, but see the "Discussion of input and output units" above. The origin of coordinates is arbitrary and does not affect the calculated strains and rotations. Stations may be entered in any order. :type ts1: numpy.ndarray :param ts1: array of x1-component seismograms, dimension nt x Na. ts1[j,k], j in arange(nt), k in arange(Na) contains the kth time sample of the x1 component ground motion at station k. NOTE that the seismogram in column k must correspond to the station whos coordinates are in row k of in.array_coords. nt is the number of time samples in the seismograms. Seismograms may be displacement, velocity, acceleration, jerk, etc. See the "Discussion of input and output units" below. :type ts2: numpy.ndarray :param ts2: same as ts1, but for the x2 component of motion. :type ts3: numpy.ndarray :param ts3: same as ts1, but for the x3 (UP or DOWN) component of motion. :type sigmau: Float or numpy.ndarray :param sigmau: standard deviation (NOT VARIANCE) of ground noise, corresponds to sigma-sub-u in S95 lines above eqn (A5). NOTE: This may be entered as a scalar, vector, or matrix!
* If sigmau is a scalar, it will be used for all components of all stations. * If sigmau is a 1D array of length Na, sigmau[i] will be the noise assigned to all components of the station corresponding to array_coords[i,:] * If sigmau is a 2D array of dimension Na x 3, then sigmau[i,j] is used as the noise of station i, component j.
In all cases, this routine assumes that the noise covariance between different stations and/or components is zero. :type subarray: numpy.ndarray :param subarray: NumPy array of subarray stations to use. I.e. if subarray = array([1, 4, 10]), then only rows 1, 4, and 10 of array_coords will be used, and only ground motion time series in the first, fourth, and tenth columns of ts1 will be used. Nplus1 is the number of elements in the subarray vector, and N is set to Nplus1 - 1. To use all stations in the array, set in.subarray = arange(Na), where Na is the total number of stations in the array (equal to the number of rows of in.array_coords. Sequence of stations in the subarray vector is unimportant; i.e. subarray = array([1, 4, 10]) will yield essentially the same rotations and strains as subarray = array([10, 4, 1]). "Essentially" because permuting subarray sequence changes the d vector, yielding a slightly different numerical result. :return: Dictionary with fields: | **A:** (array, dimension 3N x 6) - data mapping matrix 'A' of | S95(A4) | **g:** (array, dimension 6 x 3N) - generalized inverse matrix | relating ptilde and data vector, in S95(A5) | **Ce:** (4 x 4) covariance matrix of the 4 independent strain | tensor elements e11, e21, e22, e33 | **ts_d:** (array, length nt) - dilatation | (trace of the 3x3 strain tensor) as a function of time | **sigmad:** scalar, standard deviation of dilatation | **ts_dh:** (array, length nt) - horizontal dilatation (also | known as areal strain) (eEE+eNN) as a function of time | **sigmadh:** scalar, standard deviation of horizontal dilatation | (areal strain) | **ts_e:** (array, dimension nt x 3 x 3) - strain tensor | **ts_s:** (array, length nt) - maximum strain | ( .5*(max eigval of e - min eigval of e) as a | function of time, where e is the 3x3 strain tensor | **Cgamma:** (4 x 4) covariance matrix of the 4 independent shear | strain tensor elements g11, g12, g22, g33 (includes full | covariance effects). gamma is traceless part of e. | **ts_sh:** (array, length nt) - maximum horizontal strain | ( .5*(max eigval of eh - min eigval of eh) | as a function of time, where eh is e(1:2,1:2) | **Cgammah:** (3 x 3) covariance matrix of the 3 independent | horizontal shear strain tensor elements gamma11, gamma12, | gamma22 gamma is traceless part of e. | **ts_wmag:** (array, length nt) - total rotation | angle (radians) as a function of time. I.e. if the | rotation vector at the j'th time step is | w = array([w1, w2, w3]), then ts_wmag[j] = sqrt(sum(w**2)) | positive for right-handed rotation | **Cw:** (3 x 3) covariance matrix of the 3 independent | rotation tensor elements w21, w31, w32 | **ts_w1:** (array, length nt) - rotation | (rad) about the x1 axis, positive for right-handed rotation | **sigmaw1:** scalar, standard deviation of the ts_w1 | (sigma-omega-1 in SF08) | **ts_w2:** (array, length nt) - rotation | (rad) about the x2 axis, positive for right-handed rotation | **sigmaw2:** scalar, standard deviation of ts_w2 | (sigma-omega-2 in SF08) | **ts_w3:** (array, length nt) - "torsion", rotation | (rad) about a vertical up or down axis, i.e. x3, positive | for right-handed rotation | **sigmaw3:** scalar, standard deviation of the torsion | (sigma-omega-3 in SF08) | **ts_tilt:** (array, length nt) - tilt (rad) | (rotation about a horizontal axis, positive for right | handed rotation) | as a function of time. tilt = sqrt( w1^2 + w2^2) | **sigmat:** scalar, standard deviation of the tilt | (not defined in SF08, From Papoulis (1965, p. 195, | example 7.8)) | **ts_data:** (array, shape (nt x 3N)). time series of | the observed displacement | differences, which are the di in S95 eqn A1. | **ts_pred:** (array, shape (nt x 3N)) time series of | the fitted model's predicted displacement difference | Note that the fitted model displacement | differences correspond to linalg.dot(A, ptilde), where A | is the big matrix in S95 eqn A4 and ptilde is S95 eqn A5. | **ts_misfit:** (array, shape (nt x 3N)) time series of the | residuals (fitted model displacement differences minus | observed displacement differences). Note that the fitted | model displacement differences correspond to | linalg.dot(A, ptilde), where A is the big | matrix in S95 eqn A4 and ptilde is S95 eqn A5. | **ts_M:** (array, length nt) Time series of M, misfit | ratio of S95, p. 688. | **ts_ptilde:** (array, shape (nt x 6)) - solution | vector p-tilde (from S95 eqn A5) as a function of time | **Cp:** 6x6 solution covariance matrix defined in SF08.
.. rubric:: Warnings
This routine does not check to verify that your array is small enough to conform to the assumption that the array aperture is less than 1/4 of the shortest seismic wavelength in the data. See SF08 for a discussion of this assumption.
This code assumes that ts1[j,:], ts2[j,:], and ts3[j,:] are all sampled SIMULTANEOUSLY.
.. rubric:: Notes
(1) Note On Specifying Input Array And Selecting Subarrays
This routine allows the user to input the coordinates and ground motion time series of all stations in a seismic array having Na stations and the user may select for analysis a subarray of Nplus1 <= Na stations.
(2) Discussion Of Physical Units Of Input And Output
If the input seismograms are in units of displacement, the output strains and rotations will be in units of strain (unitless) and angle (radians). If the input seismograms are in units of velocity, the output will be strain rate (units = 1/s) and rotation rate (rad/s). Higher temporal derivative inputs yield higher temporal derivative outputs.
Input units of the array station coordinates must match the spatial units of the seismograms. For example, if the input seismograms are in units of m/s^2, array coordinates must be entered in m.
(3) Note On Coordinate System
This routine assumes x1-x2-x3 is a RIGHT handed orthogonal coordinate system. x3 must point either UP or DOWN. """ # start the code ------------------------------------------------- # This assumes that all stations and components have the same number of # time samples, nt
# check to ensure all components have same duration raise ValueError('ts1 and ts2 have different sizes') raise ValueError('ts1 and ts3 have different sizes')
# check to verify that the number of stations in ts1 agrees with the number # of stations in array_coords msg = 'ts1 has %s columns(stations) but array_coords has ' % Na + \ '%s rows(stations)' % nrac raise ValueError(msg)
# check stations in subarray exist raise ValueError('Station number < 0 in subarray') raise ValueError('Station number > Na in subarray')
# extract the stations of the subarray to be used
# count number of subarray stations: Nplus1 and number of station # offsets: N
msg = 'The problem is underdetermined for fewer than 3 stations' raise ValueError(msg) msg = 'For a 3-station array the problem is even-determined' warnings.warn(msg)
# ------------------- NOW SOME SEISMOLOGY!! -------------------------- # constants
# form A matrix, which relates model vector of 6 displacement derivatives # to vector of observed displacement differences. S95(A3) # dim(A) = (3*N) * 6 # model vector is [ u1,1 u1,2 u1,3 u2,1 u2,2 u2,3 ] (free surface boundary # conditions applied, S95(A2)) # first initialize A to the null matrix # fill up A np.array([-eta * ss[2], \ 0., -ss[0], 0., -eta * ss[2], -ss[1]])].transpose()
#------------------------------------------------------ # define data covariance matrix Cd. # step 1 - define data differencing matrix D # dimension of D is (3*N) * (3*Nplus1)
# step 2 - define displacement u covariance matrix Cu # This assembles a covariance matrix Cu that reflects actual data errors. # populate Cu depending on the size of sigmau # sigmau is a scalar. Make all diag elements of Cu the same elif np.shape(sigmau) == (np.size(sigmau),): # sigmau is a row or column vector # check dimension is okay if np.size(sigmau) != Na: raise ValueError('sigmau must have %s elements' % Na) junk = (np.c_[sigmau, sigmau, sigmau]) ** 2 # matrix of variances Cu = np.diag(np.reshape(junk[subarray, :], (3 * Nplus1))) elif sigmau.shape == (Na, 3): Cu = np.diag(np.reshape(((sigmau[subarray, :]) ** 2).transpose(), \ (3 * Nplus1))) else: raise ValueError('sigmau has the wrong dimensions')
# Cd is the covariance matrix of the displ differences # dim(Cd) is (3*N) * (3*N)
#--------------------------------------------------------- # form generalized inverse matrix g. dim(g) is 6 x (3*N)
msg = 'Condition number is %s' % condition_number warnings.warn(msg)
# set up storage for vectors that will contain time series ts_pred, ts_misfit, ts_M, ts_data, ts_ptilde):
# other matrices
#--------------------------------------------------------------- # here we define 4x6 Be and 3x6 Bw matrices. these map the solution # ptilde to strain or to rotation. These matrices will be used # in the calculation of the covariances of strain and rotation. # Columns of both matrices correspond to the model solution vector # containing elements [u1,1 u1,2 u1,3 u2,1 u2,2 u2,3 ]' # # the rows of Be correspond to e11 e21 e22 and e33 # # the rows of Bw correspond to w21 w31 and w32 # # this is the 4x6 matrix mapping solution to total shear strain gamma # where gamma = strain - tr(strain)/3 * eye(3) # the four elements of shear are 11, 12, 22, and 33. It is symmetric. # # this is the 3x6 matrix mapping solution to horizontal shear strain # gamma # the four elements of horiz shear are 11, 12, and 22. It is symmetric.
# solution covariance matrix. dim(Cp) = 6 * 6 # corresponding to solution elements [u1,1 u1,2 u1,3 u2,1 u2,2 u2,3 ]
# Covariance of strain tensor elements # Ce should be 4x4, correspond to e11, e21, e22, e33 # Cw should be 3x3 correspond to w21, w31, w32
# Cgamma is 4x4 correspond to 11, 12, 22, and 33. # # Cgammah is 3x3 correspond to 11, 12, and 22 # # # covariance of the horizontal dilatation and the total dilatation # both are 1x1, i.e. scalars
# covariance of the (total) dilatation, ts_dd # # Cw3, covariance of w3 rotation, i.e. torsion, is 1x1, i.e. scalar
# For tilt cannot use same approach because tilt is not a linear function # of the solution. Here is an approximation : # For tilt use conservative estimate from # Papoulis (1965, p. 195, example 7.8)
# # BEGIN LOOP OVER DATA POINTS IN TIME SERIES============================== # # # data vector is differences of stn i displ from stn 1 displ # sum the lengths of the displ difference vectors
# # form solution # ptilde is (u1,1 u1,2 u1,3 u2,1 u2,2 u2,3).T # # place in uij_vector the full 9 elements of the displacement gradients # uij_vector is (u1,1 u1,2 u1,3 u2,1 u2,2 u2,3 u3,1 u3,2 u3,3).T # The following implements the free surface boundary condition # # calculate predicted data # # calculate residuals (misfits concatenated for all stations)
# Calculate ts_M, misfit ratio. # calculate summed length of misfits (residual displacements) # # #--------------------------------------------------------------- #populate the displacement gradient matrix U # # calculate strain tensors # Fung eqn 5.1 p 97 gives dui = (eij-wij)*dxj
# Three components of the rotation vector omega (=w here)
# amount of total rotation is length of rotation vector # # Calculate tilt and torsion # 7/21/06.II.6(19), amount of tilt in radians
#--------------------------------------------------------------- # # Here I calculate horizontal quantities only # ts_dh is horizontal dilatation (+ --> expansion). # Total dilatation, ts_dd, will be calculated outside the time # step loop. # # # find maximum shear strain in horizontal plane, and find its azimuth # 7/21/06.II.2(4) # 9/14/92.II.4, 7/21/06.II.2(5)
# eigvecs are principal axes, eigvals are principal strains # max shear strain, from Fung (1965, p71, eqn (8)
# calculate max of total shear strain, not just horizontal strain # eigvecs are principal axes, eigvals are principal strains # max shear strain, from Fung (1965, p71, eqn (8) #
#========================================================================= # # (total) dilatation is a scalar times horizontal dilatation owing to there # free surface boundary condition
# load output structure
semb_thres, vel_thres, frqlow, frqhigh, stime, etime, prewhiten, verbose=False, coordsys='lonlat', timestamp='mlabday'): """ DEPRECATED: Please use ``obspy.signal.array_processing()`` """ return array_processing(stream, win_len, win_frac, sll_x, slm_x, sll_y, slm_y, sl_s, semb_thres, vel_thres, frqlow, frqhigh, stime, etime, prewhiten, verbose=False, coordsys='lonlat', timestamp='mlabday', method=0)
verbose=False): """ Method to calculate the array geometry and the center coordinates in km
:param stream: Stream object, the trace.stats dict like class must contain a obspy.core.util.attribdict with 'latitude', 'longitude' (in degrees) and 'elevation' (in km), or 'x', 'y', 'elevation' (in km) items/attributes. See param coordsys :param coordsys: valid values: 'lonlat' and 'xy', choose which stream attributes to use for coordinates :param return_center: Retruns the center coordinates as extra tuple :return: Returns the geometry of the stations as 2d numpy.ndarray The first dimension are the station indexes with the same order as the traces in the stream object. The second index are the values of [lat, lon, elev] in km last index contains center [lat, lon, elev] in degrees and km if return_center is true """
geometry[i, 0] = tr.stats.coordinates.longitude geometry[i, 1] = tr.stats.coordinates.latitude geometry[i, 2] = tr.stats.coordinates.elevation else: raise TypeError('only Stream or numpy.ndarray allowed')
print("coordys = " + coordsys)
geometry[i, 1]) else: raise ValueError("Coordsys must be one of 'lonlat', 'xy'")
return np.c_[geometry.T, np.array((center_lon, center_lat, center_h))].T else:
""" Returns timeshift table for given array geometry
:param geometry: Nested list containing the arrays geometry, as returned by get_group_geometry :param sll_x: slowness x min (lower) :param sll_y: slowness y min (lower) :param sl_s: slowness step :param grdpts_x: number of grid points in x direction :param grdpts_x: number of grid points in y direction """ # unoptimized version for reference #nstat = len(geometry) # last index are center coordinates # #time_shift_tbl = np.empty((nstat, grdpts_x, grdpts_y), dtype="float32") #for k in xrange(grdpts_x): # sx = sll_x + k * sl_s # for l in xrange(grdpts_y): # sy = sll_y + l * sl_s # time_shift_tbl[:,k,l] = sx * geometry[:, 0] + sy * geometry[:,1] #time_shift_tbl[:, k, l] = sx * geometry[:, 0] + sy * geometry[:, 1] #return time_shift_tbl # optimized version mx[:, :, np.newaxis].repeat(grdpts_y, axis=2) + \ my[:, np.newaxis, :].repeat(grdpts_x, axis=1), dtype='float32')
""" :param stime: UTCDateTime to start :param etime: UTCDateTime to end """
# now we have to adjust to the beginning of real start time msg = "Specified start-time is smaller than starttime in stream" raise ValueError(msg) msg = "Specified end-time bigger is than endtime in stream" raise ValueError(msg) msg = "Difference in start times exceeds 25% of samp rate" warnings.warn(msg)
""" Returns array transfer function as a function of wavenumber difference
:type coords: numpy.ndarray :param coords: coordinates of stations in longitude and latitude in degrees elevation in km, or x, y, z in km :type coordsys: string :param coordsys: valid values: 'lonlat' and 'xy', choose which coordinates to use :param klim: either a float to use symmetric limits for wavenumber differences or the tupel (kxmin, kxmax, kymin, kymax) """ elif isinstance(klim, tuple): if len(klim) == 4: kxmin = klim[0] kxmax = klim[1] kymin = klim[2] kymax = klim[3] else: raise TypeError('klim must either be a float or a tuple of length 4')
coords[k, 0] * kx + coords[k, 1] * ky))
coordsys='lonlat'): """ Returns array transfer function as a function of slowness difference and frequency.
:type coords: numpy.ndarray :param coords: coordinates of stations in longitude and latitude in degrees elevation in km, or x, y, z in km :type coordsys: string :param coordsys: valid values: 'lonlat' and 'xy', choose which coordinates to use :param slim: either a float to use symmetric limits for slowness differences or the tupel (sxmin, sxmax, symin, symax) :type fmin: double :param fmin: minimum frequency in signal :type fmax: double :param fmin: maximum frequency in signal :type fstep: double :param fmin: frequency sample distance """ elif isinstance(slim, tuple): if len(slim) == 4: sxmin = slim[0] sxmax = slim[1] symin = slim[2] symax = slim[3] else: raise TypeError('slim must either be a float or a tuple of length 4')
+ coords[l, 1] * sy) * 2 * np.pi * f))
np.savez('pow_map_%d.npz' % i, pow_map) np.savez('apow_map_%d.npz' % i, apow_map)
sl_s, semb_thres, vel_thres, frqlow, frqhigh, stime, etime, prewhiten, verbose=False, coordsys='lonlat', timestamp='mlabday', method=0, store=nop): """ Method for Seismic-Array-Beamforming/FK-Analysis/Capon
:param stream: Stream object, the trace.stats dict like class must contain a obspy.core.util.AttribDict with 'latitude', 'longitude' (in degrees) and 'elevation' (in km), or 'x', 'y', 'elevation' (in km) items/attributes. See param coordsys :type win_len: Float :param win_len: Sliding window length in seconds :type win_frac: Float :param win_frac: Fraction of sliding window to use for step :type sll_x: Float :param sll_x: slowness x min (lower) :type slm_x: Float :param slm_x: slowness x max :type sll_y: Float :param sll_y: slowness y min (lower) :type slm_y: Float :param slm_y: slowness y max :type sl_s: Float :param sl_s: slowness step :type semb_thres: Float :param semb_thres: Threshold for semblance :type vel_thres: Float :param vel_thres: Threshold for velocity :type frqlow: Float :param frqlow: lower frequency for fk/capon :type frqhigh: Float :param frqhigh: higher frequency for fk/capon :type stime: UTCDateTime :param stime: Starttime of interest :type etime: UTCDateTime :param etime: Endtime of interest :type prewhiten: int :param prewhiten: Do prewhitening, values: 1 or 0 :param coordsys: valid values: 'lonlat' and 'xy', choose which stream attributes to use for coordinates :type timestamp: string :param timestamp: valid values: 'julsec' and 'mlabday'; 'julsec' returns the timestamp in secons since 1970-01-01T00:00:00, 'mlabday' returns the timestamp in days (decimals represent hours, minutes and seconds) since '0001-01-01T00:00:00' as needed for matplotlib date plotting (see e.g. matplotlibs num2date) :type method: int :param method: the method to use 0 == bf, 1 == capon :type store: int :param store: a function which is called on each iteration with the relative power map and the time offset as argument. Usefull for storing or plotting the map for each iteration. For this purpose the dump and nop function of this module can be used. :return: numpy.ndarray of timestamp, relative relpow, absolute relpow, backazimut, slowness """
# check that sampling rates do not vary msg = 'in sonic sampling rates of traces in stream are not equal' raise ValueError(msg)
print("geometry:") print(geometry) print("stream contains following traces:") print(stream) print("stime = " + str(stime) + ", etime = " + str(etime))
sl_s, grdpts_x, grdpts_y) # offset of arrays # # loop with a sliding window over the dat trace array and apply bbfk #
# generate plan for rfftr # to spead up the routine a bit we estimate all steering vectors in advance deltaf, time_shift_table, steer) spoint[i] + offset + nsamp] except IndexError: break # computing the covariances of the signal at different receivers else: # P(f) = 1/(e.H R(f)^-1 e)
steer, R, nsamp, nstat, prewhiten, grdpts_x, grdpts_y, nfft, nf, dpow, method) msg = 'generalizedBeamforming exited with error %d' raise Exception(msg % errcode) # here we compute baz, slow
slow = 1e-8 slow])) print(newstart, (newstart + (nsamp / fs)), res[-1][1:])
pass # 719162 == hours between 1970 and 0001 else: msg = "Option timestamp must be one of 'julsec', or 'mlabday'" raise ValueError(msg)
if __name__ == '__main__': import doctest doctest.testmod(exclude_empty=True) |