Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
068001a
utils.py: Show waveform download exception
Jul 22, 2021
21cc214
parse_local_data: abbreviate search for local data code, catch missin…
Aug 6, 2021
b54c6d5
More logging funcitonality
Aug 6, 2021
3290111
rfdata.py: Add option to write out deconvolution wavelets
Oct 4, 2021
d27059e
rfdata.py: add _wavelet method to manipulate de-convolution wavelet
Oct 19, 2021
4898a6c
rfdata.py: _Pwavelet function: change envelope to minimum-length-enve…
Oct 25, 2021
31e83ea
Revert "More logging funcitonality"
Oct 25, 2021
353af06
Merge branch 'future' of https://github.com/paudetseis/RfPy into mybr…
Oct 25, 2021
ad8f5b4
binning: updated documentation
Oct 26, 2021
da61388
fixed setup for scripts package
paudetseis Oct 15, 2021
448e510
binning: updated documentation
Oct 26, 2021
ef558b0
binning: updated documentation
Oct 26, 2021
a8007e8
binning: corrected documentation
Oct 26, 2021
8129104
Meta: initialize floats with np.nan, instead of None
Nov 1, 2021
c90d310
binning: Added option 'include_empty' to return streams that have no …
Nov 1, 2021
052f96e
utils: Print download exception only if verbose
Nov 5, 2021
171fb01
rfdata, utils: added option to remove instrument response from donwlo…
Nov 5, 2021
86e5e84
Option to use locally saved response information
Nov 10, 2021
8c442de
download: Only prefilter traces when downsampling. Add some verbosity.
Nov 15, 2021
5257cad
First big commit for simdec
paudetseis Nov 17, 2021
f354247
re-inserted skipped commits
paudetseis Nov 17, 2021
699cbbb
next big commit
paudetseis Nov 18, 2021
5cbe0d8
third large commit
paudetseis Nov 18, 2021
a6a0d87
a bit of cleanup
paudetseis Nov 18, 2021
015b265
fixed type of data trace during binning
paudetseis Nov 22, 2021
bd0a09d
terminal strings
paudetseis Nov 22, 2021
85e497a
RFdata: handle missing align argument and to_stream store='data'
Dec 10, 2021
3e3ccbe
rfdata: Allow RFData to be initialized without station
Jan 6, 2022
65b175e
binning: only compute hilbert transform when pws=True
Feb 24, 2022
248d569
rfdata: Fixed broken computation of waterlevel
Feb 25, 2022
6124fd6
binning: new stack attribute: index_list
Mar 7, 2022
c1e0637
rfdata: deconvolution method 'water2'
Mar 7, 2022
7858460
utils: Catch None in station list
Mar 8, 2022
d951ce7
binning: doc cleanup, added function baz_slow_bin_indices()
Mar 8, 2022
e3322e9
binning:
Mar 10, 2022
ebaac7e
Automatic optimal_wlevel() now part of utils
Mar 10, 2022
887481a
Removed optimal_wlevel() method from RFData. Now part of utils
Mar 10, 2022
c6d56c4
rfpy_calc_simdec:
Mar 10, 2022
da8239b
add example data in package
paudetseis Mar 14, 2022
61a3621
added Wasja
paudetseis Mar 16, 2022
2574765
more explicit bin condition
paudetseis Apr 28, 2022
4d42d08
Added reference to PVH rotation
Jun 9, 2022
48dd5f2
deconvolve: alias 'wlevel' for 'water', catch unknown method
Jun 9, 2022
a66e97f
minor rename arg function
paudetseis Jul 8, 2022
e81fa72
change banner
paudetseis Jul 8, 2022
1d84cfd
Merge branch 'master' of https://github.com/paudetseis/RfPy
paudetseis Jul 8, 2022
50a6203
resolving conflict with master
paudetseis Jul 8, 2022
f290b7c
Search local inventory files by network, if station code not in filename
Jan 18, 2023
44e2192
Additional search pattern for local data
Jan 23, 2023
d151312
'==' instad of 'is' comparission
Jan 23, 2023
e576aba
Catch incomplete stream download
Sep 28, 2023
86875d7
Merge branch 'future' of https://github.com/paudetseis/RfPy into future
Sep 28, 2023
eb45ad9
utils: Only use remove_response kw when removing response.
wasjabloch Oct 9, 2023
ca11c4d
utils: Added SeisComp data structure for local dirs
wasjabloch Oct 12, 2023
bacb652
utils: Handle non-SAC files when parsing local data
wasjabloch Oct 13, 2023
7b7eb8d
Fixed unnecessary imports and ill-named variable
wasjabloch Oct 17, 2023
37b8280
binning: User defined baz and slow range
wasjabloch Oct 17, 2023
8ed3cfc
calc_spectra: add length parameter
Jan 31, 2024
7c68067
gauss_filt part of deconvolve, not calc_spec
Jan 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RfPy is a software to calculate single event-station receiver functions from the
Installation, Usage, API documentation and scripts are described at
https://paudetseis.github.io/RfPy/.

