Skip to content

Commit 57bf98c

Browse files
committed
add data download module
1 parent 28e7e51 commit 57bf98c

File tree

3 files changed

+392
-0
lines changed

3 files changed

+392
-0
lines changed

pytomo3d/source/source.py

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Source and Receiver classes of Instaseis.
5+
6+
:copyright:
7+
Lion Krischer (krischer@geophysik.uni-muenchen.de), 2014
8+
Martin van Driel (Martin@vanDriel.de), 2014
9+
Wenjie Lei (lei@princeton.edu), 2016
10+
:license:
11+
GNU Lesser General Public License, Version 3
12+
(http://www.gnu.org/copyleft/lgpl.html)
13+
"""
14+
from __future__ import (absolute_import, division, print_function,
15+
unicode_literals)
16+
import numpy as np
17+
import obspy
18+
import obspy.xseed
19+
import warnings
20+
21+
22+
class CMTSource(object):
23+
"""
24+
Class to handle a seismic moment tensor source including a source time
25+
function.
26+
"""
27+
def __init__(self, origin_time=obspy.UTCDateTime(0),
28+
pde_latitude=0.0, pde_longitude=0.0, mb=0.0, ms=0.0,
29+
pde_depth_in_m=None, region_tag=None, eventname=None,
30+
cmt_time=0.0, half_duration=0.0, latitude=0.0, longitude=0.0,
31+
depth_in_m=None, m_rr=0.0, m_tt=0.0, m_pp=0.0, m_rt=0.0,
32+
m_rp=0.0, m_tp=0.0):
33+
"""
34+
:param latitude: latitude of the source in degree
35+
:param longitude: longitude of the source in degree
36+
:param depth_in_m: source depth in m
37+
:param m_rr: moment tensor components in r, theta, phi in Nm
38+
:param m_tt: moment tensor components in r, theta, phi in Nm
39+
:param m_pp: moment tensor components in r, theta, phi in Nm
40+
:param m_rt: moment tensor components in r, theta, phi in Nm
41+
:param m_rp: moment tensor components in r, theta, phi in Nm
42+
:param m_tp: moment tensor components in r, theta, phi in Nm
43+
:param time_shift: correction of the origin time in seconds. only
44+
useful in the context of finite sources
45+
:param sliprate: normalized source time function (sliprate)
46+
:param dt: sampling of the source time function
47+
:param origin_time: The origin time of the source. This will be the
48+
time of the first sample in the final seismogram. Be careful to
49+
adjust it for any time shift or STF (de)convolution effects.
50+
51+
"""
52+
self.origin_time = origin_time
53+
self.pde_latitude = pde_latitude
54+
self.pde_longitude = pde_longitude
55+
self.pde_depth_in_m = pde_depth_in_m
56+
self.mb = mb
57+
self.ms = ms
58+
self.region_tag = region_tag
59+
self.eventname = eventname
60+
self.cmt_time = cmt_time
61+
self.half_duration = half_duration
62+
self.latitude = latitude
63+
self.longitude = longitude
64+
self.depth_in_m = depth_in_m
65+
self.m_rr = m_rr
66+
self.m_tt = m_tt
67+
self.m_pp = m_pp
68+
self.m_rt = m_rt
69+
self.m_rp = m_rp
70+
self.m_tp = m_tp
71+
72+
@classmethod
73+
def from_CMTSOLUTION_file(cls, filename):
74+
"""
75+
Initialize a source object from a CMTSOLUTION file.
76+
77+
:param filename: path to the CMTSOLUTION file
78+
"""
79+
80+
with open(filename, "rt") as f:
81+
line = f.readline()
82+
origin_time = line[4:].strip().split()[:6]
83+
values = list(map(int, origin_time[:-1])) + \
84+
[float(origin_time[-1])]
85+
try:
86+
origin_time = obspy.UTCDateTime(*values)
87+
except (TypeError, ValueError):
88+
warnings.warn("Could not determine origin time from line: %s"
89+
% line)
90+
origin_time = obspy.UTCDateTime(0)
91+
otherinfo = line[4:].strip().split()[6:]
92+
pde_lat = float(otherinfo[0])
93+
pde_lon = float(otherinfo[1])
94+
pde_depth_in_m = float(otherinfo[2]) * 1e3
95+
mb = float(otherinfo[3])
96+
ms = float(otherinfo[4])
97+
region_tag = ' '.join(otherinfo[5:])
98+
99+
eventname = f.readline().strip().split()[-1]
100+
time_shift = float(f.readline().strip().split()[-1])
101+
cmt_time = origin_time + time_shift
102+
half_duration = float(f.readline().strip().split()[-1])
103+
latitude = float(f.readline().strip().split()[-1])
104+
longitude = float(f.readline().strip().split()[-1])
105+
depth_in_m = float(f.readline().strip().split()[-1]) * 1e3
106+
107+
# unit: N/m
108+
m_rr = float(f.readline().strip().split()[-1]) # / 1e7
109+
m_tt = float(f.readline().strip().split()[-1]) # / 1e7
110+
m_pp = float(f.readline().strip().split()[-1]) # / 1e7
111+
m_rt = float(f.readline().strip().split()[-1]) # / 1e7
112+
m_rp = float(f.readline().strip().split()[-1]) # / 1e7
113+
m_tp = float(f.readline().strip().split()[-1]) # / 1e7
114+
115+
return cls(origin_time=origin_time,
116+
pde_latitude=pde_lat, pde_longitude=pde_lon, mb=mb, ms=ms,
117+
pde_depth_in_m=pde_depth_in_m, region_tag=region_tag,
118+
eventname=eventname, cmt_time=cmt_time,
119+
half_duration=half_duration, latitude=latitude,
120+
longitude=longitude, depth_in_m=depth_in_m,
121+
m_rr=m_rr, m_tt=m_tt, m_pp=m_pp, m_rt=m_rt,
122+
m_rp=m_rp, m_tp=m_tp)
123+
124+
@classmethod
125+
def from_quakeml_file(cls, filename):
126+
"""
127+
Initizliaze a source object from a quakeml file
128+
:param filename: path to a quakeml file
129+
"""
130+
from obspy import readEvents
131+
cat = readEvents(filename)
132+
event = cat[0]
133+
cmtsolution = event.preferred_origin()
134+
pdesolution = event.origins[0]
135+
136+
origin_time = pdesolution.time
137+
pde_lat = pdesolution.latitude
138+
pde_lon = pdesolution.longitude
139+
pde_depth_in_m = pdesolution.depth
140+
mb = 0.0
141+
ms = 0.0
142+
for mag in event.magnitudes:
143+
if mag.magnitude_type == "mb":
144+
mb = mag.mag
145+
elif mag.magnitude_type == "MS":
146+
ms = mag.mag
147+
region_tag = event.event_descriptions[0].text
148+
149+
for descrip in event.event_descriptions:
150+
if descrip.type == "earthquake name":
151+
eventname = descrip.text
152+
else:
153+
eventname = ""
154+
cmt_time = cmtsolution.time
155+
focal_mechanism = event.focal_mechanisms[0]
156+
half_duration = \
157+
focal_mechanism.moment_tensor.source_time_function.duration/2.0
158+
latitude = cmtsolution.latitude
159+
longitude = cmtsolution.longitude
160+
depth_in_m = cmtsolution.depth
161+
tensor = focal_mechanism.moment_tensor.tensor
162+
m_rr = tensor.m_rr * 1e7
163+
m_tt = tensor.m_tt * 1e7
164+
m_pp = tensor.m_pp * 1e7
165+
m_rt = tensor.m_rt * 1e7
166+
m_rp = tensor.m_rp * 1e7
167+
m_tp = tensor.m_tp * 1e7
168+
169+
return cls(origin_time=origin_time,
170+
pde_latitude=pde_lat, pde_longitude=pde_lon, mb=mb, ms=ms,
171+
pde_depth_in_m=pde_depth_in_m, region_tag=region_tag,
172+
eventname=eventname, cmt_time=cmt_time,
173+
half_duration=half_duration, latitude=latitude,
174+
longitude=longitude, depth_in_m=depth_in_m,
175+
m_rr=m_rr, m_tt=m_tt, m_pp=m_pp, m_rt=m_rt,
176+
m_rp=m_rp, m_tp=m_tp)
177+
178+
def write_CMTSOLUTION_file(self, filename):
179+
"""
180+
Initialize a source object from a CMTSOLUTION file.
181+
182+
:param filename: path to the CMTSOLUTION file
183+
"""
184+
with open(filename, "w") as f:
185+
# Reconstruct the first line as well as possible. All
186+
# hypocentral information is missing.
187+
f.write(
188+
" PDE %4i %2i %2i %2i %2i %5.2f %8.4f %9.4f %5.1f %.1f %.1f"
189+
" %s\n" % (
190+
self.origin_time.year,
191+
self.origin_time.month,
192+
self.origin_time.day,
193+
self.origin_time.hour,
194+
self.origin_time.minute,
195+
self.origin_time.second +
196+
self.origin_time.microsecond / 1E6,
197+
self.pde_latitude,
198+
self.pde_longitude,
199+
self.pde_depth_in_m / 1e3,
200+
self.mb,
201+
self.ms,
202+
str(self.region_tag)))
203+
f.write('event name: %s\n' % (str(self.eventname)))
204+
f.write('time shift:%12.4f\n' % (self.time_shift,))
205+
f.write('half duration:%9.4f\n' % (self.half_duration,))
206+
f.write('latitude:%14.4f\n' % (self.latitude,))
207+
f.write('longitude:%13.4f\n' % (self.longitude,))
208+
f.write('depth:%17.4f\n' % (self.depth_in_m / 1e3,))
209+
f.write('Mrr:%19.6e\n' % self.m_rr) # * 1e7,))
210+
f.write('Mtt:%19.6e\n' % self.m_tt) # * 1e7,))
211+
f.write('Mpp:%19.6e\n' % self.m_pp) # * 1e7,))
212+
f.write('Mrt:%19.6e\n' % self.m_rt) # * 1e7,))
213+
f.write('Mrp:%19.6e\n' % self.m_rp) # * 1e7,))
214+
f.write('Mtp:%19.6e\n' % self.m_tp) # * 1e7,))
215+
216+
@property
217+
def M0(self):
218+
"""
219+
Scalar Moment M0 in Nm
220+
"""
221+
return (self.m_rr ** 2 + self.m_tt ** 2 + self.m_pp ** 2 +
222+
2 * self.m_rt ** 2 + 2 * self.m_rp ** 2 +
223+
2 * self.m_tp ** 2) ** 0.5 * 0.5 ** 0.5
224+
225+
@property
226+
def moment_magnitude(self):
227+
"""
228+
Moment magnitude M_w
229+
"""
230+
return 2.0 / 3.0 * (np.log10(self.M0) - 7.0) - 6.0
231+
232+
@property
233+
def time_shift(self):
234+
"""
235+
Time shift between cmtsolution and pdesolution
236+
"""
237+
return self.cmt_time - self.origin_time
238+
239+
@property
240+
def tensor(self):
241+
"""
242+
List of moment tensor components in r, theta, phi coordinates:
243+
[m_rr, m_tt, m_pp, m_rt, m_rp, m_tp]
244+
"""
245+
return np.array([self.m_rr, self.m_tt, self.m_pp, self.m_rt, self.m_rp,
246+
self.m_tp])
247+
248+
def __str__(self):
249+
return_str = 'CMTS Source -- %s\n' % self.eventname
250+
return_str += 'origin time(pde): %s\n' % self.origin_time
251+
return_str += 'pde location(lat, lon): %f, %f deg\n' \
252+
% (self.pde_latitude, self.pde_longitude)
253+
return_str += 'pde depth: %f\n' % self.pde_depth_in_m
254+
return_str += 'CMT time: %s\n' % self.cmt_time
255+
return_str += 'CMT location(lat, lon): %f, %f deg\n' \
256+
% (self.latitude, self.longitude)
257+
return_str += 'CMT depth: %6.1e km\n' \
258+
% (self.depth_in_m / 1e3,)
259+
return_str += 'half duration: %f\n' % self.half_duration
260+
return_str += 'moment tensor(Mrr, Mtt, Mpp, Mrt, Mrp, Mtp)\n'
261+
return_str += 'moment tensor: %s\n' \
262+
% self.tensor
263+
return_str += 'Magnitude: %4.2f(mw), %4.2f(mb), %4.2f(ms)\n' \
264+
% (self.moment_magnitude, self.mb, self.ms)
265+
return_str += 'region tag: %s' % self.region_tag
266+
267+
return return_str
268+
269+
def __eq__(self, other):
270+
return self.__dict__ == other.__dict__
271+
272+
def __ne__(self, other):
273+
return self.__dict__ == other.__dict__

pytomo3d/utils/__init__.py

Whitespace-only changes.

pytomo3d/utils/download.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
utils for seismic data download
5+
6+
:copyright:
7+
Wenjie Lei (lei@princeton.edu), 2016
8+
:license:
9+
GNU Lesser General Public License, version 3 (LGPLv3)
10+
(http://www.gnu.org/licenses/lgpl-3.0.en.html)
11+
"""
12+
from obspy.clients.fdsn import Client
13+
import os
14+
15+
16+
def read_station_file(station_filename):
17+
stations = []
18+
with open(station_filename, "rt") as fh:
19+
for line in fh:
20+
line = line.split()
21+
stations.append((line[1], line[0]))
22+
return stations
23+
24+
25+
def _parse_station_id(station_id):
26+
content = station_id.split("_")
27+
if len(content) == 2:
28+
nw, sta = content
29+
loc = "*"
30+
comp = "*"
31+
elif len(content) == 4:
32+
nw, sta, loc, comp = content
33+
return nw, sta, loc, comp
34+
35+
36+
def download_waveform(stations, starttime, endtime, outputdir=".",
37+
client=None):
38+
"""
39+
download wavefrom data from IRIS data center
40+
41+
:param stations: list of stations, should be list of station ids,
42+
for example, "II.AAK.00.BHZ". Parts could be replaced by "*",
43+
for example, "II.AAK.*.BH*"
44+
"""
45+
if client is None:
46+
client = Client("IRIS")
47+
48+
if starttime > endtime:
49+
raise ValueError("Starttime(%s) is larger than endtime(%s)"
50+
% (starttime, endtime))
51+
52+
if not os.path.exists(outputdir):
53+
raise ValueError("Outputdir not exists: %s" % outputdir)
54+
55+
_status = {}
56+
for station_id in stations:
57+
network, station, location, channel = _parse_station_id(station_id)
58+
59+
filename = os.path.join(outputdir, "%s.mseed" % station_id)
60+
if os.path.exists(filename):
61+
os.remove(filename)
62+
63+
try:
64+
st = client.get_waveforms(
65+
network=network, station=station, location=location,
66+
channel=channel, starttime=starttime, endtime=endtime)
67+
if len(st) > 0:
68+
st.write(filename, format="MSEED")
69+
error_code = 0
70+
else:
71+
error_code = 1
72+
except Exception as e:
73+
print("Failed to download waveform '%s' due to: %s"
74+
% (station_id, str(e)))
75+
error_code = 2
76+
77+
_status[station_id] = error_code
78+
79+
return _status
80+
81+
82+
def download_stationxml(stations, starttime, endtime, outputdir=".",
83+
client=None, level="response"):
84+
85+
if client is None:
86+
client = Client("IRIS")
87+
88+
if starttime > endtime:
89+
raise ValueError("Starttime(%s) is larger than endtime(%s)"
90+
% (starttime, endtime))
91+
92+
if not os.path.exists(outputdir):
93+
raise ValueError("Outputdir not exists: %s" % outputdir)
94+
95+
_status = {}
96+
for station_id in stations:
97+
network, station, location, channel = _parse_station_id(station_id)
98+
99+
filename = os.path.join(outputdir, "%s.xml" % station_id)
100+
if os.path.exists(filename):
101+
os.remove(filename)
102+
103+
try:
104+
inv = client.get_stations(
105+
network=network, station=station, location=location,
106+
channel=channel, starttime=starttime, endtime=endtime,
107+
level=level)
108+
if len(inv) > 0:
109+
inv.write(filename, format="STATIONXML")
110+
error_code = 0
111+
else:
112+
error_code = 1
113+
except Exception as e:
114+
print("Failed to download StationXML '%s' due to: %s"
115+
% (station_id, str(e)))
116+
error_code = 1
117+
_status[station_id] = error_code
118+
119+
return _status

0 commit comments

Comments
 (0)