Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/multitrace.py: 52%
80 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-02-05 09:37 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-02-05 09:37 +0000
1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6'''
7Multi-component waveform data model.
8'''
11import numpy as num
12import numpy.ma as ma
14from . import trace
15from .trace import Trace
16from .guts import Object, Float, Timestamp, List
17from .guts_array import Array
18from .squirrel.model import CodesNSLCE
21class MultiTrace(Object):
22 '''
23 Container for multi-component waveforms with common time span and sampling.
25 Instances of this class can be used to efficiently represent
26 multi-component waveforms of a single sensor or of a sensor array. The data
27 samples are stored in a single 2D array where the first index runs over
28 components and the second index over time. Metadata contains sampling rate,
29 start-time and :py:class:`~pyrocko.squirrel.model.CodesNSLCE` identifiers
30 for the contained traces.
32 :param traces:
33 If given, construct multi-trace from given single-component waveforms
34 (see :py:func:`~pyrocko.trace.get_traces_data_as_array`) and ignore
35 any other arguments.
36 :type traces:
37 :py:class:`list` of :py:class:`~pyrocko.trace.Trace`
38 '''
40 codes = List.T(
41 CodesNSLCE.T(),
42 help='List of codes identifying the components.')
43 data = Array.T(
44 shape=(None, None),
45 help='Array containing the data samples indexed as '
46 '``(icomponent, isample)``.')
47 tmin = Timestamp.T(
48 default=Timestamp.D('1970-01-01 00:00:00'),
49 help='Start time.')
50 deltat = Float.T(
51 default=1.0,
52 help='Sampling interval [s]')
54 def __init__(
55 self,
56 traces=None,
57 assemble='concatenate',
58 data=None,
59 codes=None,
60 tmin=None,
61 deltat=None):
63 if traces is not None:
64 if len(traces) == 0:
65 data = ma.zeros((0, 0))
66 else:
67 if assemble == 'merge':
68 data, codes, tmin, deltat \
69 = trace.merge_traces_data_as_array(traces)
71 elif assemble == 'concatenate':
72 data = ma.array(trace.get_traces_data_as_array(traces))
73 codes = [tr.codes for tr in traces]
74 tmin = traces[0].tmin
75 deltat = traces[0].deltat
77 self.ntraces, self.nsamples = data.shape
79 if codes is None:
80 codes = [CodesNSLCE()] * self.ntraces
82 if len(codes) != self.ntraces:
83 raise ValueError(
84 'MultiTrace construction: mismatch between number of traces '
85 'and number of codes given.')
87 if deltat is None:
88 deltat = self.T.deltat.default()
90 if tmin is None:
91 tmin = self.T.tmin.default()
93 Object.__init__(self, codes=codes, data=data, tmin=tmin, deltat=deltat)
95 def __len__(self):
96 '''
97 Get number of components.
98 '''
99 return self.ntraces
101 def __getitem__(self, i):
102 '''
103 Get single component waveform (shared data).
105 :param i:
106 Component index.
107 :type i:
108 int
109 '''
110 return self.get_trace(i)
112 def copy(self, data='copy'):
114 if isinstance(data, str):
115 assert data in ('copy', 'reference')
116 data = self.data.copy() if data == 'copy' else self.data
117 else:
118 assert isinstance(data, ma.MaskedArray)
120 return MultiTrace(
121 data=data,
122 codes=list(self.codes),
123 tmin=self.tmin,
124 deltat=self.deltat)
126 @property
127 def tmax(self):
128 '''
129 End time (time of last sample, read-only).
130 '''
131 return self.tmin + (self.nsamples - 1) * self.deltat
133 def get_trace(self, i, span=slice(None)):
134 '''
135 Get single component waveform (shared data).
137 :param i:
138 Component index.
139 :type i:
140 int
141 '''
143 network, station, location, channel, extra = self.codes[i]
144 return Trace(
145 network=network,
146 station=station,
147 location=location,
148 channel=channel,
149 extra=extra,
150 tmin=self.tmin + (span.start or 0) * self.deltat,
151 deltat=self.deltat,
152 ydata=self.data.data[i, span])
154 def iter_valid_traces(self):
155 if self.data.mask is ma.nomask:
156 return iter(self)
158 for irow, row in enumerate(ma.notmasked_contiguous(self.data, axis=1)):
159 for slice in row:
160 yield self.get_trace(irow, slice)
162 def get_traces(self):
163 return list(self)
165 def get_valid_traces(self):
166 return list(self.iter_valid_traces())
168 def snuffle(self):
169 '''
170 Show in Snuffler.
171 '''
172 trace.snuffle(list(self))
174 def snuffle_valid(self):
175 trace.snuffle(self.get_valid_traces())
177 def bleed_mask(self, t):
178 if self.data.mask is ma.nomask:
179 return
181 nt = int(num.round(abs(t)/self.deltat))
182 for irow, row in enumerate(ma.notmasked_contiguous(self.data, axis=1)):
183 for span in row:
184 self.data.mask[irow, span.start:span.start+nt] = True
185 self.data.mask[irow, max(0, span.stop-nt):span.stop] = True
188def correlate(a, b, mode='valid', normalization=None, use_fft=False):
190 if isinstance(a, Trace) and isinstance(b, Trace):
191 return trace.correlate(
192 a, b, mode=mode, normalization=normalization, use_fft=use_fft)
194 elif isinstance(a, Trace) and isinstance(b, MultiTrace):
195 return MultiTrace([
196 trace.correlate(
197 a, b_,
198 mode=mode, normalization=normalization, use_fft=use_fft)
199 for b_ in b])
201 elif isinstance(a, MultiTrace) and isinstance(b, Trace):
202 return MultiTrace([
203 trace.correlate(
204 a_, b,
205 mode=mode, normalization=normalization, use_fft=use_fft)
206 for a_ in a])
208 elif isinstance(a, MultiTrace) and isinstance(b, MultiTrace):
209 return MultiTrace([
210 trace.correlate(
211 a_, b_,
212 mode=mode, normalization=normalization, use_fft=use_fft)
214 for a_ in a for b_ in b])