Authors: [`Pascal Audet`](https://www.uogeophysics.com/authors/admin/) (Developer and Maintainer) and [`Jeremy Gosselin`](https://www.uogeophysics.com/authors/gosselin/) (Contributor)
Authors: [Pascal Audet](https://www.uogeophysics.com/authors/admin/) and [Wasja Bloch](https://www.eoas.ubc.ca/people/wasjabloch)
<!-- #### Citing

If you use `SplitPy` in your work, please cite the
Expand Down
276 changes: 218 additions & 58 deletions rfpy/binning.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
from scipy.signal import hilbert


def bin(stream1, stream2=None, typ='baz', nbin=36+1, pws=False):
def bin(stream1, stream2=None, typ='baz', nbin=36+1, pws=False,
include_empty=False):
"""
Function to stack receiver functions into (baz or slow) bins
This can be done using a linear stack (i.e., simple
Expand All @@ -45,19 +46,29 @@ def bin(stream1, stream2=None, typ='baz', nbin=36+1, pws=False):
a single trace.
stream2 : :class:`~obspy.core.Stream`
Optionally stack a second stream in the same operation.
dbaz : int
Number of bazk-azimuth samples in bins
dslow : int
Number of slowness samples in bins
typ: str
Attribute to bin
'baz': backazimuth (degree)
'slow': Horizontal slowness (s/km)
'dist': epicentral distance (degree)
nbin : int
Number of equally sized bins within data range
pws : bool
Whether or not to perform phase-weighted stacking
include_empty : bool
Return empty bins as null traces (default omits them)

Returns
-------
stack : :class:`~obspy.core.Stream`
Stream containing one or two stacked traces,
depending on the number of input streams

Note
----
Sets the following attributes of the stack:
nbin: Number of bins
index_list: Indices of constituent traces in source stream
"""

if not typ in ['baz', 'slow', 'dist']:
Expand Down Expand Up @@ -95,6 +106,7 @@ def bin(stream1, stream2=None, typ='baz', nbin=36+1, pws=False):
nb = 0
array = np.zeros(len(stream[0].data))
weight = np.zeros(len(stream[0].data), dtype=complex)
index_list = []

# Loop through stat
for j, tr in enumerate(stream):
Expand All @@ -104,33 +116,35 @@ def bin(stream1, stream2=None, typ='baz', nbin=36+1, pws=False):

nb += 1
array += tr.data
hilb = hilbert(tr.data)
phase = np.arctan2(hilb.imag, hilb.real)
weight += np.exp(1j*phase)
index_list.append(j)

if pws:
hilb = hilbert(tr.data)
phase = np.arctan2(hilb.imag, hilb.real)
weight += np.exp(1j*phase)

continue

if nb > 0:
if nb > 0 or include_empty:

# Average and update stats
array /= nb
weight = np.real(abs(weight/np.float(nb)))

trace = Trace(header=stream[0].stats)
trace.stats.nbin = nb
trace.stats.index_list = index_list

if typ == 'baz':
trace.stats.baz = bins[i]
trace.stats.slow = None
trace.stats.nbin = nb
elif typ == 'slow':
trace.stats.slow = bins[i]
trace.stats.baz = None
trace.stats.nbin = nb
elif typ == 'dist':
trace.stats.dist = bins[i]
trace.stats.slow = None
trace.stats.baz = None
trace.stats.nbin = nb
if not pws:
weight = np.ones(len(stream[0].data))
trace.data = weight*array
Expand All @@ -144,7 +158,8 @@ def bin(stream1, stream2=None, typ='baz', nbin=36+1, pws=False):
return final_stream


def bin_baz_slow(stream1, stream2=None, nbaz=36+1, nslow=20+1, pws=False):
def bin_baz_slow(stream1, stream2=None, nbaz=36+1, nslow=20+1, baz_range=(0, 360),
slow_range=(0.04, 0.08), pws=False, include_empty=False):
"""
Function to stack receiver functions into back-azimuth and slowness bins.
This can be done using a linear stack (i.e., simple
Expand All @@ -157,24 +172,35 @@ def bin_baz_slow(stream1, stream2=None, nbaz=36+1, nslow=20+1, pws=False):
a single trace.
stream2 : :class:`~obspy.core.Stream`
Optionally stack a second stream in the same operation.
dbaz : int
Number of bazk-azimuth samples in bins
dslow : int
nbaz : int
Number of back-azimuth samples in bins
nslow : int
Number of slowness samples in bins
baz_range : 2-tuple
Minimum and maximum of the back-azimuth bins
slow_range : 2-tuple
Minimum and maximum of the slowness bins
pws : bool
Whether or not to perform phase-weighted stacking
include_empty : bool
Return empty bins as null traces (default omits them)

Returns
-------
stack : :class:`~obspy.core.Stream`
Stream containing one or two stacked traces,
depending on the number of input streams

Note
----
Sets the following attributes of the stack:
nbin: Number of bins
index_list: Indices of constituent traces in source stream
"""

# Define back-azimuth and slowness bins
baz_bins = np.linspace(0, 360, nbaz)
slow_bins = np.linspace(0.04, 0.08, nslow)
baz_bins = np.linspace(*baz_range, nbaz)
slow_bins = np.linspace(*slow_range, nslow)

# Extract baz and slowness
baz = [stream1[i].stats.baz for i in range(len(stream1))]
Expand All @@ -185,59 +211,192 @@ def bin_baz_slow(stream1, stream2=None, nbaz=36+1, nslow=20+1, pws=False):
islow = np.digitize(slow, slow_bins)

final_stream = []
if stream2:
stream1 = [stream1, stream2]
else:
stream1 = [stream1]

for stream in stream1:
# try:
# Define empty streams
binned_stream = Stream()

# Loop through baz_bins
for i in range(nbaz):
for j in range(nslow):

nbin = 0
index_list = []
array = np.zeros(len(stream[0].data), dtype=type(stream[0].data[0]))
weight = np.zeros(len(stream[0].data), dtype=complex)

for stream in [stream1, stream2]:
try:
# Define empty streams
binned_stream = Stream()

# Loop through baz_bins
for i in range(nbaz):
for j in range(nslow):

nbin = 0
array = np.zeros(len(stream[0].data))
weight = np.zeros(len(stream[0].data), dtype=complex)
# Loop all traces
for k, tr in enumerate(stream):

# Loop all traces
for k, tr in enumerate(stream):
# If index of baz_bins is equal to ibaz
if i == ibaz[k] and j == islow[k]:

# If index of baz_bins is equal to ibaz
if i == ibaz[k] and j == islow[k]:
nbin += 1
array += tr.data
index_list.append(k)

nbin += 1
array += tr.data
hilb = hilbert(tr.data)
if pws:
hilb = hilbert(np.real(tr.data))
phase = np.arctan2(hilb.imag, hilb.real)
weight += np.exp(1j*phase)
continue

continue

if nbin > 0:
if nbin > 0 or include_empty:

# Average and update stats
array /= nbin
weight = np.real(abs(weight/nbin))
# Average and update stats
array /= nbin
weight = np.real(abs(weight/nbin))

trace = Trace(header=stream[0].stats)
trace.stats.baz = baz_bins[i]
trace.stats.slow = slow_bins[j]
trace.stats.nbin = nbin
trace = Trace(header=stream[0].stats)
trace.stats.baz = baz_bins[i]
trace.stats.slow = slow_bins[j]
trace.stats.nbin = nbin
trace.stats.index_list = index_list

if not pws:
weight = np.ones(len(stream[0].data))
trace.data = weight*array
binned_stream.append(trace)
if not pws:
weight = np.ones(len(stream[0].data))
trace.data = weight*array
binned_stream.append(trace)

final_stream.append(binned_stream)
final_stream.append(binned_stream)

except:
continue
# except:
# final_stream = [Stream()]
# continue

return final_stream

def bin_all(stream1, stream2=None, pws=False):

def stack(rfdatas, which='rf', pws=False,
attributes={'bin': None, 'slow': None, 'index_list': None}):
"""
Function to stack receiver functions stored in a RFData list. This can
be done using a linear stack (i.e., simple mean), or using phase-weighted
stacking.

Parameters
----------
rfdatas : list of :class:`rfpy.rfdata.RFdata`
List of RFData objects to be stacked
which : str
'rf' stack receiver functions
'specs' stack spectra
pws : bool
Whether or not to perform phase-weighted stacking (which=rf only)
attributes : dict
Attributes passed to the traces of stack

Returns
-------
stack : :class:`~obspy.core.Stream`
Stream containing stacked traces
"""

nbin = len(rfdatas)

template = rfdatas[0].__dict__[which]
typ = float
if which == 'specs':
typ = complex

array = np.zeros((len(template), len(template[0].data)), dtype=typ)
weight = np.zeros((len(template), len(template[0].data)), dtype=complex)

for rfdata in rfdatas:
stream = rfdata.__dict__[which]

for n, tr in enumerate(stream):
array[n, :] += tr.data

if pws:
hilb = hilbert(np.real(tr.data))
phase = np.arctan2(hilb.imag, hilb.real)
weight[n, :] += np.exp(1j*phase)

if pws:
weight = np.real(abs(weight/nbin))
array *= weight
array /= nbin

stacks = Stream()
for n in range(len(template)):
stack = Trace(header=template[n].stats)
stack.data = array[n, :]
stack.stats.nbin = nbin
for att in attributes:
val = attributes[att]
stack.stats.__dict__[att] = val
stacks.append(stack)

return stacks


def baz_slow_bin_indices(
rfs, nbaz=36+1, nslow=20+1, baz_range=(0, 360), slow_range=(0.04, 0.08), include_empty=False):
"""
Sort traces of streams into backazimuth (nbaz) and slowness (nslow) bins.

Parameters
----------
rfs : list of :class:`~rfpy.RFData`
List of receiver functions to be sorted in bins
bazs : int
Number of back-azimuth samples in bins
slows : int
Number of slowness samples in bins
baz_range : 2-tuple
Minimum and maximum of the back-azimuth bins
slow_range : 2-tuple
Minimum and maximum of the slowness bins
include_empty : bool
Include empty bins (default omits them)

Returns
-------
index_lists : list of lists
Indices that sorts traces of stream into bins defined by nbaz and nslow
bazslow_list : list of 2*tuple
Backazimuth and slowness values of each element of index list
"""

# Define back-azimuth and slowness bins
baz_bins = np.linspace(*baz_range, nbaz)
slow_bins = np.linspace(*slow_range, nslow)

# Extract baz and slowness
baz = [rf.meta.baz for rf in rfs]
slow = [rf.meta.slow for rf in rfs]

# Digitize baz and slowness
ibaz = np.digitize(baz, baz_bins)
islow = np.digitize(slow, slow_bins)

index_lists = []
bazslow_list = []

for i in range(nbaz):
for j in range(nslow):
index_list = []

for k in range(len(rfs)):
if i == ibaz[k] and j == islow[k]:
index_list.append(k)

if len(index_list) > 0 or include_empty:
index_lists.append(index_list)
bazslow_list.append((baz_bins[i], slow_bins[j]))

return index_lists, bazslow_list


def bin_all(stream1, stream2=None, pws=False):
"""
Function to bin all streams into a single trace.
This can be done using a linear stack (i.e., simple
mean), or using phase-weighted stacking.
Expand Down Expand Up @@ -274,9 +433,10 @@ def bin_all(stream1, stream2=None, pws=False):
# Get phase weights
for tr in stream:
array += tr.data
hilb = hilbert(tr.data)
phase = np.arctan2(hilb.imag, hilb.real)
pweight += np.exp(1j*phase)
if pws:
hilb = hilbert(tr.data)
phase = np.arctan2(hilb.imag, hilb.real)
pweight += np.exp(1j*phase)

# Normalize
array = array/len(stream)
Expand Down
Loading