def make_steering_vectors(geometry, stream, freqs, xSlowness, ySlowness = 0): if isinstance(geometry, list): geometry = np.array(geometry).transpose() ## define the frequency axis fs = stream[0].stats.sampling_rate deltaf = freqs[1] - freqs[0] nlow = 1 nf = len(freqs) nhigh = freqs.max() / deltaf ## code adapted from get_timeshift mx = np.outer(geometry[:, 0], xSlowness) my = np.outer(geometry[:, 1], ySlowness) timeShiftTable = np.require(mx[:, :, np.newaxis].repeat(len(ySlowness), axis=2) + my[:, np.newaxis, :].repeat(len(xSlowness), axis=1), dtype=np.float32) ## code adapted from array_processing steeringVectors = np.empty((nf, len(xSlowness), len(ySlowness), len(stream)), dtype=np.complex128) clibsignal.calcSteer(len(stream), len(xSlowness), len(ySlowness), nf, nlow, deltaf, timeShiftTable, steeringVectors) return steeringVectors
def 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, store=None): """ Method for Seismic-Array-Beamforming/FK-Analysis/Capon :param stream: Stream object, the trace.stats dict like class must contain an :class:`~obspy.core.util.attribdict.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: :class:`~obspy.core.utcdatetime.UTCDateTime` :param stime: Start time of interest :type etime: :class:`~obspy.core.utcdatetime.UTCDateTime` :param etime: End time 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: str :param timestamp: valid values: 'julsec' and 'mlabday'; 'julsec' returns the timestamp in seconds 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. matplotlib's num2date) :type method: int :param method: the method to use 0 == bf, 1 == capon :type store: function :param store: A custom function which gets called on each iteration. It is called with the relative power map and the time offset as first and second arguments and the iteration number as third argument. Useful for storing or plotting the map for each iteration. For this purpose the dump function of this module can be used. :return: :class:`numpy.ndarray` of timestamp, relative relpow, absolute relpow, backazimuth, slowness """ res = [] eotr = True # check that sampling rates do not vary fs = stream[0].stats.sampling_rate if len(stream) != len(stream.select(sampling_rate=fs)): msg = 'in sonic sampling rates of traces in stream are not equal' raise ValueError(msg) grdpts_x = int(((slm_x - sll_x) / sl_s + 0.5) + 1) grdpts_y = int(((slm_y - sll_y) / sl_s + 0.5) + 1) geometry = get_geometry(stream, coordsys=coordsys, verbose=verbose) if verbose: print("geometry:") print(geometry) print("stream contains following traces:") print(stream) print("stime = " + str(stime) + ", etime = " + str(etime)) time_shift_table = get_timeshift(geometry, sll_x, sll_y, sl_s, grdpts_x, grdpts_y) # offset of arrays spoint, _epoint = get_spoint(stream, stime, etime) # # loop with a sliding window over the dat trace array and apply bbfk # nstat = len(stream) fs = stream[0].stats.sampling_rate nsamp = int(win_len * fs) nstep = int(nsamp * win_frac) # generate plan for rfftr nfft = next_pow_2(nsamp) deltaf = fs / float(nfft) nlow = int(frqlow / float(deltaf) + 0.5) nhigh = int(frqhigh / float(deltaf) + 0.5) nlow = max(1, nlow) # avoid using the offset nhigh = min(nfft // 2 - 1, nhigh) # avoid using nyquist nf = nhigh - nlow + 1 # include upper and lower frequency # to speed up the routine a bit we estimate all steering vectors in advance steer = np.empty((nf, grdpts_x, grdpts_y, nstat), dtype=np.complex128) clibsignal.calcSteer(nstat, grdpts_x, grdpts_y, nf, nlow, deltaf, time_shift_table, steer) _r = np.empty((nf, nstat, nstat), dtype=np.complex128) ft = np.empty((nstat, nf), dtype=np.complex128) newstart = stime # 0.22 matches 0.2 of historical C bbfk.c tap = cosine_taper(nsamp, p=0.22) offset = 0 relpow_map = np.empty((grdpts_x, grdpts_y), dtype=np.float64) abspow_map = np.empty((grdpts_x, grdpts_y), dtype=np.float64) while eotr: try: for i, tr in enumerate(stream): dat = tr.data[spoint[i] + offset: spoint[i] + offset + nsamp] dat = (dat - dat.mean()) * tap ft[i, :] = np.fft.rfft(dat, nfft)[nlow:nlow + nf] except IndexError: break ft = np.ascontiguousarray(ft, np.complex128) relpow_map.fill(0.) abspow_map.fill(0.) # computing the covariances of the signal at different receivers dpow = 0. for i in range(nstat): for j in range(i, nstat): _r[:, i, j] = ft[i, :] * ft[j, :].conj() if method == 1: _r[:, i, j] /= np.abs(_r[:, i, j].sum()) if i != j: _r[:, j, i] = _r[:, i, j].conjugate() else: dpow += np.abs(_r[:, i, j].sum()) dpow *= nstat if method == 1: # P(f) = 1/(e.H R(f)^-1 e) for n in range(nf): _r[n, :, :] = np.linalg.pinv(_r[n, :, :], rcond=1e-6) errcode = clibsignal.generalizedBeamformer( relpow_map, abspow_map, steer, _r, nstat, prewhiten, grdpts_x, grdpts_y, nf, dpow, method) if errcode != 0: msg = 'generalizedBeamforming exited with error %d' raise Exception(msg % errcode) ix, iy = np.unravel_index(relpow_map.argmax(), relpow_map.shape) relpow, abspow = relpow_map[ix, iy], abspow_map[ix, iy] if store is not None: store(relpow_map, abspow_map, offset) # here we compute baz, slow slow_x = sll_x + ix * sl_s slow_y = sll_y + iy * sl_s slow = np.sqrt(slow_x ** 2 + slow_y ** 2) if slow < 1e-8: slow = 1e-8 azimut = 180 * math.atan2(slow_x, slow_y) / math.pi baz = azimut % -360 + 180 if relpow > semb_thres and 1. / slow > vel_thres: res.append(np.array([newstart.timestamp, relpow, abspow, baz, slow])) if verbose: print(newstart, (newstart + (nsamp / fs)), res[-1][1:]) if (newstart + (nsamp + nstep) / fs) > etime: eotr = False offset += nstep newstart += nstep / fs res = np.array(res) if timestamp == 'julsec': pass elif timestamp == 'mlabday': # 719163 == days between 1970 and 0001 + 1 res[:, 0] = res[:, 0] / (24. * 3600) + 719163 else: msg = "Option timestamp must be one of 'julsec', or 'mlabday'" raise ValueError(msg) return np.array(res)
def 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, store=None): """ Method for Seismic-Array-Beamforming/FK-Analysis/Capon :param stream: Stream object, the trace.stats dict like class must contain an :class:`~obspy.core.util.attribdict.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: :class:`~obspy.core.utcdatetime.UTCDateTime` :param stime: Start time of interest :type etime: :class:`~obspy.core.utcdatetime.UTCDateTime` :param etime: End time 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: str :param timestamp: valid values: 'julsec' and 'mlabday'; 'julsec' returns the timestamp in seconds 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. matplotlib's num2date) :type method: int :param method: the method to use 0 == bf, 1 == capon :type store: function :param store: A custom function which gets called on each iteration. It is called with the relative power map and the time offset as first and second arguments and the iteration number as third argument. Useful for storing or plotting the map for each iteration. For this purpose the dump function of this module can be used. :return: :class:`numpy.ndarray` of timestamp, relative relpow, absolute relpow, backazimuth, slowness """ res = [] eotr = True # check that sampling rates do not vary fs = stream[0].stats.sampling_rate if len(stream) != len(stream.select(sampling_rate=fs)): msg = 'in sonic sampling rates of traces in stream are not equal' raise ValueError(msg) grdpts_x = int(((slm_x - sll_x) / sl_s + 0.5) + 1) grdpts_y = int(((slm_y - sll_y) / sl_s + 0.5) + 1) geometry = get_geometry(stream, coordsys=coordsys, verbose=verbose) if verbose: print("geometry:") print(geometry) print("stream contains following traces:") print(stream) print("stime = " + str(stime) + ", etime = " + str(etime)) time_shift_table = get_timeshift(geometry, sll_x, sll_y, sl_s, grdpts_x, grdpts_y) # offset of arrays spoint, _epoint = get_spoint(stream, stime, etime) # # loop with a sliding window over the dat trace array and apply bbfk # nstat = len(stream) fs = stream[0].stats.sampling_rate nsamp = int(win_len * fs) nstep = int(nsamp * win_frac) # generate plan for rfftr nfft = nextpow2(nsamp) deltaf = fs / float(nfft) nlow = int(frqlow / float(deltaf) + 0.5) nhigh = int(frqhigh / float(deltaf) + 0.5) nlow = max(1, nlow) # avoid using the offset nhigh = min(nfft // 2 - 1, nhigh) # avoid using nyquist nf = nhigh - nlow + 1 # include upper and lower frequency # to speed up the routine a bit we estimate all steering vectors in advance steer = np.empty((nf, grdpts_x, grdpts_y, nstat), dtype=np.complex128) clibsignal.calcSteer(nstat, grdpts_x, grdpts_y, nf, nlow, deltaf, time_shift_table, steer) R = np.empty((nf, nstat, nstat), dtype=np.complex128) ft = np.empty((nstat, nf), dtype=np.complex128) newstart = stime tap = cosTaper(nsamp, p=0.22) # 0.22 matches 0.2 of historical C bbfk.c offset = 0 relpow_map = np.empty((grdpts_x, grdpts_y), dtype=np.float64) abspow_map = np.empty((grdpts_x, grdpts_y), dtype=np.float64) while eotr: try: for i, tr in enumerate(stream): dat = tr.data[spoint[i] + offset: spoint[i] + offset + nsamp] dat = (dat - dat.mean()) * tap ft[i, :] = np.fft.rfft(dat, nfft)[nlow:nlow + nf] except IndexError: break ft = np.ascontiguousarray(ft, np.complex128) relpow_map.fill(0.) abspow_map.fill(0.) # computing the covariances of the signal at different receivers dpow = 0. for i in range(nstat): for j in range(i, nstat): R[:, i, j] = ft[i, :] * ft[j, :].conj() if method == 1: R[:, i, j] /= np.abs(R[:, i, j].sum()) if i != j: R[:, j, i] = R[:, i, j].conjugate() else: dpow += np.abs(R[:, i, j].sum()) dpow *= nstat if method == 1: # P(f) = 1/(e.H R(f)^-1 e) for n in range(nf): R[n, :, :] = np.linalg.pinv(R[n, :, :], rcond=1e-6) errcode = clibsignal.generalizedBeamformer( relpow_map, abspow_map, steer, R, nstat, prewhiten, grdpts_x, grdpts_y, nf, dpow, method) if errcode != 0: msg = 'generalizedBeamforming exited with error %d' raise Exception(msg % errcode) ix, iy = np.unravel_index(relpow_map.argmax(), relpow_map.shape) relpow, abspow = relpow_map[ix, iy], abspow_map[ix, iy] if store is not None: store(relpow_map, abspow_map, offset) # here we compute baz, slow slow_x = sll_x + ix * sl_s slow_y = sll_y + iy * sl_s slow = np.sqrt(slow_x ** 2 + slow_y ** 2) if slow < 1e-8: slow = 1e-8 azimut = 180 * math.atan2(slow_x, slow_y) / math.pi baz = azimut % -360 + 180 if relpow > semb_thres and 1. / slow > vel_thres: res.append(np.array([newstart.timestamp, relpow, abspow, baz, slow])) if verbose: print(newstart, (newstart + (nsamp / fs)), res[-1][1:]) if (newstart + (nsamp + nstep) / fs) > etime: eotr = False offset += nstep newstart += nstep / fs res = np.array(res) if timestamp == 'julsec': pass elif timestamp == 'mlabday': # 719162 == hours between 1970 and 0001 res[:, 0] = res[:, 0] / (24. * 3600) + 719162 else: msg = "Option timestamp must be one of 'julsec', or 'mlabday'" raise ValueError(msg) return np.array(res)