DASPy-toolbox 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. DASPy_toolbox-1.0.0.dist-info/LICENSE.txt +1 -0
  2. DASPy_toolbox-1.0.0.dist-info/METADATA +85 -0
  3. DASPy_toolbox-1.0.0.dist-info/RECORD +49 -0
  4. DASPy_toolbox-1.0.0.dist-info/WHEEL +5 -0
  5. DASPy_toolbox-1.0.0.dist-info/entry_points.txt +2 -0
  6. DASPy_toolbox-1.0.0.dist-info/top_level.txt +1 -0
  7. daspy/__init__.py +4 -0
  8. daspy/advanced_tools/__init__.py +0 -0
  9. daspy/advanced_tools/channel.py +354 -0
  10. daspy/advanced_tools/decomposition.py +165 -0
  11. daspy/advanced_tools/denoising.py +276 -0
  12. daspy/advanced_tools/fdct.py +789 -0
  13. daspy/advanced_tools/strain2vel.py +245 -0
  14. daspy/basic_tools/__init__.py +0 -0
  15. daspy/basic_tools/filter.py +257 -0
  16. daspy/basic_tools/freqattributes.py +117 -0
  17. daspy/basic_tools/preprocessing.py +238 -0
  18. daspy/basic_tools/visualization.py +186 -0
  19. daspy/core/__init__.py +4 -0
  20. daspy/core/collection.py +279 -0
  21. daspy/core/dasdatetime.py +72 -0
  22. daspy/core/example.pkl +0 -0
  23. daspy/core/make_example.py +32 -0
  24. daspy/core/read.py +544 -0
  25. daspy/core/section.py +1319 -0
  26. daspy/core/write.py +282 -0
  27. daspy/seismic_detection/__init__.py +1 -0
  28. daspy/seismic_detection/calc_travel_time.py +23 -0
  29. daspy/seismic_detection/core.py +119 -0
  30. daspy/seismic_detection/detection.py +12 -0
  31. daspy/seismic_detection/gamma/__init__.py +13 -0
  32. daspy/seismic_detection/gamma/_base.py +549 -0
  33. daspy/seismic_detection/gamma/_bayesian_mixture.py +875 -0
  34. daspy/seismic_detection/gamma/_gaussian_mixture.py +866 -0
  35. daspy/seismic_detection/gamma/app.py +192 -0
  36. daspy/seismic_detection/gamma/seismic_ops.py +478 -0
  37. daspy/seismic_detection/gamma/utils.py +512 -0
  38. daspy/seismic_detection/location.py +266 -0
  39. daspy/seismic_detection/magnitude.py +43 -0
  40. daspy/seismic_detection/phase_picking.py +67 -0
  41. daspy/structure_imaging/__init__.py +0 -0
  42. daspy/structure_imaging/ambient_noise.py +4 -0
  43. daspy/structure_imaging/dispersion.py +27 -0
  44. daspy/structure_imaging/fault_zone.py +59 -0
  45. daspy/structure_imaging/inversion.py +6 -0
  46. daspy/traffic_monitoring/JamDetection.py +6 -0
  47. daspy/traffic_monitoring/SpeedMeasurement.py +6 -0
  48. daspy/traffic_monitoring/VehicleDetection.py +6 -0
  49. daspy/traffic_monitoring/__init__.py +0 -0
