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
daspy/core/read.py ADDED
@@ -0,0 +1,544 @@
1
+ # Purpose: Module for reading DAS data.
2
+ # Author: Minzhe Hu
3
+ # Date: 2024.11.1
4
+ # Email: hmz2018@mail.ustc.edu.cn
5
+ # Partially modified from
6
+ # https://github.com/RobbinLuo/das-toolkit/blob/main/DasTools/DasPrep.py
7
+ import warnings
8
+ import json
9
+ import pickle
10
+ import numpy as np
11
+ import h5py
12
+ import segyio
13
+ from pathlib import Path
14
+ from nptdms import TdmsFile
15
+ from daspy.core.section import Section
16
+ from daspy.core.dasdatetime import DASDateTime, utc
17
+
18
+
19
+ def read(fname=None, output_type='section', ftype=None, headonly=False,
20
+ **kwargs):
21
+ """
22
+ Read a .pkl/.pickle, .tdms, .h5/.hdf5, .segy/.sgy file.
23
+
24
+ :param fname: str or pathlib.PosixPath. Path of DAS data file.
25
+ :param output_type: str. 'Section' means return an instance of
26
+ daspy.Section, 'array' means return numpy.array for data and a
27
+ dictionary for metadata.
28
+ :param ftype: None, str or function. None for automatic detection, or str to
29
+ specify a type of 'pkl', 'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy',
30
+ or 'npy', or a function for read data and metadata.
31
+ :param headonly. bool. If True, only metadata will be read, the returned
32
+ data will be an array of all zeros of the same size as the original
33
+ data.
34
+ :param ch1: int. The first channel required.
35
+ :param ch2: int. The last channel required (not included).
36
+ :param dch: int. Channel step.
37
+ :return: An instance of daspy.Section, or numpy.array for data and a
38
+ dictionary for metadata.
39
+ """
40
+ fun_map = {'pkl': _read_pkl, 'tdms': _read_tdms, 'h5': _read_h5,
41
+ 'sgy': _read_segy, 'npy': _read_npy}
42
+ if fname is None:
43
+ fname = Path(__file__).parent / 'example.pkl'
44
+ ftype = 'pkl'
45
+ if ftype is None:
46
+ ftype = str(fname).split('.')[-1].lower()
47
+
48
+ if callable(ftype):
49
+ try:
50
+ data, metadata = ftype(fname, headonly=headonly, **kwargs)
51
+ except TypeError:
52
+ data, metadata = ftype(fname)
53
+ else:
54
+ for rtp in [('pickle', 'pkl'), ('hdf5', 'h5'), ('segy', 'sgy')]:
55
+ ftype = ftype.replace(*rtp)
56
+ data, metadata = fun_map[ftype](fname, headonly=headonly, **kwargs)
57
+
58
+ if output_type.lower() == 'section':
59
+ metadata['source'] = Path(fname)
60
+ metadata['source_type'] = ftype
61
+ return Section(data.astype(float), **metadata)
62
+ elif output_type.lower() == 'array':
63
+ return data, metadata
64
+
65
+
66
+ def _read_pkl(fname, headonly=False, **kwargs):
67
+ dch = kwargs.pop('dch', 1)
68
+ with open(fname, 'rb') as f:
69
+ pkl_data = pickle.load(f)
70
+ if isinstance(pkl_data, np.ndarray):
71
+ warnings.warn('This data format doesn\'t include channel interval'
72
+ 'and sampling rate. Please set manually')
73
+ if headonly:
74
+ return np.zeros_like(pkl_data), {'dx': None, 'fs': None}
75
+ else:
76
+ ch1 = kwargs.pop('ch1', 0)
77
+ ch2 = kwargs.pop('ch2', len(pkl_data))
78
+ return pkl_data[ch1:ch2:dch], {'dx': None, 'fs': None}
79
+ elif isinstance(pkl_data, dict):
80
+ data = pkl_data.pop('data')
81
+ if headonly:
82
+ data = np.zeros_like(data)
83
+ else:
84
+ if 'ch1' in kwargs.keys() or 'ch2' in kwargs.keys():
85
+ if 'start_channel' in pkl_data.keys():
86
+ s_chn = pkl_data['start_channel']
87
+ print(f'Data is start with channel {s_chn}.')
88
+ else:
89
+ s_chn = 0
90
+ ch1 = kwargs.pop('ch1', s_chn)
91
+ ch2 = kwargs.pop('ch2', s_chn + len(data))
92
+ data = data[ch1 - s_chn:ch2 - s_chn, :]
93
+ pkl_data['start_channel'] = ch1
94
+ return data, pkl_data
95
+ else:
96
+ raise TypeError('Unknown data type.')
97
+
98
+
99
+ def _read_h5_headers(group):
100
+ headers = {}
101
+ if len(group.attrs) != 0:
102
+ headers['attrs'] = dict(group.attrs)
103
+ for k in group.keys():
104
+ gp = group[k]
105
+ if isinstance(gp, h5py._hl.dataset.Dataset):
106
+ continue
107
+ elif isinstance(gp, h5py._hl.group.Group):
108
+ gp_headers = _read_h5_headers(group[k])
109
+ if len(gp_headers):
110
+ headers[k] = gp_headers
111
+ else:
112
+ headers[k] = gp
113
+
114
+ return headers
115
+
116
+
117
+ def _read_h5_starttime(h5_file):
118
+ try:
119
+ stime = h5_file['Acquisition/Raw[0]/RawData'].\
120
+ attrs['PartStartTime'].decode('ascii')
121
+ except KeyError:
122
+ try:
123
+ stime = h5_file['Acquisition'].\
124
+ attrs['MeasurementStartTime'].decode('ascii')
125
+ except KeyError:
126
+ try:
127
+ stime = h5_file['Acquisition/Raw[0]/RawDataTime/'][0]
128
+ except KeyError:
129
+ return 0
130
+
131
+ if isinstance(stime, str):
132
+ if len(stime) > 26:
133
+ stime = DASDateTime.strptime(stime, '%Y-%m-%dT%H:%M:%S.%f%z')
134
+ else:
135
+ stime = DASDateTime.strptime(stime, '%Y-%m-%dT%H:%M:%S.%f').\
136
+ astimezone(utc)
137
+ else:
138
+ stime = DASDateTime.fromtimestamp(stime / 1e6).astimezone(utc)
139
+
140
+ return stime
141
+
142
+
143
+ def _read_h5(fname, headonly=False, **kwargs):
144
+ with h5py.File(fname, 'r') as h5_file:
145
+ dch = kwargs.pop('dch', 1)
146
+ group = list(h5_file.keys())[0]
147
+ if len(h5_file.keys()) >= 10: # ASN/OptoDAS https://github.com/ASN-Norway/simpleDAS
148
+ ch1 = kwargs.pop('ch1', 0)
149
+ if h5_file['header/dimensionNames'][0] == b'time':
150
+ nch = h5_file['data'].shape[1]
151
+ if headonly:
152
+ data = np.zeros_like(h5_file['data']).T
153
+ else:
154
+ ch2 = kwargs.pop('ch2', nch)
155
+ data = h5_file['data'][:, ch1:ch2:dch].T
156
+ elif h5_file['header/dimensionNames'][0] == b'distance':
157
+ nch = h5_file['data'].shape[1]
158
+ if headonly:
159
+ data = np.zeros_like(h5_file['data'])
160
+ else:
161
+ ch2 = kwargs.pop('ch2', nch)
162
+ data = h5_file['data'][ch1:ch2:dch, :]
163
+ dx = h5_file['header/dx'][()]
164
+ start_time = DASDateTime.fromtimestamp(
165
+ h5_file['header/time'][()]).utc()
166
+ metadata = {'dx': dx * dch, 'fs': 1 / h5_file['header/dt'][()],
167
+ 'start_time': start_time, 'start_channel': ch1,
168
+ 'start_distance': ch1 * dx,
169
+ 'scale': h5_file['header/dataScale'][()]}
170
+ if h5_file['header/gaugeLength'][()] != np.nan:
171
+ metadata['guage_length'] = h5_file['header/gaugeLength'][()]
172
+ elif len(h5_file.keys()) == 5: # AP Sensing
173
+ # read data
174
+ nch = h5_file['strain'].shape[1]
175
+ ch1 = kwargs.pop('ch1', 0)
176
+ ch2 = kwargs.pop('ch2', nch)
177
+ if headonly:
178
+ data = np.zeros_like(h5_file['strain']).T
179
+ else:
180
+ data = h5_file['strain'][:, ch1:ch2:dch].T
181
+
182
+ # read metadata
183
+ dx = h5_file['spatialsampling'][()]
184
+ metadata = {'fs': h5_file['RepetitionFrequency'][()],
185
+ 'dx': dx * dch, 'start_channel': ch1,
186
+ 'start_distance': ch1 * dx,
187
+ 'gauge_length': h5_file.get('GaugeLength')[()]}
188
+ elif set(h5_file.keys()) == {'Mapping', 'Acquisition'}: # Silixa/iDAS
189
+ nch = h5_file['Acquisition/Raw[0]'].attrs['NumberOfLoci']
190
+ ch1 = kwargs.pop('ch1', 0)
191
+ ch2 = kwargs.pop('ch2', nch)
192
+ if h5_file['Acquisition/Raw[0]/RawData/'].shape[0] == nch:
193
+ if headonly:
194
+ data = np.zeros_like(h5_file['Acquisition/Raw[0]/RawData/'])
195
+ else:
196
+ data = h5_file['Acquisition/Raw[0]/RawData/']\
197
+ [ch1:ch2:dch, :]
198
+ else:
199
+ if headonly:
200
+ data = np.zeros_like(
201
+ h5_file['Acquisition/Raw[0]/RawData/']).T
202
+ else:
203
+ data = h5_file['Acquisition/Raw[0]/RawData/']\
204
+ [:, ch1:ch2:dch].T
205
+
206
+ dx = np.mean(h5_file['Mapping/MeasuredSpatialResolution'])
207
+ start_distance = h5_file['Acquisition/Custom/UserSettings'].\
208
+ attrs['StartDistance'] + ch1 * dx
209
+ h5_file['Acquisition/Raw[0]/RawData'].attrs['PartStartTime']
210
+ fs = h5_file['Acquisition/Raw[0]'].attrs['OutputDataRate']
211
+ gauge_length = h5_file['Acquisition'].attrs['GaugeLength']
212
+ scale = h5_file['Acquisition/Raw[0]'].attrs['AmpScaling']
213
+ geometry = np.vstack((h5_file['Mapping/Lon'],
214
+ h5_file['Mapping/Lat'])).T
215
+ metadata = {'dx': dx * dch, 'fs': fs, 'start_channel': ch1,
216
+ 'start_distance': ch1 * dx,
217
+ 'gauge_length': gauge_length, 'geometry': geometry,
218
+ 'scale': scale}
219
+ metadata['start_time'] = _read_h5_starttime(h5_file)
220
+ elif group == 'Acquisition': # OptaSens/ODH, Silixa/iDAS, Smart Sensing/ZD DAS
221
+ # read data
222
+ try:
223
+ nch = h5_file['Acquisition'].attrs['NumberOfLoci']
224
+ except KeyError:
225
+ nch = len(h5_file['Acquisition/Raw[0]/RawData/'])
226
+ ch1 = kwargs.pop('ch1', 0)
227
+ ch2 = kwargs.pop('ch2', nch)
228
+ if h5_file['Acquisition/Raw[0]/RawData/'].shape[0] == nch:
229
+ if headonly:
230
+ data = np.zeros_like(h5_file['Acquisition/Raw[0]/RawData/'])
231
+ else:
232
+ data = h5_file['Acquisition/Raw[0]/RawData/']\
233
+ [ch1:ch2:dch, :]
234
+ else:
235
+ if headonly:
236
+ data = np.zeros_like(
237
+ h5_file['Acquisition/Raw[0]/RawData/']).T
238
+ else:
239
+ data = h5_file['Acquisition/Raw[0]/RawData/']\
240
+ [:, ch1:ch2:dch].T
241
+
242
+ # read metadata
243
+ try:
244
+ fs = h5_file['Acquisition/Raw[0]'].attrs['OutputDataRate']
245
+ except KeyError:
246
+ time_arr = h5_file['Acquisition/Raw[0]/RawDataTime/']
247
+ fs = 1 / (np.diff(time_arr).mean() / 1e6)
248
+
249
+ dx = h5_file['Acquisition'].attrs['SpatialSamplingInterval']
250
+ gauge_length = h5_file['Acquisition'].attrs['GaugeLength']
251
+ metadata = {'dx': dx * dch, 'fs': fs, 'start_channel': ch1,
252
+ 'start_distance': ch1 * dx,
253
+ 'gauge_length': gauge_length}
254
+
255
+ metadata['start_time'] = _read_h5_starttime(h5_file)
256
+ elif group == 'raw':
257
+ nch = len(h5_file['raw'])
258
+ ch1 = kwargs.pop('ch1', 0)
259
+ ch2 = kwargs.pop('ch2', nch)
260
+ if headonly:
261
+ data = np.zeros_like(h5_file['raw'])
262
+ else:
263
+ data = h5_file['raw'][ch1:ch2:dch, :]
264
+ fs = round(1 / np.diff(h5_file['timestamp']).mean())
265
+ start_time = DASDateTime.fromtimestamp(
266
+ h5_file['timestamp'][0]).astimezone(utc)
267
+ warnings.warn('This data format doesn\'t include channel interval. '
268
+ 'Please set manually')
269
+ metadata = {'dx': None, 'fs': fs, 'start_channel': ch1,
270
+ 'start_time': start_time}
271
+ elif group == 'data_product':
272
+ # read data
273
+ nch = h5_file.attrs['nx']
274
+ ch1 = kwargs.pop('ch1', 0)
275
+ ch2 = kwargs.pop('ch2', nch)
276
+ array_shape = h5_file['data_product/data'].shape
277
+ if array_shape[0] == nch:
278
+ if headonly:
279
+ data = np.zeros_like(h5_file['data_product/data'])
280
+ else:
281
+ data = h5_file['data_product/data'][ch1:ch2:dch, :]
282
+ else:
283
+ if headonly:
284
+ data = np.zeros_like(h5_file['data_product/data']).T
285
+ else:
286
+ data = h5_file['data_product/data'][:, ch1:ch2:dch].T
287
+
288
+ # read metadata
289
+ fs = 1 / h5_file.attrs['dt_computer']
290
+ dx = h5_file.attrs['dx']
291
+ gauge_length = h5_file.attrs['gauge_length']
292
+ if h5_file.attrs['saving_start_gps_time'] > 0:
293
+ start_time = DASDateTime.fromtimestamp(
294
+ h5_file.attrs['file_start_gps_time'])
295
+ else:
296
+ start_time = DASDateTime.fromtimestamp(
297
+ h5_file.attrs['file_start_computer_time'])
298
+ data_type = h5_file.attrs['data_product']
299
+
300
+ metadata = {'dx': dx * dch, 'fs': fs, 'start_channel': ch1,
301
+ 'start_distance': ch1 * dx,
302
+ 'start_time': start_time.astimezone(utc),
303
+ 'gauge_length': gauge_length, 'data_type': data_type}
304
+ else: # Febus
305
+ acquisition = list(h5_file[f'{group}/Source1/Zone1'].keys())[0]
306
+ # read data
307
+ start_channel = int(h5_file[f'{group}/Source1/Zone1'].
308
+ attrs['Extent'][0])
309
+ dataset = h5_file[f'{group}/Source1/Zone1/{acquisition}']
310
+ nch = dataset.shape[-1]
311
+ ch1 = kwargs.pop('ch1', start_channel)
312
+ ch2 = kwargs.pop('ch2', start_channel + nch)
313
+ if headonly:
314
+ data = np.zeros_like(dataset).T.reshape((nch, -1))
315
+ else:
316
+ if len(dataset.shape) == 3: # Febus A1-R
317
+ data = dataset[:, :, ch1 - start_channel:ch2 - start_channel
318
+ :dch].T.reshape(((ch2 - ch1) // dch, -1))
319
+ elif len(dataset.shape) == 2: # Febus A1
320
+ data = dataset[:, ch1 - start_channel:ch2 - start_channel:
321
+ dch].T
322
+ # read metadata
323
+ attrs = h5_file[f'{group}/Source1/Zone1'].attrs
324
+ dx = attrs['Spacing'][0]
325
+ try:
326
+ fs = float(attrs['FreqRes'])
327
+ except KeyError:
328
+ try:
329
+ fs = (attrs['PulseRateFreq'][0] /
330
+ attrs['SamplingRes'][0]) / 1000
331
+ except KeyError:
332
+ fs = attrs['SamplingRate'][0]
333
+ start_distance = attrs['Origin'][0]
334
+ time = h5_file[f'{group}/Source1/time']
335
+ if len(time.shape) == 2: # Febus A1-R
336
+ start_time = DASDateTime.fromtimestamp(time[0, 0]).\
337
+ astimezone(utc)
338
+ elif len(time.shape) == 1: # Febus A1
339
+ start_time = DASDateTime.fromtimestamp(time[0]).astimezone(utc)
340
+ gauge_length = attrs['GaugeLength'][0]
341
+ metadata = {'dx': dx * dch, 'fs': fs, 'start_channel': ch1,
342
+ 'start_distance': start_distance +
343
+ (ch1 - start_channel) * dx,
344
+ 'start_time': start_time, 'gauge_length': gauge_length}
345
+
346
+ metadata['headers'] = _read_h5_headers(h5_file)
347
+
348
+ return data, metadata
349
+
350
+
351
+ def _read_tdms(fname, headonly=False, **kwargs):
352
+ # https://nptdms.readthedocs.io/en/stable/quickstart.html
353
+ with TdmsFile.read(fname) as tdms_file:
354
+ group_name = [group.name for group in tdms_file.groups()]
355
+ if 'Measurement' in group_name:
356
+ key = 'Measurement'
357
+ elif 'DAS' in group_name:
358
+ key = 'DAS'
359
+ else:
360
+ key = group_name[0]
361
+
362
+ headers = {**tdms_file.properties, **tdms_file[key].properties}
363
+ nch = len(tdms_file[key])
364
+ dch = kwargs.pop('dch', 1)
365
+ # read data
366
+ if nch > 1:
367
+ start_channel = min(int(channel.name) for channel in
368
+ tdms_file[key].channels())
369
+ ch1 = max(kwargs.pop('ch1', start_channel), start_channel)
370
+ ch2 = min(kwargs.pop('ch2', start_channel + nch),
371
+ start_channel + nch)
372
+ if headonly:
373
+ nt = len(tdms_file[key][str(start_channel)])
374
+ data = np.zeros((nch, nt))
375
+ else:
376
+ data = np.asarray([tdms_file[key][str(ch)]
377
+ for ch in range(ch1, ch2, dch)])
378
+ elif nch == 1:
379
+ try:
380
+ start_channel = int(headers['Initial Channel'])
381
+ except KeyError:
382
+ start_channel = 0
383
+
384
+ ch1 = max(kwargs.pop('ch1', start_channel), start_channel)
385
+ nch = int(headers['Total Channels'])
386
+ ch2 = min(kwargs.pop('ch2', start_channel + nch),
387
+ start_channel + nch)
388
+ if headonly:
389
+ data = np.zeros(len(tdms_file[key].channels()[0])).\
390
+ reshape((nch, -1))
391
+ else:
392
+ data = np.asarray(tdms_file[key].channels()[0]).\
393
+ reshape((-1, nch)).T
394
+ data = data[ch1 - start_channel:ch2 - start_channel:dch]
395
+
396
+ # read metadata
397
+ try:
398
+ dx = headers['SpatialResolution[m]']
399
+ except KeyError:
400
+ try:
401
+ dx = headers['Spatial Resolution']
402
+ except KeyError:
403
+ dx = None
404
+
405
+ try:
406
+ fs = headers['SamplingFrequency[Hz]']
407
+ except KeyError:
408
+ try:
409
+ fs = 1 / headers['Time Base']
410
+ except KeyError:
411
+ fs = None
412
+
413
+ try:
414
+ start_distance = headers['Start Distance (m)'] + \
415
+ dx * (ch1 - start_channel)
416
+ except KeyError:
417
+ start_distance = dx * ch1
418
+
419
+ try:
420
+ start_time = DASDateTime.strptime(headers['ISO8601 Timestamp'],
421
+ '%Y-%m-%dT%H:%M:%S.%f%z')
422
+ except ValueError:
423
+ start_time = DASDateTime.strptime(headers['ISO8601 Timestamp'],
424
+ '%Y-%m-%dT%H:%M:%S.%f')
425
+ except KeyError:
426
+ start_time = 0
427
+ for key in ['GPSTimeStamp', 'CPUTimeStamp', 'Trigger Time']:
428
+ if key in headers.keys():
429
+ if headers[key]:
430
+ start_time = DASDateTime.from_datetime(headers[key].
431
+ item())
432
+ break
433
+
434
+ if dx is not None:
435
+ dx *= dch
436
+ metadata = {'dx': dx, 'fs': fs, 'start_channel': ch1,
437
+ 'start_distance': start_distance, 'start_time': start_time,
438
+ 'headers': headers}
439
+
440
+ if 'GaugeLength' in headers.keys():
441
+ metadata['gauge_length'] = headers['GaugeLength']
442
+
443
+ return data, metadata
444
+
445
+
446
+ def _read_segy(fname, headonly=False, **kwargs):
447
+ # https://github.com/equinor/segyio-notebooks/blob/master/notebooks/basic/02_segy_quicklook.ipynb
448
+ with segyio.open(fname, ignore_geometry=True) as segy_file:
449
+ nch = segy_file.tracecount
450
+ ch1 = kwargs.pop('ch1', 0)
451
+ ch2 = kwargs.pop('ch2', nch)
452
+ dch = kwargs.pop('dch', 1)
453
+
454
+ # read data
455
+ if headonly:
456
+ data = np.zeros_like(segy_file.trace.raw[:])
457
+ else:
458
+ data = segy_file.trace.raw[ch1:ch2:dch]
459
+
460
+ # read metadata:
461
+ fs = 1 / (segyio.tools.dt(segy_file) / 1e6)
462
+ metadata = {'dx': None, 'fs': fs, 'start_channel': ch1}
463
+ warnings.warn('This data format doesn\'t include channel interval.'
464
+ 'Please set manually')
465
+
466
+ return data, metadata
467
+
468
+
469
+ def _read_npy(fname, headonly=False, **kwargs):
470
+ data = np.load(fname)
471
+ if headonly:
472
+ return np.zeros_like(data), {'dx': None, 'fs': None}
473
+ else:
474
+ ch1 = kwargs.pop('ch1', 0)
475
+ ch2 = kwargs.pop('ch2', len(data))
476
+ dch = kwargs.pop('dch', 1)
477
+ warnings.warn('This data format doesn\'t include channel interval and '
478
+ 'sampling rate. Please set manually')
479
+ return data[ch1:ch2:dch], {'dx': None, 'fs': None}
480
+
481
+
482
+ def read_json(fname, output_type='dict'):
483
+ """
484
+ Read .json metadata file. See {Lai et al. , 2024, Seismol. Res. Lett.}
485
+
486
+ :param fname: str or pathlib.PosixPath. Path of json file.
487
+ :param output_type: str. 'dict' means return a dictionary, and 'Section'
488
+ means return a empty daspy.Section instance with metadata.
489
+ :return: A dictionary of metadata or an instance of daspy.Section without
490
+ data.
491
+ """
492
+ with open(fname, 'r') as fcc_file:
493
+ headers = json.load(fcc_file)
494
+ if output_type.lower() == 'dict':
495
+ return headers
496
+ elif output_type.lower() in ['section', 'sec']:
497
+ if len(headers['Overview']['Interrogator']) > 1:
498
+ case_type = 'Multiple interrogators, single cable'
499
+ sec_num = len(headers['Overview']['Interrogator'])
500
+ sec = []
501
+ for interrogator in headers['Overview']['Interrogator']:
502
+ nch = interrogator['Acquisition'][0]['Attributes']['number_of_channels']
503
+ data = np.zeros((nch, 0))
504
+ dx = interrogator['Acquisition'][0]['Attributes']['spatial_sampling_interval']
505
+ fs = interrogator['Acquisition'][0]['Attributes']['acquisition_sample_rate']
506
+ gauge_length = interrogator['Acquisition'][0]['Attributes']['gauge_length']
507
+ sec.append(Section(data, dx, fs, gauge_length=gauge_length,
508
+ headers=headers))
509
+ elif len(headers['Overview']['Interrogator'][0]['Acquisition']) > 1:
510
+ case_type = 'Active survey'
511
+ sec_num = len(
512
+ headers['Overview']['Interrogator'][0]['Acquisition'])
513
+ sec = []
514
+ for acquisition in headers['Overview']['Interrogator'][0]['Acquisition']:
515
+ nch = acquisition['Attributes']['number_of_channels']
516
+ data = np.zeros((nch, 0))
517
+ dx = acquisition['Attributes']['spatial_sampling_interval']
518
+ fs = acquisition['Attributes']['acquisition_sample_rate']
519
+ gauge_length = acquisition['Attributes']['gauge_length']
520
+ sec.append(Section(data, dx, fs, gauge_length=gauge_length,
521
+ headers=headers))
522
+ else:
523
+ sec_num = 1
524
+ if len(headers['Overview']['Cable']) > 1:
525
+ case_type = 'Single interrogators, multiple cable'
526
+ else:
527
+ env = headers['Overview']['Cable'][0]['Attributes']['cable_environment']
528
+ if env == 'trench':
529
+ case_type = 'Direct buried'
530
+ elif env == 'conduit':
531
+ case_type = 'Dark fiber'
532
+ elif env in ['wireline', 'outside borehole casing']:
533
+ case_type = 'Borehole cable'
534
+ nch = headers['Overview']['Interrogator'][0]['Acquisition'][0]['Attributes']['number_of_channels']
535
+ dx = headers['Overview']['Interrogator'][0]['Acquisition'][0]['Attributes']['spatial_sampling_interval']
536
+ fs = headers['Overview']['Interrogator'][0]['Acquisition'][0]['Attributes']['acquisition_sample_rate']
537
+ gauge_length = headers['Overview']['Interrogator'][0]['Acquisition'][0]['Attributes']['gauge_length']
538
+ data = np.zeros((nch, 0))
539
+ sec = Section(data, dx, fs, gauge_length=gauge_length,
540
+ headers=headers)
541
+
542
+ print(f'For case of {case_type}, create {sec_num} empty daspy.Section '
543
+ 'instance(s)')
544
+ return sec