Skip to content

Commit 43976d6

Browse files
committed
Option to Process Only R1
* Surface-wave polarization analyses can now be carried out on the R1 arm alone, for stations with noisy data
1 parent 161f15d commit 43976d6

File tree

3 files changed

+100
-35
lines changed

3 files changed

+100
-35
lines changed

seismic/bulk_station_orientations.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,9 @@ def main(src_h5_event_file, network, output_basename, station_list, dump_swp_dat
257257
ax2.set_title('Surface-wave Polarization')
258258

259259
results_rf = rf_station_orientations(ned_rf, ax=ax1)
260-
results_swp = swp_station_orientations(ned_swp, grv_dict, ax=ax2,
261-
data_dump_file_name=curr_swp_dump_file)
260+
results_swp = swp_station_orientations(ned_swp, grv_dict,
261+
data_dump_file_name=curr_swp_dump_file,
262+
ax=ax2)
262263

263264
plt.tight_layout()
264265
pdf.savefig(dpi=300, orientation='portrait')

seismic/extract_event_traces.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,13 +430,13 @@ def extract_data(recording_timespan_getter, waveform_getter,
430430
@click.option('--s-data', is_flag=True, default=False, show_default=True,
431431
help='Extracts waveform data around S-arrival')
432432
@click.option('--sw-data', is_flag=True, default=False, show_default=True,
433-
help='Extracts waveform data around surface-wave arrival')
433+
help='Extracts waveform data for surface waves, defined by window around origin time')
434434
@click.option('--p-magnitude-range', type=(float, float), default=(5.5, 10.0), show_default=True,
435-
help='Range of seismic event magnitudes to sample from the event catalog for P arrivals.')
435+
help='Range of seismic event magnitudes to sample from the event catalog for P arrivals')
436436
@click.option('--s-magnitude-range', type=(float, float), default=(5.5, 10.0), show_default=True,
437-
help='Range of seismic event magnitudes to sample from the event catalog for S arrivals.')
437+
help='Range of seismic event magnitudes to sample from the event catalog for S arrivals')
438438
@click.option('--sw-magnitude-range', type=(float, float), default=(6.0, 10.0), show_default=True,
439-
help='Range of seismic event magnitudes to sample from the event catalog for surface waves.')
439+
help='Range of seismic event magnitudes to sample from the event catalog for surface waves')
440440
@click.option('--p-data-window', type=(int, int), default=(-70, 150), show_default=True,
441441
help='Time window for waveform data around P-arrivals to extract. Has no effect without '
442442
'--p-data')

seismic/swp_station_orientations.py

Lines changed: 93 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,20 @@
4545
logging.basicConfig()
4646

4747
GRV_FN = os.path.join(os.path.dirname(__file__), 'data/grv.h5')
48+
DATA_LENGTH_SECONDS = 60 * 60 * 4
4849

49-
def checklen(st, hrs):
50+
def checklen(st):
5051
# checks to see if there is enough downloaded data to run program
5152
L=len(st)
5253

5354
if(L != 3): return True
5455

5556
for i in np.arange((L)):
56-
if (UTCDateTime(st[i].stats.endtime)-UTCDateTime(st[i].stats.starttime))+100 < hrs:
57+
if (UTCDateTime(st[i].stats.endtime)-UTCDateTime(st[i].stats.starttime))+100 < DATA_LENGTH_SECONDS:
5758
return True
58-
if np.var(st[0].data)<1 or np.var(st[1].data)<1 or np.var(st[2].data)<1:
59+
if np.var(st[0].data) < 1 or np.var(st[1].data) < 1 or np.var(st[2].data) < 1:
5960
return True
61+
6062
return False
6163
# end func
6264

@@ -295,7 +297,6 @@ def org(T,nameconv):
295297

296298
def compute_phis(ned, grv_dict, logger=None):
297299
nameconv = 1
298-
hrs = 60 * 60 * 4
299300

300301
R1phi = None
301302
R1cc = None
@@ -332,7 +333,7 @@ def compute_phis(ned, grv_dict, logger=None):
332333
for j, (evid, st) in enumerate(evts.items()):
333334
st = org(st.copy(), nameconv)
334335

335-
if checklen(st, hrs):
336+
if checklen(st):
336337
discarded += 1
337338
continue
338339
# end if
@@ -563,7 +564,7 @@ def fcalc1(phi,cc,lim,R1cc,R2cc):
563564
# end func
564565

565566