@@ -0,0 +1,279 @@
1
+ # Purpose: Module for handling Collection objects.
2
+ # Author: Minzhe Hu
3
+ # Date: 2024.11.1
4
+ # Email: hmz2018@mail.ustc.edu.cn
5
+ import os
6
+ import warnings
7
+ import numpy as np
8
+ from tqdm import tqdm
9
+ from glob import glob
10
+ from daspy.core.read import read
11
+ from daspy.core.dasdatetime import DASDateTime
12
+ from daspy.basic_tools.preprocessing import cosine_taper
13
+
14
+
15
+ cascade_method = ['time_integration', 'time_differential', 'downsampling',
16
+ 'bandpass', 'bandstop', 'lowpass', 'highpass',
17
+ 'lowpass_cheby_2']
18
+
19
+ class Collection(object):
20
+ def __init__(self, fpath, ftype=None, flength=None, meta_from_file=True,
21
+ timeinfo_format=None, timeinfo_from_basename=True, **kwargs):
22
+ """
23
+ :param fpath: str or Sequence of str. File path(s) containing data.
24
+ :param ftype: None or str. None for automatic detection, or 'pkl',
25
+ 'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy', 'npy'.
26
+ :param flength: float. The duration of a single file in senconds.
27
+ :param meta_from_file: bool or 'all'. False for manually set dt, dx, fs
28
+ and gauge_length. True for extracting dt, dx, fs and gauge_length
29
+ from first 2 file. 'all' for exracting and checking these metadata
30
+ from all file.
31
+ :param timeinfo_format: str or (slice, str). Format for extracting start
32
+ time from file name.
33
+ :param timeinfo_from_basename: bool. If True, timeinfo_format will use
34
+ DASDateTime.strptime to basename of fpath.
35
+ :param nch: int. Channel number.
36
+ :param nt: int. Sampling points of each file.
37
+ :param dx: number. Channel interval in m.
38
+ :param fs: number. Sampling rate in Hz.
39
+ :param gauge_length: number. Gauge length in m.
40
+ """
41
+ if isinstance(fpath, (list, tuple)):
42
+ self.flist = []
43
+ for fp in fpath:
44
+ self.flist.extend(glob(fp))
45
+ else:
46
+ self.flist = glob(fpath)
47
+ if not len(self.flist):
48
+ raise ValueError('No file input.')
49
+ self.flist.sort()
50
+ self.ftype = ftype
51
+ for key in ['nch', 'nt', 'dx', 'fs', 'gauge_length']:
52
+ if key in kwargs.keys():
53
+ setattr(self, key, kwargs[key])
54
+ if timeinfo_format is None and not meta_from_file:
55
+ meta_from_file = True
56
+
57
+ if meta_from_file == 'all':
58
+ ftime = []
59
+ metadata_list = []
60
+ for f in self.flist:
61
+ sec = read(f, ftype=ftype, headonly=True)
62
+ if not hasattr(sec, 'gauge_length'):
63
+ sec.gauge_length = None
64
+ ftime.append(sec.start_time)
65
+ metadata_list.append((sec.nch, sec.nt, sec.dx, sec.fs,
66
+ sec.gauge_length))
67
+
68
+ if len(set(metadata_list)) > 1:
69
+ warnings.warn('More than one kind of setting detected.')
70
+ metadata = max(metadata_list, key=metadata_list.count)
71
+ for i, key in enumerate(['nch', 'nt', 'dx', 'fs', 'gauge_length']):
72
+ if not hasattr(self, key):
73
+ setattr(self, key, metadata[i])
74
+ self.ftime = ftime
75
+ elif meta_from_file:
76
+ i = int(len(self.flist) > 1)
77
+ sec = read(self.flist[i], ftype=ftype, headonly=True)
78
+ if timeinfo_format is None:
79
+ if flength is None:
80
+ flength = sec.duration
81
+ self.ftime = [sec.start_time + (j - i) * flength for j in
82
+ range(len(self))]
83
+ if not hasattr(sec, 'gauge_length'):
84
+ sec.gauge_length = None
85
+ metadata = (sec.nch, sec.nt, sec.dx, sec.fs, sec.gauge_length)
86
+ for i, key in enumerate(['nch', 'nt', 'dx', 'fs', 'gauge_length']):
87
+ if not hasattr(self, key):
88
+ setattr(self, key, metadata[i])
89
+
90
+ if not hasattr(self, 'ftime'):
91
+ if isinstance(timeinfo_format, tuple):
92
+ timeinfo_slice, timeinfo_format = timeinfo_format
93
+ else:
94
+ timeinfo_slice = slice(None)
95
+ if timeinfo_from_basename:
96
+ self.ftime = [DASDateTime.strptime(
97
+ os.path.basename(f)[timeinfo_slice], timeinfo_format)
98
+ for f in self.flist]
99
+ else:
100
+ self.ftime = [DASDateTime.strptime(f[timeinfo_slice],
101
+ timeinfo_format) for f in self.flist]
102
+
103
+ self._sort()
104
+ if flength is None:
105
+ if len(self.flist) > 2:
106
+ time_diff = np.round(np.diff(self.ftime[1:]).astype(float))
107
+ flength_set, counts = np.unique(time_diff, return_counts=True)
108
+ if len(flength_set) > 1:
109
+ warnings.warn('File start times are unevenly spaced. Data '
110
+ 'may not be continuous and self.flength may '
111
+ 'be incorrectly detected.')
112
+ flength = flength_set[counts.argmax()]
113
+ elif len(self.flist) == 2:
114
+ flength = self.ftime[1] - self.ftime[0]
115
+ else:
116
+ flength = read(self.flist[0], ftype=ftype,
117
+ headonly=True).duration
118
+ elif flength <= 0:
119
+ raise ValueError('dt must > 0')
120
+
121
+ self.flength = flength
122
+
123
+ def __str__(self):
124
+ if len(self) == 1:
125
+ describe = f' flist: {self.flist}\n'
126
+ elif len(self) <= 5:
127
+ describe = f' flist: {len(self)} files\n' + \
128
+ f' {self.flist}\n'
129
+ else:
130
+ describe = f' flist: {len(self)} files\n' + \
131
+ f' [{self[0]},\n' + \
132
+ f' {self[1]},\n' + \
133
+ f' ...,\n' + \
134
+ f' {self[-1]}]\n'
135
+
136
+ describe += f' ftime: {self.start_time} to {self.end_time}\n' + \
137
+ f' flength: {self.flength}\n' + \
138
+ f' nch: {self.nch}\n' + \
139
+ f' nt: {self.nt}\n' + \
140
+ f' dx: {self.dx}\n' + \
141
+ f' fs: {self.fs}\n' + \
142
+ f'gauge_length: {self.gauge_length}\n'
143
+
144
+ return describe
145
+
146
+ __repr__ = __str__
147
+
148
+ def __getitem__(self, i):
149
+ return self.flist[i]
150
+
151
+ def __len__(self):
152
+ return len(self.flist)
153
+
154
+ def _sort(self):
155
+ sort = np.argsort(self.ftime)
156
+ self.ftime = [self.ftime[i] for i in sort]
157
+ self.flist = [self.flist[i] for i in sort]
158
+ return self
159
+
160
+ @property
161
+ def start_time(self):
162
+ return self.ftime[0]
163
+
164
+ @property
165
+ def end_time(self):
166
+ return self.ftime[-1] + self.flength
167
+
168
+ @property
169
+ def duration(self):
170
+ return self.end_time - self.start_time
171
+
172
+ def select(self, stime=None, etime=None, readsec=False, **kwargs):
173
+ """
174
+ Select a period of data.
175
+
176
+ :param stime, etime: DASDateTime. Start and end time of required data.
177
+ :param readsec: bool. If True, read as a instance of daspy.Section and
178
+ return. If False, update self.flist.
179
+ :param ch1: int. The first channel required. Only works when
180
+ readsec=True.
181
+ :param ch2: int. The last channel required (not included). Only works
182
+ when readsec=True.
183
+ """
184
+ if stime is None:
185
+ stime = self.ftime[0]
186
+ elif stime - self.ftime[0] < 0:
187
+ warnings.warn('stime is earlier than the start time of the first '
188
+ 'file. Set stime to self.ftime[0].')
189
+
190
+ if etime is None:
191
+ etime = self.ftime[-1] + self.flength
192
+ elif etime - self.ftime[-1] > self.flength:
193
+ warnings.warn('etime is later than the end time of the last file. '
194
+ 'Set etime to self.ftime[-1] + self.flength.')
195
+
196
+ if stime > etime:
197
+ raise ValueError('Start time can\'t be later than end time.')
198
+
199
+ flist = []
200
+ ftime = []
201
+ for i in range(len(self)):
202
+ if (stime - self.flength) < self.ftime[i] < etime:
203
+ flist.append(self.flist[i])
204
+ ftime.append(self.ftime[i])
205
+
206
+ if readsec:
207
+ sec = read(flist[0], **kwargs)
208
+ for f in flist[1:]:
209
+ sec += read(f, **kwargs)
210
+ sec.trimming(tmin=stime, tmax=etime)
211
+ return sec
212
+ else:
213
+ self.flist = flist
214
+ self.ftime = ftime
215
+ return self
216
+
217
+ def _optimize_for_continuity(self, operations):
218
+ method_list = []
219
+ kwargs_list = []
220
+ if not isinstance(operations[0], (list, tuple)):
221
+ operations = [operations]
222
+ for opera in operations:
223
+ method, kwargs = opera
224
+ if method == 'downsampling':
225
+ if hasattr(kwargs, 'lowpass_filter') and not\
226
+ kwargs['lowpass_filter']:
227
+ method_list.append('downsampling')
228
+ kwargs_list.append(kwargs)
229
+ else:
230
+ method_list.extend(['lowpass_cheby_2', 'downsampling'])
231
+ kwargs['lowpass_filter'] = False
232
+ kwargs0 = dict(freq=self.fs/2/kwargs['tint'], zi=0)
233
+ kwargs_list.extend([kwargs0, kwargs])
234
+ else:
235
+ if method in cascade_method:
236
+ kwargs.setdefault('zi', 0)
237
+
238
+ method_list.append(method)
239
+ kwargs_list.append(kwargs)
240
+ return method_list, kwargs_list
241
+
242
+ def process(self, operations, savepath='./processed',
243
+ suffix='_pro', ftype=None, **read_kwargs):
244
+ """
245
+ :param savepath:
246
+ :param ch1: int. The first channel required.
247
+ :param ch2: int. The last channel required (not included).
248
+ """
249
+ if not os.path.exists(savepath):
250
+ os.makedirs(savepath)
251
+ method_list, kwargs_list = self._optimize_for_continuity(operations)
252
+ new_flist = []
253
+ for i in tqdm(range(len(self))):
254
+ f = self[i]
255
+ sec = read(f, ftype=self.ftype, **read_kwargs)
256
+ for j, method in enumerate(method_list):
257
+ if method == 'taper':
258
+ if i == 0:
259
+ win = cosine_taper(np.ones_like(sec.data), **kwargs_list[j])
260
+ win[:, -sec.nt//2:] = 1
261
+ sec.data *= win
262
+ elif i == len(self) - 1:
263
+ win = cosine_taper(np.ones_like(sec.data), **kwargs_list[j])
264
+ win[:, :sec.nt//2] = 1
265
+ sec.data *= win
266
+ else:
267
+ out = eval(f'sec.{method}')(**kwargs_list[j])
268
+ if method == 'time_integration':
269
+ kwargs_list[j]['c'] = sec.data[:, -1]
270
+ elif method == 'time_differential':
271
+ kwargs_list[j]['prepend'] = sec.data[:, -1]
272
+ elif method in cascade_method:
273
+ kwargs_list[j]['zi'] = out
274
+ f0, f1 = os.path.splitext(os.path.basename(f))
275
+ if ftype is not None:
276
+ f1 = ftype
277
+ filepath = os.path.join(savepath, f0+suffix+f1)
278
+ sec.save(filepath)
279
+ new_flist.append(filepath)
@@ -0,0 +1,72 @@
1
+ # Purpose: Module for handling DASDateTime objects.
2
+ # Author: Minzhe Hu
3
+ # Date: 2024.9.25
4
+ # Email: hmz2018@mail.ustc.edu.cn
5
+ import time
6
+ from typing import Iterable
7
+ from datetime import datetime, timedelta, timezone
8
+
9
+
10
+ utc = timezone.utc
11
+ local_tz = timezone(timedelta(seconds=-time.altzone))
12
+
13
+
14
+ class DASDateTime(datetime):
15
+
16
+
17
+ def __add__(self, other):
18
+ if isinstance(other, Iterable):
19
+ out = []
20
+ for t in other:
21
+ out.append(self + t)
22
+ return out
23
+ elif not isinstance(other, timedelta):
24
+ other = timedelta(seconds=float(other))
25
+ return super().__add__(other)
26
+
27
+ def __sub__(self, other):
28
+ if isinstance(other, Iterable):
29
+ out = []
30
+ for t in other:
31
+ out.append(self - t)
32
+ return out
33
+ elif isinstance(other, datetime):
34
+ return datetime.__sub__(*self._unify_tz(other)).total_seconds()
35
+ elif not isinstance(other, timedelta):
36
+ other = timedelta(seconds=other)
37
+ return super().__sub__(other)
38
+
39
+ def __le__(self, other):
40
+ return datetime.__le__(*self._unify_tz(other))
41
+
42
+ def __lt__(self, other):
43
+ return datetime.__lt__(*self._unify_tz(other))
44
+
45
+ def __ge__(self, other):
46
+ return datetime.__ge__(*self._unify_tz(other))
47
+
48
+ def __gt__(self, other):
49
+ return datetime.__gt__(*self._unify_tz(other))
50
+
51
+ def _unify_tz(self, other):
52
+ if self.tzinfo and not other.tzinfo:
53
+ return self, other.replace(tzinfo=self.tzinfo)
54
+ elif not self.tzinfo and other.tzinfo:
55
+ return self.replace(tzinfo=other.tzinfo), other
56
+ return self, other
57
+
58
+ def local(self):
59
+ return self.astimezone(tz=local_tz)
60
+
61
+ def utc(self):
62
+ return self.astimezone(tz=utc)
63
+
64
+ def remove_tz(self):
65
+ return self.replace(tzinfo=None)
66
+
67
+ @classmethod
68
+ def from_datetime(cls, dt: datetime):
69
+ return cls.fromtimestamp(dt.timestamp(), tz=dt.tzinfo)
70
+
71
+ def to_datetime(self):
72
+ return datetime.fromtimestamp(self.timestamp(), tz=self.tzinfo)
daspy/core/example.pkl ADDED
Binary file
@@ -0,0 +1,32 @@
1
+ import h5py
2
+ import numpy as np
3
+ from daspy import read, Section, DASDateTime
4
+ from daspy.core.dasdatetime import utc
5
+
6
+
7
+ origin_time = DASDateTime(2016, 3, 21, 7, 37, 10, 535000, tzinfo=utc)
8
+
9
+ # read DAS data
10
+ dx, fs = 1, 1000
11
+ sec = Section(np.zeros((8721, 0)), dx, fs, data_type='Strain rate')
12
+ filename = ['PoroTomo_iDAS16043_160321073651.h5',
13
+ 'PoroTomo_iDAS16043_160321073721.h5',
14
+ 'PoroTomo_iDAS16043_160321073751.h5',
15
+ 'PoroTomo_iDAS16043_160321073821.h5']
16
+
17
+ first = True
18
+ for fn in filename:
19
+ with h5py.File(fn,'r') as fp:
20
+ if first:
21
+ sec.start_time = DASDateTime.fromtimestamp(fp['t'][0]).astimezone(utc)
22
+ sec.start_channel = fp['channel'][0]
23
+ first = False
24
+ sec += fp['das'][()].T
25
+
26
+ sec.origin_time = origin_time
27
+ sec.trimming(mode=0, xmin=2500, xmax=3000)
28
+ sec.trimming(tmin=origin_time, tmax=origin_time+90)
29
+ sec.downsampling(tint=10)
30
+ sec.trimming(tmin=origin_time+20, tmax=origin_time+70)
31
+
32
+ sec.save('example.py')