566-
def summary_calculations(R1cc, R1phi, R2cc, R2phi, logger=None):
567+
def summary_calculations(R1cc, R1phi, R2cc, R2phi, exclude_r2=False, logger=None):
567568
"""
568569
Final Orientation Calculation File
569570
A. Doran and G. Laske
@@ -648,11 +649,22 @@ def flatten(X):
648649

649650
for i in A:
650651
# create one massive list with necessary angles and cc values
651-
phis = np.concatenate((flatten(R1phi[startL:i, :]), flatten(R2phi[startL:i, :])))
652-
ccs = np.concatenate((flatten(R1cc[startL:i, :]), flatten(R2cc[startL:i, :])))
652+
if(not exclude_r2):
653+
phis = np.concatenate((flatten(R1phi[startL:i, :]), flatten(R2phi[startL:i, :])))
654+
ccs = np.concatenate((flatten(R1cc[startL:i, :]), flatten(R2cc[startL:i, :])))
655+
else:
656+
phis = flatten(R1phi[startL:i, :])
657+
ccs = flatten(R1cc[startL:i, :])
658+
# end if
653659

654660
# Doran-Laske calculation
655-
val, err, n, ph = fcalc1(phis, ccs, LIM, R1cc, R2cc)
661+
val = err = n = ph = None
662+
if(not exclude_r2):
663+
val, err, n, ph = fcalc1(phis, ccs, LIM, R1cc, R2cc)
664+
else:
665+
val, err, n, ph = fcalc1(phis, ccs, LIM, R1cc, R1cc)
666+
# end if
667+
656668
finval = np.append(finval, val)
657669
finerr = np.append(finerr, err)
658670
phases = np.append(phases, ph)
@@ -665,40 +677,87 @@ def flatten(X):
665677
return finval[-1], finerr[-1], phases[-1], max(LN)
666678
# end func
667679

680+
def should_exclude_r2(ned):
681+
lengths = []
682+
for i, (sta, evts) in enumerate(ned.by_station()): # ned contains 1 station
683+
for j, (evid, st) in enumerate(evts.items()):
684+
for tr in st:
685+
lengths.append(tr.stats.endtime - tr.stats.starttime)
686+
# end for
687+
# end for
688+
# end for
689+
lengths = np.array(lengths)
690+
if(np.median(lengths) < DATA_LENGTH_SECONDS): # exclude r2
691+
# expand arrays to DATA_LENGTH_SECONDS for qualifying traces
692+
for i, (sta, evts) in enumerate(ned.by_station()): # ned contains 1 station
693+
for j, (evid, st) in enumerate(evts.items()):
694+
for tr in st:
695+
length = int(tr.stats.endtime - tr.stats.starttime)
696+
if (((length + 100) >= DATA_LENGTH_SECONDS / 2) and
697+
(length < DATA_LENGTH_SECONDS)):
698+
expanded = np.zeros(int(DATA_LENGTH_SECONDS * tr.stats.sampling_rate))
699+
expanded[:tr.data.shape[0]] = tr.data
700+
tr.data = expanded
701+
# end if
702+
# end for
703+
# end for
704+
# end for
705+
return True
706+
else:
707+
return False
708+
# end if
709+
# end func
710+
668711
def analyze_station_orientations(ned, grv_dict, save_plots_path=None, data_dump_file_name=None, ax=None):
712+
assert isinstance(ned, NetworkEventDataset), 'Pass NetworkEventDataset as input'
713+
if len(ned) == 0:
714+
return {}
715+
# end if
716+
exclude_r2 = should_exclude_r2(ned)
717+
669718
def dump_swp_data(r1phi, r1cc, r2phi, r2cc, e_array):
670719
freqs = [40, 35, 30, 25, 20, 15, 10]
671720
col_names = list(e_array.dtype.names) + \
672721
['R1Phi_{}'.format(item) for item in freqs] + \
673-
['R1CC_{}'.format(item) for item in freqs] + \
674-
['R2Phi_{}'.format(item) for item in freqs] + \
675-
['R2CC_{}'.format(item) for item in freqs]
722+
['R1CC_{}'.format(item) for item in freqs]
723+
if(not exclude_r2):
724+
col_names += ['R2Phi_{}'.format(item) for item in freqs] + \
725+
['R2CC_{}'.format(item) for item in freqs]
726+
# end if
727+
728+
fs = line = None
676729
with open(data_dump_file_name, 'w') as fh:
677730
fh.write(', '.join(col_names) + '\n')
678-
fs = ('%f, '*32)[:-2] # 32 floats per line (4 event-related and 4x7 polarization values)
731+
732+
if(not exclude_r2):
733+
fs = ('%f, '*32)[:-2] # 32 floats per line (4 event-related and 4x7 polarization values)
734+
else:
735+
fs = ('%f, '*18)[:-2] # 18 floats per line (4 event-related and 2x7 polarization values)
736+
# end if
679737

680738
for i in np.arange(len(e_array)):
681739
if(np.sum(r1phi[i, :]) == np.sum(r2phi[i, :])):
682740
# skip null entry
683741
continue
684742
# end if
685743

686-
line = fs % (*e_array[i], \
687-
*r1phi[i, :], \
688-
*r1cc[i, :], \
689-
*r2phi[i, :], \
690-
*r2cc[i, :])
744+
if(not exclude_r2):
745+
line = fs % (*e_array[i], \
746+
*r1phi[i, :], \
747+
*r1cc[i, :], \
748+
*r2phi[i, :], \
749+
*r2cc[i, :])
750+
else:
751+
line = fs % (*e_array[i], \
752+
*r1phi[i, :], \
753+
*r1cc[i, :])
754+
# end if
755+
691756
fh.write(line + '\n')
692757
# end for
693758
# end with
694759
# end func
695760

696-
assert isinstance(ned, NetworkEventDataset), 'Pass NetworkEventDataset as input'
697-
698-
if len(ned) == 0:
699-
return {}
700-
# end if
701-
702761
# Determine limiting date range per station
703762
results = defaultdict(dict)
704763
full_code = None
@@ -739,7 +798,8 @@ def dump_swp_data(r1phi, r1cc, r2phi, r2cc, e_array):
739798
str(e))
740799
# end try
741800
#end if
742-
corr, err, ndata, nevents_c = summary_calculations(r1cc, r1phi, r2cc, r2phi, logger)
801+
corr, err, ndata, nevents_c = summary_calculations(r1cc, r1phi, r2cc, r2phi,
802+
exclude_r2=exclude_r2, logger=logger)
743803

744804
corr *= -1 # converting to azimuth correction
745805
while (corr > 180): corr -= 360
@@ -759,8 +819,10 @@ def dump_swp_data(r1phi, r1cc, r2phi, r2cc, e_array):
759819
plt.plot([0, 1], [corr, corr], '-', linewidth=4, color=(0.8, 0.8, 0.8), zorder=5)
760820
sc = plt.scatter(r1cc, centerat(-r1phi, m=CEN), c=c, marker='o', cmap=cm.viridis, alpha=0.5, zorder=1,
761821
label='R1')
762-
plt.scatter(r2cc, centerat(-r2phi, m=CEN), c=c, marker='^', cmap=cm.viridis, alpha=0.5, zorder=1,
763-
label='R2')
822+
if(not exclude_r2):
823+
plt.scatter(r2cc, centerat(-r2phi, m=CEN), c=c, marker='^', cmap=cm.viridis, alpha=0.5, zorder=1,
824+
label='R2')
825+
# end if
764826
plt.text(0.5, corr, 'Mean: {0:.3f} deg'.format(corr), fontsize=14, zorder=10)
765827
cbar = plt.colorbar(sc)
766828
cbar.set_label('Frequency (mHz)')
@@ -788,8 +850,10 @@ def dump_swp_data(r1phi, r1cc, r2phi, r2cc, e_array):
788850
ax.plot([0, 1], [corr, corr], '-', linewidth=4, color=(0.8, 0.8, 0.8), zorder=5)
789851
sc = ax.scatter(r1cc, centerat(-r1phi, m=CEN), c=c, marker='o', cmap=cm.viridis, alpha=0.5, zorder=1,
790852
label='R1')
791-
ax.scatter(r2cc, centerat(-r2phi, m=CEN), c=c, marker='^', cmap=cm.viridis, alpha=0.5, zorder=1,
792-
label='R2')
853+
if(not exclude_r2):
854+
ax.scatter(r2cc, centerat(-r2phi, m=CEN), c=c, marker='^', cmap=cm.viridis, alpha=0.5, zorder=1,
855+
label='R2')
856+
# end if
793857
ax.text(0.5, corr, 'Mean: {0:.3f} deg'.format(corr), fontsize=14, zorder=10)
794858

795859
cax = fig.add_axes([0.1, 0.075, 0.01, 0.1])

0 commit comments

Comments
 (0)